[svn r37264] create the new development trunk
--HG-- branch : trunk
This commit is contained in:
1
py/test/rsession/__init__.py
Normal file
1
py/test/rsession/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
#
|
||||
106
py/test/rsession/box.py
Normal file
106
py/test/rsession/box.py
Normal file
@@ -0,0 +1,106 @@
|
||||
|
||||
""" boxing - wrapping process with another process so we can run
|
||||
a process inside and see if it crashes
|
||||
"""
|
||||
|
||||
import py
|
||||
import os
|
||||
import sys
|
||||
import marshal
|
||||
|
||||
PYTESTSTDOUT = "pyteststdout"
|
||||
PYTESTSTDERR = "pyteststderr"
|
||||
PYTESTRETVAL = "pytestretval"
|
||||
|
||||
import tempfile
|
||||
from StringIO import StringIO
|
||||
|
||||
class FileBox(object):
|
||||
count = 0
|
||||
|
||||
def __init__(self, fun, args=None, kwargs=None):
|
||||
if args is None:
|
||||
args = []
|
||||
if kwargs is None:
|
||||
kwargs = {}
|
||||
self.fun = fun
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
|
||||
def run(self, continuation=False):
|
||||
tempdir = py.test.ensuretemp("box%d" % self.count)
|
||||
self.count += 1
|
||||
self.tempdir = tempdir
|
||||
self.PYTESTRETVAL = tempdir.join('retval')
|
||||
self.PYTESTSTDOUT = tempdir.join('stdout')
|
||||
self.PYTESTSTDERR = tempdir.join('stderr')
|
||||
|
||||
from py.__.test.rsession.rsession import remote_options
|
||||
nice_level = remote_options.nice_level
|
||||
pid = os.fork()
|
||||
if pid:
|
||||
if not continuation:
|
||||
self.parent(pid)
|
||||
else:
|
||||
return self.parent, pid
|
||||
else:
|
||||
try:
|
||||
outcome = self.children(nice_level)
|
||||
except:
|
||||
excinfo = py.code.ExceptionInfo()
|
||||
print "Internal box error"
|
||||
for i in excinfo.traceback:
|
||||
print str(i)[2:-1]
|
||||
print excinfo
|
||||
os._exit(1)
|
||||
os.close(1)
|
||||
os.close(2)
|
||||
os._exit(0)
|
||||
return pid
|
||||
|
||||
def children(self, nice_level):
|
||||
# right now we need to call a function, but first we need to
|
||||
# map all IO that might happen
|
||||
# make sure sys.stdout points to file descriptor one
|
||||
sys.stdout = stdout = self.PYTESTSTDOUT.open('w')
|
||||
sys.stdout.flush()
|
||||
fdstdout = stdout.fileno()
|
||||
if fdstdout != 1:
|
||||
os.dup2(fdstdout, 1)
|
||||
sys.stderr = stderr = self.PYTESTSTDERR.open('w')
|
||||
fdstderr = stderr.fileno()
|
||||
if fdstderr != 2:
|
||||
os.dup2(fdstderr, 2)
|
||||
retvalf = self.PYTESTRETVAL.open("w")
|
||||
try:
|
||||
if nice_level:
|
||||
os.nice(nice_level)
|
||||
retval = self.fun(*self.args, **self.kwargs)
|
||||
retvalf.write(marshal.dumps(retval))
|
||||
finally:
|
||||
stdout.close()
|
||||
stderr.close()
|
||||
retvalf.close()
|
||||
os._exit(0)
|
||||
|
||||
def parent(self, pid):
|
||||
pid, exitstat = os.waitpid(pid, 0)
|
||||
self.signal = exitstat & 0x7f
|
||||
self.exitstat = exitstat & 0xff00
|
||||
|
||||
|
||||
if not exitstat:
|
||||
retval = self.PYTESTRETVAL.open()
|
||||
try:
|
||||
retval_data = retval.read()
|
||||
finally:
|
||||
retval.close()
|
||||
self.retval = marshal.loads(retval_data)
|
||||
else:
|
||||
self.retval = None
|
||||
|
||||
self.stdoutrepr = self.PYTESTSTDOUT.read()
|
||||
self.stderrrepr = self.PYTESTSTDERR.read()
|
||||
return self.stdoutrepr, self.stderrrepr
|
||||
|
||||
Box = FileBox
|
||||
10
py/test/rsession/conftest.py
Normal file
10
py/test/rsession/conftest.py
Normal file
@@ -0,0 +1,10 @@
|
||||
import py
|
||||
Option = py.test.config.Option
|
||||
|
||||
option = py.test.config.addoptions("boxing test options",
|
||||
Option('', '--skip-kill-test', action='store_true', default=False,
|
||||
dest='skip_kill_test',
|
||||
help='skip a certain test that checks for os.kill results, this '
|
||||
'should be used when kill() is not allowed for the current '
|
||||
'user'),
|
||||
)
|
||||
95
py/test/rsession/executor.py
Normal file
95
py/test/rsession/executor.py
Normal file
@@ -0,0 +1,95 @@
|
||||
""" Remote executor
|
||||
"""
|
||||
|
||||
import py, os
|
||||
|
||||
from py.__.test.rsession.outcome import Outcome, ReprOutcome
|
||||
from py.__.test.rsession.box import Box
|
||||
from py.__.test.rsession import report
|
||||
|
||||
class RunExecutor(object):
|
||||
""" Same as in executor, but just running run
|
||||
"""
|
||||
wraps = False
|
||||
|
||||
def __init__(self, item, usepdb=False, reporter=None):
|
||||
self.item = item
|
||||
self.usepdb = usepdb
|
||||
self.reporter = reporter
|
||||
|
||||
def execute(self):
|
||||
try:
|
||||
self.item.run()
|
||||
outcome = Outcome()
|
||||
except py.test.Item.Skipped, e:
|
||||
outcome = Outcome(skipped=str(e))
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except:
|
||||
excinfo = py.code.ExceptionInfo()
|
||||
if isinstance(self.item, py.test.Function):
|
||||
fun = self.item.obj # hope this is stable
|
||||
code = py.code.Code(fun)
|
||||
excinfo.traceback = excinfo.traceback.cut(
|
||||
path=code.path, firstlineno=code.firstlineno)
|
||||
outcome = Outcome(excinfo=excinfo, setupfailure=False)
|
||||
if self.usepdb:
|
||||
if self.reporter is not None:
|
||||
self.reporter(report.ImmediateFailure(self.item,
|
||||
ReprOutcome(outcome.make_repr())))
|
||||
import pdb
|
||||
pdb.post_mortem(excinfo._excinfo[2])
|
||||
# XXX hmm, we probably will not like to continue from that
|
||||
# point
|
||||
raise SystemExit()
|
||||
outcome.stdout = ""
|
||||
outcome.stderr = ""
|
||||
return outcome
|
||||
|
||||
class BoxExecutor(RunExecutor):
|
||||
""" Same as run executor, but boxes test instead
|
||||
"""
|
||||
wraps = True
|
||||
|
||||
def execute(self):
|
||||
def fun():
|
||||
outcome = RunExecutor.execute(self)
|
||||
return outcome.make_repr()
|
||||
b = Box(fun)
|
||||
pid = b.run()
|
||||
assert pid
|
||||
if b.retval is not None:
|
||||
passed, setupfailure, excinfo, skipped, critical, _, _, _\
|
||||
= b.retval
|
||||
return (passed, setupfailure, excinfo, skipped, critical, 0,
|
||||
b.stdoutrepr, b.stderrrepr)
|
||||
else:
|
||||
return (False, False, None, False, False, b.signal,
|
||||
b.stdoutrepr, b.stderrrepr)
|
||||
|
||||
class AsyncExecutor(RunExecutor):
|
||||
""" same as box executor, but instead it returns function to continue
|
||||
computations (more async mode)
|
||||
"""
|
||||
wraps = True
|
||||
|
||||
def execute(self):
|
||||
def fun():
|
||||
outcome = RunExecutor.execute(self)
|
||||
return outcome.make_repr()
|
||||
|
||||
b = Box(fun)
|
||||
parent, pid = b.run(continuation=True)
|
||||
|
||||
def cont():
|
||||
parent(pid)
|
||||
if b.retval is not None:
|
||||
passed, setupfailure, excinfo, skipped,\
|
||||
critical, _, _, _ = b.retval
|
||||
return (passed, setupfailure, excinfo, skipped, critical, 0,
|
||||
b.stdoutrepr, b.stderrrepr)
|
||||
else:
|
||||
return (False, False, None, False, False,
|
||||
b.signal, b.stdoutrepr, b.stderrrepr)
|
||||
|
||||
return cont, pid
|
||||
163
py/test/rsession/hostmanage.py
Normal file
163
py/test/rsession/hostmanage.py
Normal file
@@ -0,0 +1,163 @@
|
||||
import sys, os
|
||||
import py
|
||||
import time
|
||||
import thread, threading
|
||||
from py.__.test.rsession.master import \
|
||||
setup_slave, MasterNode
|
||||
from py.__.test.rsession import report
|
||||
from py.__.test.rsession.rsync import RSync
|
||||
|
||||
class HostInfo(object):
|
||||
""" Class trying to store all necessary attributes
|
||||
for host
|
||||
"""
|
||||
host_ids = {}
|
||||
|
||||
def __init__(self, hostname, relpath=None):
|
||||
self.hostid = self._getuniqueid(hostname)
|
||||
self.hostname = hostname
|
||||
self.relpath = relpath
|
||||
|
||||
def _getuniqueid(cls, hostname):
|
||||
if not hostname in cls.host_ids:
|
||||
cls.host_ids[hostname] = 0
|
||||
return hostname
|
||||
retval = hostname + '_' + str(cls.host_ids[hostname])
|
||||
cls.host_ids[hostname] += 1
|
||||
return retval
|
||||
_getuniqueid = classmethod(_getuniqueid)
|
||||
|
||||
def __str__(self):
|
||||
return "<HostInfo %s>" % (self.hostname,)
|
||||
|
||||
def __hash__(self):
|
||||
return hash(self.hostid)
|
||||
|
||||
def __eq__(self, other):
|
||||
return self.hostid == other.hostid
|
||||
|
||||
def __ne__(self, other):
|
||||
return not self == other
|
||||
|
||||
class HostRSync(RSync):
|
||||
""" An rsync wrapper which filters out *~, .svn/ and *.pyc
|
||||
"""
|
||||
def __init__(self, rsync_roots):
|
||||
RSync.__init__(self, delete=True)
|
||||
self.rsync_roots = rsync_roots
|
||||
|
||||
def filter(self, path):
|
||||
if path.endswith('.pyc') or path.endswith('~'):
|
||||
return False
|
||||
dir, base = os.path.split(path)
|
||||
if base == '.svn':
|
||||
return False
|
||||
if self.rsync_roots is None or dir != self.sourcedir:
|
||||
return True
|
||||
else:
|
||||
return base in self.rsync_roots
|
||||
|
||||
class DummyGateway(object):
|
||||
pass
|
||||
|
||||
def prepare_gateway(sshosts, optimise_localhost,
|
||||
remote_python, pkgdir, real_create=True):
|
||||
hosts = []
|
||||
for host in sshosts:
|
||||
if host.hostname != 'localhost' or not optimise_localhost:
|
||||
if real_create:
|
||||
# for tests we want to use somtehing different
|
||||
if host.hostname == 'localhost' and optimise_localhost is False:
|
||||
from py.__.execnet.register import PopenCmdGateway
|
||||
gw = PopenCmdGateway("cd ~; python -u -c 'exec input()'")
|
||||
if not host.relpath.startswith("/"):
|
||||
host.relpath = os.environ['HOME'] + '/' + host.relpath
|
||||
else:
|
||||
if remote_python is None:
|
||||
gw = py.execnet.SshGateway(host.hostname)
|
||||
else:
|
||||
gw = py.execnet.SshGateway(host.hostname,
|
||||
remotepython=remote_python)
|
||||
else:
|
||||
gw = DummyGateway()
|
||||
else:
|
||||
if remote_python is None:
|
||||
gw = py.execnet.PopenGateway()
|
||||
else:
|
||||
gw = py.execnet.PopenGateway(remotepython=remote_python)
|
||||
host.relpath = str(pkgdir.dirpath())
|
||||
host.gw = gw
|
||||
gw.host = host
|
||||
return sshosts
|
||||
|
||||
def init_hosts(reporter, sshhosts, pkgdir, rsync_roots=None,
|
||||
remote_python=None, \
|
||||
remote_options={}, optimise_localhost=True,\
|
||||
do_sync=True, done_dict=None):
|
||||
if done_dict is None:
|
||||
done_dict = {}
|
||||
assert pkgdir.join("__init__.py").check(), (
|
||||
"%s probably wrong" %(pkgdir,))
|
||||
|
||||
exc_info = [None]
|
||||
hosts = prepare_gateway(sshhosts, optimise_localhost,
|
||||
remote_python, pkgdir, real_create=do_sync)
|
||||
|
||||
# rsyncing
|
||||
rsynced = {}
|
||||
|
||||
if do_sync:
|
||||
rsync = HostRSync(rsync_roots)
|
||||
for host in hosts:
|
||||
#for num, host, gw, remoterootpath in hosts:
|
||||
remoterootpath = host.relpath
|
||||
if (host.hostname, remoterootpath) in rsynced or\
|
||||
(host.hostname == 'localhost' and optimise_localhost):
|
||||
reporter(report.HostReady(host))
|
||||
continue
|
||||
rsynced[(host.hostname, host.relpath)] = True
|
||||
def done(host=host):
|
||||
reporter(report.HostReady(host))
|
||||
reporter(report.HostRSyncing(host))
|
||||
if do_sync:
|
||||
rsync.add_target(host.gw, remoterootpath, done)
|
||||
if not do_sync:
|
||||
return # for testing only
|
||||
rsync.send(pkgdir.dirpath())
|
||||
|
||||
# hosts ready
|
||||
return setup_nodes(hosts, pkgdir, remote_options, reporter, done_dict)
|
||||
|
||||
def setup_nodes(hosts, pkgdir, remote_options, reporter, done_dict):
|
||||
nodes = []
|
||||
for host in hosts:
|
||||
ch = setup_slave(host.gw, os.path.join(host.relpath,\
|
||||
pkgdir.basename), remote_options)
|
||||
nodes.append(MasterNode(ch, reporter, done_dict))
|
||||
|
||||
return nodes
|
||||
|
||||
def teardown_hosts(reporter, channels, nodes, waiter=lambda : time.sleep(.1),
|
||||
exitfirst=False):
|
||||
for channel in channels:
|
||||
channel.send(None)
|
||||
|
||||
from py.__.test.rsession.rsession import session_options
|
||||
|
||||
clean = exitfirst
|
||||
while not clean:
|
||||
clean = True
|
||||
for node in nodes:
|
||||
if node.pending:
|
||||
clean = False
|
||||
waiter()
|
||||
|
||||
|
||||
for channel in channels:
|
||||
try:
|
||||
report.wrapcall(reporter, channel.waitclose, int(session_options.waittime))
|
||||
except KeyboardInterrupt, SystemExit:
|
||||
raise
|
||||
except:
|
||||
pass
|
||||
channel.gateway.exit()
|
||||
76
py/test/rsession/local.py
Normal file
76
py/test/rsession/local.py
Normal file
@@ -0,0 +1,76 @@
|
||||
|
||||
""" local-only operations
|
||||
"""
|
||||
|
||||
from py.__.test.rsession.executor import BoxExecutor, RunExecutor
|
||||
from py.__.test.rsession import report
|
||||
from py.__.test.rsession.outcome import ReprOutcome
|
||||
|
||||
# XXX copied from session.py
|
||||
def startcapture(session):
|
||||
if not session.config.option.nocapture and not session.config.option.usepdb:
|
||||
# XXX refactor integrate capturing
|
||||
from py.__.misc.simplecapture import SimpleOutErrCapture
|
||||
session._capture = SimpleOutErrCapture()
|
||||
|
||||
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)
|
||||
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)
|
||||
outcome = r.execute()
|
||||
outcome = ReprOutcome(outcome.make_repr())
|
||||
outcome.stdout, outcome.stderr = finishcapture(session)
|
||||
return outcome
|
||||
|
||||
RunnerPolicy = {
|
||||
'plain_runner':plain_runner,
|
||||
'box_runner':box_runner
|
||||
}
|
||||
|
||||
def benchmark_runner(item, session, reporter):
|
||||
raise NotImplementedError()
|
||||
|
||||
def apigen_runner(item, session, reporter):
|
||||
r = RunExecutor(item, reporter=reporter)
|
||||
session.tracer.start_tracing()
|
||||
retval = plain_runner(item, session, reporter)
|
||||
session.tracer.end_tracing()
|
||||
return retval
|
||||
|
||||
def exec_runner(item, session, reporter):
|
||||
raise NotImplementedError()
|
||||
|
||||
# runner interface is here to perform several different types of run
|
||||
#+1. box_runner - for running normal boxed tests
|
||||
#+2. plain_runner - for performing running without boxing (necessary for pdb)
|
||||
# XXX: really?
|
||||
#-3. exec_runner - for running under different interpreter
|
||||
#-4. benchmark_runner - for running with benchmarking
|
||||
#-5. apigen_runner - for running under apigen to generate api out of it.
|
||||
def local_loop(session, reporter, itemgenerator, shouldstop, config, runner=None):
|
||||
assert runner is not None
|
||||
#if runner is None:
|
||||
# if session.config.option.apigen:
|
||||
# runner = apigen_runner
|
||||
# else:
|
||||
# runner = box_runner
|
||||
while 1:
|
||||
try:
|
||||
item = itemgenerator.next()
|
||||
if shouldstop():
|
||||
return
|
||||
outcome = runner(item, session, reporter)
|
||||
reporter(report.ReceivedItemOutcome(None, item, outcome))
|
||||
except StopIteration:
|
||||
break
|
||||
87
py/test/rsession/master.py
Normal file
87
py/test/rsession/master.py
Normal file
@@ -0,0 +1,87 @@
|
||||
"""
|
||||
Node code for Master.
|
||||
"""
|
||||
import py
|
||||
from py.__.test.rsession.outcome import ReprOutcome
|
||||
from py.__.test.rsession import report
|
||||
|
||||
class MasterNode(object):
|
||||
def __init__(self, channel, reporter, done_dict):
|
||||
self.channel = channel
|
||||
self.reporter = reporter
|
||||
|
||||
def callback(outcome):
|
||||
item = self.pending.pop()
|
||||
if not item in done_dict:
|
||||
self.receive_result(outcome, item)
|
||||
done_dict[item] = True
|
||||
channel.setcallback(callback)
|
||||
self.pending = []
|
||||
|
||||
def receive_result(self, outcomestring, item):
|
||||
repr_outcome = ReprOutcome(outcomestring)
|
||||
# send finish report
|
||||
self.reporter(report.ReceivedItemOutcome(
|
||||
self.channel, item, repr_outcome))
|
||||
|
||||
def send(self, item):
|
||||
if item is StopIteration:
|
||||
self.channel.send(42)
|
||||
else:
|
||||
self.pending.insert(0, item)
|
||||
itemspec = item.listnames()[1:]
|
||||
self.channel.send(itemspec)
|
||||
# send start report
|
||||
self.reporter(report.SendItem(self.channel, item))
|
||||
|
||||
def itemgen(colitems, reporter, keyword, reporterror):
|
||||
for x in colitems:
|
||||
for y in x.tryiter(reporterror = lambda x: reporterror(reporter, x), keyword = keyword):
|
||||
yield y
|
||||
|
||||
def randomgen(items, done_dict):
|
||||
""" Generator, which randomly gets all the tests from items as long
|
||||
as they're not in done_dict
|
||||
"""
|
||||
import random
|
||||
while items:
|
||||
values = items.keys()
|
||||
item = values[int(random.random()*len(values))]
|
||||
if item in done_dict:
|
||||
del items[item]
|
||||
else:
|
||||
yield item
|
||||
|
||||
def dispatch_loop(masternodes, itemgenerator, shouldstop,
|
||||
waiter = lambda: py.std.time.sleep(0.1),
|
||||
max_tasks_per_node=None):
|
||||
if not max_tasks_per_node:
|
||||
from py.__.test.rsession.rsession import session_options
|
||||
|
||||
max_tasks_per_node = session_options.max_tasks_per_node
|
||||
all_tests = {}
|
||||
while 1:
|
||||
try:
|
||||
for node in masternodes:
|
||||
if len(node.pending) < max_tasks_per_node:
|
||||
item = itemgenerator.next()
|
||||
all_tests[item] = True
|
||||
if shouldstop():
|
||||
for _node in masternodes:
|
||||
_node.send(StopIteration) # magic connector
|
||||
return None
|
||||
node.send(item)
|
||||
except StopIteration:
|
||||
break
|
||||
waiter()
|
||||
return all_tests
|
||||
|
||||
def setup_slave(gateway, pkgpath, options):
|
||||
from py.__.test.rsession import slave
|
||||
import os
|
||||
ch = gateway.remote_exec(str(py.code.Source(slave.setup, "setup()")))
|
||||
#if hasattr(gateway, 'sshaddress'):
|
||||
# assert not os.path.isabs(pkgpath)
|
||||
ch.send(str(pkgpath))
|
||||
ch.send(options)
|
||||
return ch
|
||||
80
py/test/rsession/outcome.py
Normal file
80
py/test/rsession/outcome.py
Normal file
@@ -0,0 +1,80 @@
|
||||
|
||||
""" Classes for representing outcomes on master and slavenode sides
|
||||
"""
|
||||
|
||||
# WARNING! is_critical is debugging flag which means something
|
||||
# wrong went on a different level. Usually that means
|
||||
# internal bug.
|
||||
|
||||
import sys
|
||||
import py
|
||||
|
||||
class Outcome(object):
|
||||
def __init__(self, setupfailure=False, excinfo=None, skipped=None,
|
||||
is_critical=False):
|
||||
self.passed = not excinfo and not skipped
|
||||
self.skipped = skipped
|
||||
self.setupfailure = setupfailure
|
||||
self.excinfo = excinfo
|
||||
self.is_critical = is_critical
|
||||
self.signal = 0
|
||||
assert bool(self.passed) + bool(excinfo) + bool(skipped) == 1
|
||||
|
||||
def make_excinfo_repr(self):
|
||||
if self.excinfo is None:
|
||||
return None
|
||||
excinfo = self.excinfo
|
||||
tb_info = [self.traceback_entry_repr(x) for x in excinfo.traceback]
|
||||
return (excinfo.type.__name__, str(excinfo.value), tb_info)
|
||||
|
||||
def traceback_entry_repr(self, tb_entry):
|
||||
lineno = tb_entry.lineno
|
||||
relline = lineno - tb_entry.frame.code.firstlineno
|
||||
path = str(tb_entry.path)
|
||||
try:
|
||||
from py.__.test.rsession.rsession import remote_options
|
||||
if remote_options.tbstyle == 'long':
|
||||
source = str(tb_entry.getsource())
|
||||
else:
|
||||
source = str(tb_entry.getsource()).split("\n")[relline]
|
||||
except:
|
||||
source = "<could not get source>"
|
||||
return (relline, lineno, source, path)
|
||||
|
||||
def make_repr(self):
|
||||
return (self.passed, self.setupfailure,
|
||||
self.make_excinfo_repr(),
|
||||
self.skipped, self.is_critical, 0, "", "")
|
||||
|
||||
class TracebackEntryRepr(object):
|
||||
def __init__(self, tbentry):
|
||||
relline, lineno, self.source, self.path = tbentry
|
||||
self.relline = int(relline)
|
||||
self.lineno = int(lineno)
|
||||
|
||||
def __repr__(self):
|
||||
return "line %s in %s\n %s" %(self.lineno, self.path, self.source[100:])
|
||||
|
||||
class ExcInfoRepr(object):
|
||||
def __init__(self, excinfo):
|
||||
self.typename, self.value, tb = excinfo
|
||||
self.traceback = [TracebackEntryRepr(x) for x in tb]
|
||||
|
||||
def __repr__(self):
|
||||
l = ["%s=%s" %(x, getattr(self, x))
|
||||
for x in "typename value traceback".split()]
|
||||
return "<ExcInfoRepr %s>" %(" ".join(l),)
|
||||
|
||||
class ReprOutcome(object):
|
||||
def __init__(self, repr_tuple):
|
||||
(self.passed, self.setupfailure, excinfo, self.skipped,
|
||||
self.is_critical, self.signal, self.stdout, self.stderr) = repr_tuple
|
||||
if excinfo is None:
|
||||
self.excinfo = None
|
||||
else:
|
||||
self.excinfo = ExcInfoRepr(excinfo)
|
||||
|
||||
def __repr__(self):
|
||||
l = ["%s=%s" %(x, getattr(self, x))
|
||||
for x in "signal passed skipped setupfailure excinfo stdout stderr".split()]
|
||||
return "<ReprOutcome %s>" %(" ".join(l),)
|
||||
126
py/test/rsession/report.py
Normal file
126
py/test/rsession/report.py
Normal file
@@ -0,0 +1,126 @@
|
||||
""" Reporter classes for showing asynchronous and synchronous status events
|
||||
"""
|
||||
|
||||
import py
|
||||
import time
|
||||
|
||||
def basic_report(msg_type, message):
|
||||
print msg_type, message
|
||||
|
||||
#def report(msg_type, message):
|
||||
# pass
|
||||
|
||||
##def report_error(excinfo):
|
||||
## if isinstance(excinfo, py.test.Item.Skipped):
|
||||
## # we need to dispatch this info
|
||||
## report(Skipped(excinfo))
|
||||
## else:
|
||||
## report("itererror", excinfo)
|
||||
|
||||
def wrapcall(reporter, func, *args, **kwargs):
|
||||
reporter(CallStart(func, args, kwargs))
|
||||
try:
|
||||
retval = func(*args, **kwargs)
|
||||
except:
|
||||
reporter(CallException(func, args, kwargs))
|
||||
raise
|
||||
else:
|
||||
reporter(CallFinish(func, args, kwargs))
|
||||
return retval
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# Reporting Events
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
class ReportEvent(object):
|
||||
def __repr__(self):
|
||||
l = ["%s=%s" %(key, value)
|
||||
for key, value in self.__dict__.items()]
|
||||
return "<%s %s>" %(self.__class__.__name__, " ".join(l),)
|
||||
|
||||
class SendItem(ReportEvent):
|
||||
def __init__(self, channel, item):
|
||||
self.item = item
|
||||
self.channel = channel
|
||||
if channel:
|
||||
self.host = channel.gateway.host
|
||||
|
||||
class ReceivedItemOutcome(ReportEvent):
|
||||
def __init__(self, channel, item, outcome):
|
||||
self.channel = channel
|
||||
if channel:
|
||||
self.host = channel.gateway.host
|
||||
self.item = item
|
||||
self.outcome = outcome
|
||||
|
||||
class CallEvent(ReportEvent):
|
||||
def __init__(self, func, args, kwargs):
|
||||
self.func = func
|
||||
self.args = args
|
||||
self.kwargs = kwargs
|
||||
|
||||
def __repr__(self):
|
||||
call = "%s args=%s, kwargs=%s" %(self.func.__name__,
|
||||
self.args, self.kwargs)
|
||||
return '<%s %s>' %(self.__class__.__name__, call)
|
||||
|
||||
class CallStart(CallEvent):
|
||||
pass
|
||||
|
||||
class CallException(CallEvent):
|
||||
pass
|
||||
|
||||
class CallFinish(CallEvent):
|
||||
pass
|
||||
|
||||
class HostRSyncing(ReportEvent):
|
||||
def __init__(self, host):
|
||||
self.host = host
|
||||
|
||||
class HostReady(ReportEvent):
|
||||
def __init__(self, host):
|
||||
self.host = host
|
||||
|
||||
class TestStarted(ReportEvent):
|
||||
def __init__(self, hosts):
|
||||
self.hosts = hosts
|
||||
self.timestart = time.time()
|
||||
|
||||
class TestFinished(ReportEvent):
|
||||
def __init__(self):
|
||||
self.timeend = time.time()
|
||||
|
||||
class Nodes(ReportEvent):
|
||||
def __init__(self, nodes):
|
||||
self.nodes = nodes
|
||||
|
||||
class SkippedTryiter(ReportEvent):
|
||||
def __init__(self, excinfo, item):
|
||||
self.excinfo = excinfo
|
||||
self.item = item
|
||||
|
||||
class FailedTryiter(ReportEvent):
|
||||
def __init__(self, excinfo, item):
|
||||
self.excinfo = excinfo
|
||||
self.item = item
|
||||
|
||||
class ItemStart(ReportEvent):
|
||||
""" This class shows most of the start stuff, like directory, module, class
|
||||
can be used for containers
|
||||
"""
|
||||
def __init__(self, item):
|
||||
self.item = item
|
||||
|
||||
class RsyncFinished(ReportEvent):
|
||||
def __init__(self):
|
||||
self.time = time.time()
|
||||
|
||||
class ImmediateFailure(ReportEvent):
|
||||
def __init__(self, item, outcome):
|
||||
self.item = item
|
||||
self.outcome = outcome
|
||||
|
||||
class PongReceived(ReportEvent):
|
||||
def __init__(self, hostid, result):
|
||||
self.hostid = hostid
|
||||
self.result = result
|
||||
309
py/test/rsession/reporter.py
Normal file
309
py/test/rsession/reporter.py
Normal file
@@ -0,0 +1,309 @@
|
||||
|
||||
""" reporter - different reporter for different purposes ;-)
|
||||
Still lacks:
|
||||
|
||||
1. Hanging nodes are not good done
|
||||
"""
|
||||
|
||||
import py
|
||||
|
||||
from py.__.test.terminal.out import getout
|
||||
from py.__.test.rsession import report
|
||||
from py.__.test.rsession import outcome
|
||||
|
||||
class AbstractReporter(object):
|
||||
def __init__(self, config, hosts, pkgdir=py.path.local(py.__file__)):
|
||||
self.config = config
|
||||
self.pkgdir = pkgdir
|
||||
self.hosts = hosts
|
||||
self.failed_tests_outcome = []
|
||||
self.skipped_tests_outcome = []
|
||||
self.out = getout(py.std.sys.stdout)
|
||||
self.failed = dict([(host, 0) for host in hosts])
|
||||
self.skipped = dict([(host, 0) for host in hosts])
|
||||
self.passed = dict([(host, 0) for host in hosts])
|
||||
|
||||
def get_item_name(self, event, colitem):
|
||||
return "/".join(colitem.listnames())
|
||||
|
||||
def report(self, what):
|
||||
repfun = getattr(self, "report_" + what.__class__.__name__,
|
||||
self.report_unknown)
|
||||
try:
|
||||
return repfun(what)
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except:
|
||||
print "Internal reporting problem"
|
||||
excinfo = py.code.ExceptionInfo()
|
||||
for i in excinfo.traceback:
|
||||
print str(i)[2:-1]
|
||||
print excinfo
|
||||
|
||||
def report_unknown(self, what):
|
||||
if self.config.option.verbose:
|
||||
print "Unknown report: %s" % what
|
||||
|
||||
def report_SendItem(self, item):
|
||||
address = item.host.hostname
|
||||
if self.config.option.verbose:
|
||||
print "Sending %s to %s" % (item.item,
|
||||
address)
|
||||
|
||||
def report_HostRSyncing(self, item):
|
||||
print "%10s: RSYNC ==> %s" % (item.host.hostname[:10],
|
||||
item.host.relpath)
|
||||
|
||||
def report_HostReady(self, item):
|
||||
self.hosts_to_rsync -= 1
|
||||
if self.hosts_to_rsync:
|
||||
print "%10s: READY (still %d to go)" % (item.host.hostname[:10],
|
||||
self.hosts_to_rsync)
|
||||
else:
|
||||
print "%10s: READY" % item.host.hostname[:10]
|
||||
|
||||
def report_TestStarted(self, item):
|
||||
hostnames = [host.hostname for host in item.hosts]
|
||||
txt = " Test started, hosts: %s " % ", ".join(hostnames)
|
||||
self.hosts_to_rsync = len(item.hosts)
|
||||
self.out.sep("=", txt)
|
||||
self.timestart = item.timestart
|
||||
|
||||
def report_RsyncFinished(self, item):
|
||||
self.timersync = item.time
|
||||
|
||||
def report_ImmediateFailure(self, event):
|
||||
self.repr_failure(event.item, event.outcome)
|
||||
|
||||
def report_TestFinished(self, item):
|
||||
self.out.line()
|
||||
assert hasattr(self, 'timestart')
|
||||
self.timeend = item.timeend
|
||||
self.skips()
|
||||
self.failures()
|
||||
if hasattr(self, 'nodes'): # XXX: Testing
|
||||
self.hangs()
|
||||
self.summary()
|
||||
return len(self.failed_tests_outcome) > 0
|
||||
|
||||
def hangs(self):
|
||||
h = []
|
||||
if self.config.option.exitfirst:
|
||||
# reporting hanging nodes in that case makes no sense at all
|
||||
# but we should share some code in all reporters than
|
||||
return
|
||||
for node in self.nodes:
|
||||
h += [(i, node.channel.gateway.sshaddress) for i in node.pending]
|
||||
if h:
|
||||
self.out.sep("=", " HANGING NODES ")
|
||||
for i, node in h:
|
||||
self.out.line("%s on %s" % (" ".join(i.listnames()), node))
|
||||
|
||||
def failures(self):
|
||||
if self.failed_tests_outcome:
|
||||
self.out.sep("=", " FAILURES ")
|
||||
for event in self.failed_tests_outcome:
|
||||
if isinstance(event, report.ReceivedItemOutcome):
|
||||
host = self.gethost(event)
|
||||
self.out.sep('_', "%s on %s" %
|
||||
(" ".join(event.item.listnames()), host))
|
||||
if event.outcome.signal:
|
||||
self.repr_signal(event.item, event.outcome)
|
||||
else:
|
||||
self.repr_failure(event.item, event.outcome)
|
||||
else:
|
||||
self.out.sep('_', " ".join(event.item.listnames()))
|
||||
out = outcome.Outcome(excinfo=event.excinfo)
|
||||
self.repr_failure(event.item, outcome.ReprOutcome(out.make_repr()))
|
||||
|
||||
def gethost(self, event):
|
||||
return event.host.hostname
|
||||
|
||||
def repr_failure(self, item, outcome):
|
||||
excinfo = outcome.excinfo
|
||||
traceback = excinfo.traceback
|
||||
#if item and not self.config.option.fulltrace:
|
||||
# path, firstlineno = item.getpathlineno()
|
||||
if not traceback:
|
||||
self.out.line("empty traceback from item %r" % (item,))
|
||||
return
|
||||
#handler = getattr(self, 'repr_failure_tb%s' % self.config.option.tbstyle)
|
||||
self.repr_traceback(item, excinfo, traceback)
|
||||
if outcome.stdout:
|
||||
self.out.sep('-', " Captured process stdout: ")
|
||||
self.out.write(outcome.stdout)
|
||||
if outcome.stderr:
|
||||
self.out.sep('-', " Captured process stderr: ")
|
||||
self.out.write(outcome.stderr)
|
||||
|
||||
def repr_signal(self, item, outcome):
|
||||
signal = outcome.signal
|
||||
self.out.line("Received signal: %d" % outcome.signal)
|
||||
if outcome.stdout:
|
||||
self.out.sep('-', " Captured process stdout: ")
|
||||
self.out.write(outcome.stdout)
|
||||
if outcome.stderr:
|
||||
self.out.sep('-', " Captured process stderr: ")
|
||||
self.out.write(outcome.stderr)
|
||||
|
||||
def repr_traceback(self, item, excinfo, traceback):
|
||||
if self.config.option.tbstyle == 'long':
|
||||
for index, entry in py.builtin.enumerate(traceback):
|
||||
self.out.sep('-')
|
||||
self.out.line("%s: %s" % (entry.path, entry.lineno))
|
||||
self.repr_source(entry.relline, str(entry.source))
|
||||
elif self.config.option.tbstyle == 'short':
|
||||
for index, entry in py.builtin.enumerate(traceback):
|
||||
self.out.line("%s: %s" % (entry.path, entry.lineno))
|
||||
self.out.line(entry.source)
|
||||
self.out.line("%s: %s" % (excinfo.typename, excinfo.value))
|
||||
|
||||
def repr_source(self, relline, source):
|
||||
for num, line in enumerate(source.split("\n")):
|
||||
if num == relline:
|
||||
self.out.line(">>>>" + line)
|
||||
else:
|
||||
self.out.line(" " + line)
|
||||
|
||||
def skips(self):
|
||||
texts = {}
|
||||
for event in self.skipped_tests_outcome:
|
||||
colitem = event.item
|
||||
if isinstance(event, report.ReceivedItemOutcome):
|
||||
outcome = event.outcome
|
||||
text = outcome.skipped
|
||||
itemname = self.get_item_name(event, colitem)
|
||||
elif isinstance(event, report.SkippedTryiter):
|
||||
text = str(event.excinfo.value)
|
||||
itemname = "/".join(colitem.listnames())
|
||||
if text not in texts:
|
||||
texts[text] = [itemname]
|
||||
else:
|
||||
texts[text].append(itemname)
|
||||
|
||||
if texts:
|
||||
self.out.line()
|
||||
self.out.sep('_', 'reasons for skipped tests')
|
||||
for text, items in texts.items():
|
||||
for item in items:
|
||||
self.out.line('Skipped in %s' % item)
|
||||
self.out.line("reason: %s" % text)
|
||||
|
||||
def summary(self):
|
||||
def gather(dic):
|
||||
total = 0
|
||||
for key, val in dic.iteritems():
|
||||
total += val
|
||||
return total
|
||||
|
||||
def create_str(name, count):
|
||||
if count:
|
||||
return ", %d %s" % (count, name)
|
||||
return ""
|
||||
|
||||
total_passed = gather(self.passed)
|
||||
total_failed = gather(self.failed)
|
||||
total_skipped = gather(self.skipped)
|
||||
total = total_passed + total_failed + total_skipped
|
||||
skipped_str = create_str("skipped", total_skipped)
|
||||
failed_str = create_str("failed", total_failed)
|
||||
self.print_summary(total, skipped_str, failed_str)
|
||||
|
||||
def print_summary(self, total, skipped_str, failed_str):
|
||||
self.out.sep("=", " %d test run%s%s in %.2fs (rsync: %.2f)" %
|
||||
(total, skipped_str, failed_str, self.timeend - self.timestart,
|
||||
self.timersync - self.timestart))
|
||||
|
||||
def report_SkippedTryiter(self, event):
|
||||
#event.outcome.excinfo.source =
|
||||
self.skipped_tests_outcome.append(event)
|
||||
|
||||
def report_FailedTryiter(self, event):
|
||||
pass
|
||||
# XXX: right now we do not do anything with it
|
||||
|
||||
def report_ReceivedItemOutcome(self, event):
|
||||
host = event.host
|
||||
if event.outcome.passed:
|
||||
status = "PASSED "
|
||||
self.passed[host] += 1
|
||||
elif event.outcome.skipped:
|
||||
status = "SKIPPED"
|
||||
self.skipped_tests_outcome.append(event)
|
||||
self.skipped[host] += 1
|
||||
else:
|
||||
status = "FAILED "
|
||||
self.failed[host] += 1
|
||||
self.failed_tests_outcome.append(event)
|
||||
# we'll take care of them later
|
||||
itempath = " ".join(event.item.listnames()[1:])
|
||||
print "%10s: %s %s" %(host.hostname[:10], status, itempath)
|
||||
|
||||
def report_Nodes(self, event):
|
||||
self.nodes = event.nodes
|
||||
|
||||
class RemoteReporter(AbstractReporter):
|
||||
def get_item_name(self, event, colitem):
|
||||
return event.host.hostname + ":" + \
|
||||
"/".join(colitem.listnames())
|
||||
|
||||
def report_FailedTryiter(self, event):
|
||||
self.out.line("FAILED TO LOAD MODULE: %s\n" % "/".join(event.item.listnames()))
|
||||
self.failed_tests_outcome.append(event)
|
||||
|
||||
def report_SkippedTryiter(self, event):
|
||||
self.out.line("Skipped (%s) %s\n" % (str(event.excinfo.value), "/".
|
||||
join(event.item.listnames())))
|
||||
|
||||
class LocalReporter(AbstractReporter):
|
||||
def get_item_name(self, event, colitem):
|
||||
return "/".join(colitem.listnames())
|
||||
|
||||
def report_SkippedTryiter(self, event):
|
||||
#self.show_item(event.item, False)
|
||||
self.out.write("- skipped (%s)" % event.excinfo.value)
|
||||
|
||||
def report_FailedTryiter(self, event):
|
||||
#self.show_item(event.item, False)
|
||||
self.out.write("- FAILED TO LOAD MODULE")
|
||||
self.failed_tests_outcome.append(event)
|
||||
|
||||
def report_ReceivedItemOutcome(self, event):
|
||||
host = self.hosts[0]
|
||||
if event.outcome.passed:
|
||||
self.passed[host] += 1
|
||||
self.out.write(".")
|
||||
elif event.outcome.skipped:
|
||||
self.skipped_tests_outcome.append(event)
|
||||
self.skipped[host] += 1
|
||||
self.out.write("s")
|
||||
else:
|
||||
self.failed[host] += 1
|
||||
self.failed_tests_outcome.append(event)
|
||||
self.out.write("F")
|
||||
|
||||
def report_ItemStart(self, event):
|
||||
self.show_item(event.item)
|
||||
|
||||
def show_item(self, item, count_elems = True):
|
||||
if isinstance(item, py.test.collect.Module):
|
||||
# XXX This is a terrible hack, I don't like it
|
||||
# and will rewrite it at some point
|
||||
#self.count = 0
|
||||
lgt = len(list(item.tryiter()))
|
||||
#self.lgt = lgt
|
||||
# print names relative to current workdir
|
||||
name = "/".join(item.listnames())
|
||||
local = str(py.path.local())
|
||||
d = str(self.pkgdir.dirpath().dirpath())
|
||||
if local.startswith(d):
|
||||
local = local[len(d) + 1:]
|
||||
if local and name.startswith(local):
|
||||
name = name[len(local) + 1:]
|
||||
self.out.write("\n%s[%d] " % (name, lgt))
|
||||
|
||||
def gethost(self, event):
|
||||
return 'localhost'
|
||||
|
||||
def hangs(self):
|
||||
pass
|
||||
271
py/test/rsession/rest.py
Normal file
271
py/test/rsession/rest.py
Normal file
@@ -0,0 +1,271 @@
|
||||
|
||||
""" Rest reporting stuff
|
||||
"""
|
||||
|
||||
import py
|
||||
import sys
|
||||
from StringIO import StringIO
|
||||
from py.__.test.rsession.reporter import AbstractReporter
|
||||
from py.__.test.rsession import report
|
||||
from py.__.rest.rst import *
|
||||
|
||||
class RestReporter(AbstractReporter):
|
||||
linkwriter = None
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(RestReporter, self).__init__(*args, **kwargs)
|
||||
self.rest = Rest()
|
||||
self.traceback_num = 0
|
||||
|
||||
def get_linkwriter(self):
|
||||
if self.linkwriter is None:
|
||||
try:
|
||||
self.linkwriter = self.config.getvalue('linkwriter')
|
||||
except KeyError:
|
||||
print >>sys.stderr, ('no linkwriter configured, using default '
|
||||
'one')
|
||||
self.linkwriter = RelLinkWriter()
|
||||
return self.linkwriter
|
||||
|
||||
def report_unknown(self, what):
|
||||
if self.config.option.verbose:
|
||||
self.add_rest(Paragraph("Unknown report: %s" % what))
|
||||
|
||||
def report_SendItem(self, item):
|
||||
address = item.channel.gateway.host.hostname
|
||||
if self.config.option.verbose:
|
||||
self.add_rest(Paragraph('sending item %s to %s' % (item.item,
|
||||
address)))
|
||||
|
||||
def report_HostRSyncing(self, item):
|
||||
self.add_rest(LiteralBlock('%10s: RSYNC ==> %s' % (item.hostname[:10],
|
||||
item.remoterootpath)))
|
||||
|
||||
def report_HostReady(self, item):
|
||||
self.add_rest(LiteralBlock('%10s: READY' % (item.hostname[:10],)))
|
||||
|
||||
def report_TestStarted(self, event):
|
||||
txt = "Running tests on hosts: %s" % ", ".join(event.hosts)
|
||||
self.add_rest(Title(txt, abovechar='=', belowchar='='))
|
||||
self.timestart = event.timestart
|
||||
|
||||
def report_TestFinished(self, item):
|
||||
self.timeend = item.timeend
|
||||
self.summary()
|
||||
return len(self.failed_tests_outcome) > 0
|
||||
|
||||
def report_ImmediateFailure(self, item):
|
||||
pass
|
||||
|
||||
def report_ItemStart(self, event):
|
||||
item = event.item
|
||||
if isinstance(item, py.test.collect.Module):
|
||||
lgt = len(list(item.tryiter()))
|
||||
lns = item.listnames()[1:]
|
||||
name = "/".join(lns)
|
||||
link = self.get_linkwriter().get_link(self.get_rootpath(item),
|
||||
item.fspath)
|
||||
if link:
|
||||
name = Link(name, link)
|
||||
txt = 'Testing module %s (%d items)' % (name, lgt)
|
||||
self.add_rest(Title('Testing module', name, '(%d items)' % (lgt,),
|
||||
belowchar='-'))
|
||||
|
||||
def get_rootpath(self, item):
|
||||
root = item.parent
|
||||
while root.parent is not None:
|
||||
root = root.parent
|
||||
return root.fspath
|
||||
|
||||
def print_summary(self, total, skipped_str, failed_str):
|
||||
self.skips()
|
||||
self.failures()
|
||||
|
||||
txt = "%d tests run%s%s in %.2fs (rsync: %.2f)" % \
|
||||
(total, skipped_str, failed_str, self.timeend - self.timestart,
|
||||
self.timersync - self.timestart)
|
||||
self.add_rest(Title(txt, belowchar='-'))
|
||||
|
||||
# since we're rendering each item, the links haven't been rendered
|
||||
# yet
|
||||
self.out.write(self.rest.render_links())
|
||||
|
||||
def report_ReceivedItemOutcome(self, event):
|
||||
host = event.channel.gateway.host
|
||||
if event.outcome.passed:
|
||||
status = [Strong("PASSED")]
|
||||
self.passed[host] += 1
|
||||
elif event.outcome.skipped:
|
||||
status = [Strong("SKIPPED")]
|
||||
self.skipped_tests_outcome.append(event)
|
||||
self.skipped[host] += 1
|
||||
else:
|
||||
status = [Strong("FAILED"),
|
||||
InternalLink("traceback%d" % self.traceback_num)]
|
||||
self.traceback_num += 1
|
||||
self.failed[host] += 1
|
||||
self.failed_tests_outcome.append(event)
|
||||
# we'll take care of them later
|
||||
itempath = self.get_path_from_item(event.item)
|
||||
status.append(Text(itempath))
|
||||
hostname = host.hostname
|
||||
self.add_rest(ListItem(Text("%10s:" % (hostname[:10],)), *status))
|
||||
|
||||
def skips(self):
|
||||
# XXX hrmph, copied code
|
||||
texts = {}
|
||||
for event in self.skipped_tests_outcome:
|
||||
colitem = event.item
|
||||
if isinstance(event, report.ReceivedItemOutcome):
|
||||
outcome = event.outcome
|
||||
text = outcome.skipped
|
||||
itemname = self.get_item_name(event, colitem)
|
||||
elif isinstance(event, report.SkippedTryiter):
|
||||
text = str(event.excinfo.value)
|
||||
itemname = "/".join(colitem.listnames())
|
||||
if text not in texts:
|
||||
texts[text] = [itemname]
|
||||
else:
|
||||
texts[text].append(itemname)
|
||||
if texts:
|
||||
self.add_rest(Title('Reasons for skipped tests:', belowchar='+'))
|
||||
for text, items in texts.items():
|
||||
for item in items:
|
||||
self.add_rest(ListItem('%s: %s' % (item, text)))
|
||||
|
||||
def get_host(self, event):
|
||||
return event.channel.gateway.host
|
||||
|
||||
def failures(self):
|
||||
self.traceback_num = 0
|
||||
tbstyle = self.config.option.tbstyle
|
||||
if self.failed_tests_outcome:
|
||||
self.add_rest(Title('Exceptions:', belowchar='+'))
|
||||
for i, event in enumerate(self.failed_tests_outcome):
|
||||
if i > 0:
|
||||
self.add_rest(Transition())
|
||||
if isinstance(event, report.ReceivedItemOutcome):
|
||||
host = self.get_host(event)
|
||||
itempath = self.get_path_from_item(event.item)
|
||||
root = self.get_rootpath(event.item)
|
||||
link = self.get_linkwriter().get_link(root, event.item.fspath)
|
||||
t = Title(belowchar='+')
|
||||
if link:
|
||||
t.add(Link(itempath, link))
|
||||
else:
|
||||
t.add(Text(itempath))
|
||||
t.add(Text('on %s' % (host.hostname,)))
|
||||
self.add_rest(t)
|
||||
if event.outcome.signal:
|
||||
self.repr_signal(event.item, event.outcome)
|
||||
else:
|
||||
self.repr_failure(event.item, event.outcome, tbstyle)
|
||||
else:
|
||||
itempath = self.get_path_from_item(event.item)
|
||||
root = self.get_rootpath(event.item)
|
||||
link = self.get_linkwriter().get_link(root, event.item.fspath)
|
||||
t = Title(abovechar='+', belowchar='+')
|
||||
if link:
|
||||
t.add(Link(itempath, link))
|
||||
else:
|
||||
t.add(Text(itempath))
|
||||
out = outcome.Outcome(excinfo=event.excinfo)
|
||||
self.repr_failure(event.item,
|
||||
outcome.ReprOutcome(out.make_repr()),
|
||||
tbstyle)
|
||||
|
||||
def repr_signal(self, item, outcome):
|
||||
signal = outcome.signal
|
||||
self.add_rest(Title('Received signal: %d' % (outcome.signal,),
|
||||
abovechar='+', belowchar='+'))
|
||||
if outcome.stdout.strip():
|
||||
self.add_rest(Paragraph('Captured process stdout:'))
|
||||
self.add_rest(LiteralBlock(outcome.stdout))
|
||||
if outcome.stderr.strip():
|
||||
self.add_rest(Paragraph('Captured process stderr:'))
|
||||
self.add_rest(LiteralBlock(outcome.stderr))
|
||||
|
||||
def repr_failure(self, item, outcome, style):
|
||||
excinfo = outcome.excinfo
|
||||
traceback = excinfo.traceback
|
||||
if not traceback:
|
||||
self.add_rest(Paragraph('empty traceback from item %r' % (item,)))
|
||||
return
|
||||
self.repr_traceback(item, excinfo, traceback, style)
|
||||
if outcome.stdout:
|
||||
self.add_rest(Title('Captured process stdout:', abovechar='+',
|
||||
belowchar='+'))
|
||||
self.add_rest(LiteralBlock(outcome.stdout))
|
||||
if outcome.stderr:
|
||||
self.add_rest(Title('Captured process stderr:', abovechar='+',
|
||||
belowchar='+'))
|
||||
self.add_rest(LiteralBlock(outcome.stderr))
|
||||
|
||||
def repr_traceback(self, item, excinfo, traceback, style):
|
||||
root = self.get_rootpath(item)
|
||||
self.add_rest(LinkTarget('traceback%d' % self.traceback_num, ""))
|
||||
self.traceback_num += 1
|
||||
if style == 'long':
|
||||
for entry in traceback:
|
||||
link = self.get_linkwriter().get_link(root,
|
||||
py.path.local(entry.path))
|
||||
if link:
|
||||
self.add_rest(Title(Link(entry.path, link),
|
||||
'line %d' % (entry.lineno,),
|
||||
belowchar='+', abovechar='+'))
|
||||
else:
|
||||
self.add_rest(Title('%s line %d' % (entry.path,
|
||||
entry.lineno,),
|
||||
belowchar='+', abovechar='+'))
|
||||
self.add_rest(LiteralBlock(self.prepare_source(entry.relline,
|
||||
entry.source)))
|
||||
elif style == 'short':
|
||||
text = []
|
||||
for entry in traceback:
|
||||
text.append('%s line %d' % (entry.path, entry.lineno))
|
||||
text.append(' %s' % (entry.source.strip(),))
|
||||
self.add_rest(LiteralBlock('\n'.join(text)))
|
||||
self.add_rest(Title(excinfo.typename, belowchar='+'))
|
||||
self.add_rest(LiteralBlock(excinfo.value))
|
||||
|
||||
def prepare_source(self, relline, source):
|
||||
text = []
|
||||
for num, line in enumerate(source.split('\n')):
|
||||
if num == relline:
|
||||
text.append('>>> %s' % (line,))
|
||||
else:
|
||||
text.append(' %s' % (line,))
|
||||
return '\n'.join(text)
|
||||
|
||||
def add_rest(self, item):
|
||||
self.rest.add(item)
|
||||
self.out.write('%s\n\n' % (item.text(),))
|
||||
|
||||
def get_path_from_item(self, item):
|
||||
lns = item.listnames()[1:]
|
||||
for i, ln in enumerate(lns):
|
||||
if i > 0 and ln != '()':
|
||||
lns[i] = '/%s' % (ln,)
|
||||
itempath = ''.join(lns)
|
||||
return itempath
|
||||
|
||||
class AbstractLinkWriter(object):
|
||||
def get_link(self, base, path):
|
||||
pass
|
||||
|
||||
class NoLinkWriter(AbstractLinkWriter):
|
||||
def get_link(self, base, path):
|
||||
return ''
|
||||
|
||||
class LinkWriter(AbstractLinkWriter):
|
||||
def __init__(self, baseurl):
|
||||
self.baseurl = baseurl
|
||||
|
||||
def get_link(self, base, path):
|
||||
relpath = path.relto(base)
|
||||
return self.baseurl + relpath
|
||||
|
||||
class RelLinkWriter(AbstractLinkWriter):
|
||||
def get_link(self, base, path):
|
||||
return path.relto(base)
|
||||
|
||||
324
py/test/rsession/rsession.py
Normal file
324
py/test/rsession/rsession.py
Normal file
@@ -0,0 +1,324 @@
|
||||
|
||||
""" Remote session base class
|
||||
"""
|
||||
|
||||
import os
|
||||
import py
|
||||
import sys
|
||||
import re
|
||||
import time
|
||||
|
||||
from py.__.test.rsession import report
|
||||
from py.__.test.rsession.master import \
|
||||
setup_slave, MasterNode, dispatch_loop, itemgen, randomgen
|
||||
from py.__.test.rsession.hostmanage import init_hosts, teardown_hosts, HostInfo
|
||||
|
||||
from py.__.test.rsession.local import local_loop, plain_runner, apigen_runner,\
|
||||
box_runner, RunnerPolicy
|
||||
from py.__.test.rsession.reporter import LocalReporter, RemoteReporter
|
||||
|
||||
class RemoteOptions(object):
|
||||
def __init__(self, d):
|
||||
self.d = d
|
||||
|
||||
def __getattr__(self, attr):
|
||||
if attr == 'd':
|
||||
return self.__dict__['d']
|
||||
return self.d[attr]
|
||||
|
||||
def __setitem__(self, item, val):
|
||||
self.d[item] = val
|
||||
|
||||
# XXX: Must be initialised somehow
|
||||
remote_options = RemoteOptions({'we_are_remote':False})
|
||||
|
||||
class SessionOptions:
|
||||
defaults = {
|
||||
'max_tasks_per_node' : 15,
|
||||
'runner_policy' : 'plain_runner',
|
||||
'nice_level' : 0,
|
||||
'waittime' : 100.0,
|
||||
'import_pypy' : False,
|
||||
}
|
||||
|
||||
config = None
|
||||
|
||||
def getvalue(self, opt):
|
||||
try:
|
||||
return getattr(self.config.getvalue('SessionOptions'), opt)
|
||||
except (KeyError, AttributeError):
|
||||
try:
|
||||
return self.defaults[opt]
|
||||
except KeyError:
|
||||
raise AttributeError("Option %s undeclared" % opt)
|
||||
|
||||
def bind_config(self, config):
|
||||
self.config = config
|
||||
# copy to remote session options
|
||||
try:
|
||||
ses_opt = self.config.getvalue('SessionOptions').__dict__
|
||||
except KeyError:
|
||||
ses_opt = self.defaults
|
||||
for key in self.defaults:
|
||||
try:
|
||||
val = ses_opt[key]
|
||||
except KeyError:
|
||||
val = self.defaults[key]
|
||||
remote_options[key] = val
|
||||
# copy to remote all options
|
||||
for item, val in config.option.__dict__.items():
|
||||
remote_options[item] = val
|
||||
|
||||
def __repr__(self):
|
||||
return "<SessionOptions %s>" % self.config
|
||||
|
||||
def __getattr__(self, attr):
|
||||
if self.config is None:
|
||||
raise AttributeError("Need to set up config first")
|
||||
return self.getvalue(attr)
|
||||
|
||||
session_options = SessionOptions()
|
||||
|
||||
class AbstractSession(object):
|
||||
"""
|
||||
An abstract session executes collectors/items through a runner.
|
||||
|
||||
"""
|
||||
def __init__(self, config, optimise_localhost=True):
|
||||
self.config = config
|
||||
self.optimise_localhost = optimise_localhost
|
||||
|
||||
def make_colitems(paths, baseon):
|
||||
# we presume that from the base we can simply get to
|
||||
# the target paths by joining the basenames
|
||||
res = []
|
||||
for x in paths:
|
||||
x = py.path.local(x)
|
||||
current = py.test.collect.Directory(baseon)
|
||||
relparts = x.relto(baseon).split(x.sep)
|
||||
assert relparts
|
||||
for part in relparts:
|
||||
next = current.join(part)
|
||||
assert next is not None, (current, part)
|
||||
current = next
|
||||
res.append(current)
|
||||
return res
|
||||
make_colitems = staticmethod(make_colitems)
|
||||
|
||||
def getpkgdir(path):
|
||||
path = py.path.local(path)
|
||||
pkgpath = path.pypkgpath()
|
||||
if pkgpath is None:
|
||||
pkgpath = path
|
||||
return pkgpath
|
||||
getpkgdir = staticmethod(getpkgdir)
|
||||
|
||||
def init_reporter(self, reporter, sshhosts, reporter_class, arg=""):
|
||||
startserverflag = self.config.option.startserver
|
||||
restflag = self.config.option.restreport
|
||||
|
||||
if startserverflag and reporter is None:
|
||||
from py.__.test.rsession.web import start_server, exported_methods
|
||||
|
||||
reporter = exported_methods.report
|
||||
start_server()
|
||||
if self.config.option.runbrowser:
|
||||
import webbrowser
|
||||
webbrowser.open("http://localhost:8000")
|
||||
elif reporter is None:
|
||||
if restflag:
|
||||
from py.__.test.rsession.rest import RestReporter
|
||||
reporter_class = RestReporter
|
||||
if arg:
|
||||
reporter_instance = reporter_class(self.config, sshhosts, self.getpkgdir(arg))
|
||||
else:
|
||||
reporter_instance = reporter_class(self.config, sshhosts)
|
||||
reporter = reporter_instance.report
|
||||
else:
|
||||
startserverflag = False
|
||||
|
||||
return reporter, startserverflag
|
||||
|
||||
def reporterror(reporter, data):
|
||||
excinfo, item = data
|
||||
if excinfo is None:
|
||||
reporter(report.ItemStart(item))
|
||||
elif excinfo.type is py.test.Item.Skipped:
|
||||
reporter(report.SkippedTryiter(excinfo, item))
|
||||
else:
|
||||
reporter(report.FailedTryiter(excinfo, item))
|
||||
reporterror = staticmethod(reporterror)
|
||||
|
||||
def kill_server(self, startserverflag):
|
||||
if startserverflag:
|
||||
from py.__.test.rsession.web import kill_server
|
||||
kill_server()
|
||||
|
||||
def wrap_reporter(self, reporter):
|
||||
""" We wrap reporter around, which makes it possible to us to track
|
||||
number of failures
|
||||
"""
|
||||
self.was_failure = False
|
||||
def new_reporter(event):
|
||||
if isinstance(event, report.ReceivedItemOutcome) and \
|
||||
not event.outcome.passed and \
|
||||
not event.outcome.skipped:
|
||||
self.was_failure = True
|
||||
return reporter(event)
|
||||
checkfun = lambda : self.config.option.exitfirst and \
|
||||
self.was_failure
|
||||
# for tests
|
||||
self.checkfun = checkfun
|
||||
return new_reporter, checkfun
|
||||
|
||||
def parse_directories(sshhosts):
|
||||
directories = {}
|
||||
for host in sshhosts:
|
||||
m = re.match("^(.*?):(.*)$", host.hostname)
|
||||
if m:
|
||||
host.hostname = m.group(1)
|
||||
host.relpath = m.group(2) + "-" + host.hostname
|
||||
else:
|
||||
host.relpath = "pytestcache-%s" % host.hostname
|
||||
|
||||
class RSession(AbstractSession):
|
||||
""" Remote version of session
|
||||
"""
|
||||
def main(self, reporter=None):
|
||||
""" main loop for running tests. """
|
||||
args = self.config.remaining
|
||||
|
||||
session_options.bind_config(self.config)
|
||||
sshhosts, remotepython, rsync_roots = self.read_distributed_config()
|
||||
reporter, startserverflag = self.init_reporter(reporter,
|
||||
sshhosts, RemoteReporter)
|
||||
reporter, checkfun = self.wrap_reporter(reporter)
|
||||
|
||||
reporter(report.TestStarted(sshhosts))
|
||||
|
||||
pkgdir = self.getpkgdir(args[0])
|
||||
done_dict = {}
|
||||
nodes = init_hosts(reporter, sshhosts, pkgdir,
|
||||
rsync_roots, remotepython, remote_options=remote_options.d,
|
||||
optimise_localhost=self.optimise_localhost, done_dict=done_dict)
|
||||
reporter(report.RsyncFinished())
|
||||
|
||||
try:
|
||||
self.dispatch_tests(nodes, args, pkgdir, reporter, checkfun, done_dict)
|
||||
finally:
|
||||
teardown_hosts(reporter, [node.channel for node in nodes], nodes,
|
||||
exitfirst=self.config.option.exitfirst)
|
||||
reporter(report.Nodes(nodes))
|
||||
retval = reporter(report.TestFinished())
|
||||
self.kill_server(startserverflag)
|
||||
return retval
|
||||
|
||||
def read_distributed_config(self):
|
||||
try:
|
||||
rsync_roots = self.config.getvalue("distrsync_roots")
|
||||
except:
|
||||
rsync_roots = None # all files and directories in the pkgdir
|
||||
sshhosts = [HostInfo(i) for i in
|
||||
self.config.getvalue("disthosts")]
|
||||
parse_directories(sshhosts)
|
||||
try:
|
||||
remotepython = self.config.getvalue("dist_remotepython")
|
||||
except:
|
||||
remotepython = None
|
||||
return sshhosts, remotepython, rsync_roots
|
||||
|
||||
def dispatch_tests(self, nodes, args, pkgdir, reporter, checkfun, done_dict):
|
||||
colitems = self.make_colitems(args, baseon=pkgdir.dirpath())
|
||||
keyword = self.config.option.keyword
|
||||
itemgenerator = itemgen(colitems, reporter, keyword, self.reporterror)
|
||||
|
||||
all_tests = dispatch_loop(nodes, itemgenerator, checkfun)
|
||||
#if all_tests:
|
||||
# todo = {}
|
||||
# for key, value in all_tests.items():
|
||||
# if key not in done_dict:
|
||||
# todo[key] = True
|
||||
# rg = randomgen(todo, done_dict)
|
||||
# dispatch_loop(nodes, rg, lambda:False, max_tasks_per_node=1)
|
||||
|
||||
|
||||
class LSession(AbstractSession):
|
||||
""" Local version of session
|
||||
"""
|
||||
def main(self, reporter=None, runner=None):
|
||||
# check out if used options makes any sense
|
||||
args = self.config.remaining
|
||||
|
||||
sshhosts = [HostInfo('localhost')] # this is just an info to reporter
|
||||
|
||||
if not self.config.option.nomagic:
|
||||
py.magic.invoke(assertion=1)
|
||||
|
||||
session_options.bind_config(self.config)
|
||||
reporter, startserverflag = self.init_reporter(reporter,
|
||||
sshhosts, LocalReporter, args[0])
|
||||
reporter, checkfun = self.wrap_reporter(reporter)
|
||||
|
||||
reporter(report.TestStarted(sshhosts))
|
||||
pkgdir = self.getpkgdir(args[0])
|
||||
colitems = self.make_colitems(args, baseon=pkgdir.dirpath())
|
||||
reporter(report.RsyncFinished())
|
||||
|
||||
if runner is None:
|
||||
runner = self.init_runner(pkgdir)
|
||||
|
||||
keyword = self.config.option.keyword
|
||||
|
||||
itemgenerator = itemgen(colitems, reporter, keyword, self.reporterror)
|
||||
local_loop(self, reporter, itemgenerator, checkfun, self.config, runner=runner)
|
||||
|
||||
retval = reporter(report.TestFinished())
|
||||
self.kill_server(startserverflag)
|
||||
|
||||
if not self.config.option.nomagic:
|
||||
py.magic.revoke(assertion=1)
|
||||
|
||||
self.write_docs(pkgdir)
|
||||
return retval
|
||||
|
||||
def write_docs(self, pkgdir):
|
||||
if self.config.option.apigen:
|
||||
from py.__.apigen.tracer.docstorage import DocStorageAccessor
|
||||
apigen = py.path.local(self.config.option.apigen).pyimport()
|
||||
print >>sys.stderr, 'building documentation'
|
||||
capture = py.io.OutErrCapture()
|
||||
try:
|
||||
try:
|
||||
apigen.build(pkgdir, DocStorageAccessor(self.docstorage))
|
||||
except (ValueError, AttributeError):
|
||||
#import traceback
|
||||
#exc, e, tb = sys.exc_info()
|
||||
#print '%s - %s' % (exc, e)
|
||||
#print ''.join(traceback.format_tb(tb))
|
||||
#del tb
|
||||
#print '-' * 79
|
||||
raise NotImplementedError("Provided script does not seem "
|
||||
"to contain build function")
|
||||
finally:
|
||||
capture.reset()
|
||||
|
||||
def init_runner(self, pkgdir):
|
||||
if self.config.option.apigen:
|
||||
from py.__.apigen.tracer.tracer import Tracer, DocStorage
|
||||
module = py
|
||||
try:
|
||||
apigen = py.path.local(self.config.option.apigen).pyimport()
|
||||
items = apigen.get_documentable_items(pkgdir)
|
||||
if isinstance(items, dict):
|
||||
self.docstorage = DocStorage().from_dict(items)
|
||||
else:
|
||||
self.docstorage = DocStorage().from_pkg(items)
|
||||
except ImportError:
|
||||
raise ImportError("Provided script cannot be imported")
|
||||
except (ValueError, AttributeError):
|
||||
raise NotImplementedError("Provided script does not seem "
|
||||
"to contain get_documentable_items")
|
||||
self.tracer = Tracer(self.docstorage)
|
||||
return apigen_runner
|
||||
else:
|
||||
return RunnerPolicy[session_options.runner_policy]
|
||||
137
py/test/rsession/rsync.py
Normal file
137
py/test/rsession/rsync.py
Normal file
@@ -0,0 +1,137 @@
|
||||
import py, os, stat, md5
|
||||
from Queue import Queue
|
||||
|
||||
|
||||
class RSync(object):
|
||||
|
||||
def __init__(self, callback=None, **options):
|
||||
for name in options:
|
||||
assert name in ('delete')
|
||||
self.options = options
|
||||
self.callback = callback
|
||||
self.channels = {}
|
||||
self.receivequeue = Queue()
|
||||
self.links = []
|
||||
|
||||
def filter(self, path):
|
||||
return True
|
||||
|
||||
def add_target(self, gateway, destdir, finishedcallback=None):
|
||||
def itemcallback(req):
|
||||
self.receivequeue.put((channel, req))
|
||||
channel = gateway.remote_exec(REMOTE_SOURCE)
|
||||
channel.setcallback(itemcallback, endmarker = None)
|
||||
channel.send((str(destdir), self.options))
|
||||
self.channels[channel] = finishedcallback
|
||||
|
||||
def send(self, sourcedir):
|
||||
self.sourcedir = str(sourcedir)
|
||||
# normalize a trailing '/' away
|
||||
self.sourcedir = os.path.dirname(os.path.join(self.sourcedir, 'x'))
|
||||
# send directory structure and file timestamps/sizes
|
||||
self._send_directory_structure(self.sourcedir)
|
||||
|
||||
# paths and to_send are only used for doing
|
||||
# progress-related callbacks
|
||||
self.paths = {}
|
||||
self.to_send = {}
|
||||
|
||||
# send modified file to clients
|
||||
while self.channels:
|
||||
channel, req = self.receivequeue.get()
|
||||
if req is None:
|
||||
# end-of-channel
|
||||
if channel in self.channels:
|
||||
# too early! we must have got an error
|
||||
channel.waitclose()
|
||||
# or else we raise one
|
||||
raise IOError('connection unexpectedly closed: %s ' % (
|
||||
channel.gateway,))
|
||||
else:
|
||||
command, data = req
|
||||
if command == "links":
|
||||
for link in self.links:
|
||||
channel.send(link)
|
||||
# completion marker, this host is done
|
||||
channel.send(42)
|
||||
elif command == "done":
|
||||
finishedcallback = self.channels.pop(channel)
|
||||
if finishedcallback:
|
||||
finishedcallback()
|
||||
elif command == "ack":
|
||||
if self.callback:
|
||||
self.callback("ack", self.paths[data], channel)
|
||||
elif command == "list_done":
|
||||
# sum up all to send
|
||||
if self.callback:
|
||||
s = sum([self.paths[i] for i in self.to_send[channel]])
|
||||
self.callback("list", s, channel)
|
||||
elif command == "send":
|
||||
modified_rel_path, checksum = data
|
||||
modifiedpath = os.path.join(self.sourcedir, *modified_rel_path)
|
||||
f = open(modifiedpath, 'rb')
|
||||
data = f.read()
|
||||
|
||||
# provide info to progress callback function
|
||||
modified_rel_path = "/".join(modified_rel_path)
|
||||
self.paths[modified_rel_path] = len(data)
|
||||
if channel not in self.to_send:
|
||||
self.to_send[channel] = []
|
||||
self.to_send[channel].append(modified_rel_path)
|
||||
|
||||
f.close()
|
||||
if checksum is not None and checksum == md5.md5(data).digest():
|
||||
data = None # not really modified
|
||||
else:
|
||||
# ! there is a reason for the interning:
|
||||
# sharing multiple copies of the file's data
|
||||
data = intern(data)
|
||||
print '%s <= %s' % (
|
||||
channel.gateway._getremoteaddress(),
|
||||
modified_rel_path)
|
||||
channel.send(data)
|
||||
del data
|
||||
else:
|
||||
assert "Unknown command %s" % command
|
||||
|
||||
def _broadcast(self, msg):
|
||||
for channel in self.channels:
|
||||
channel.send(msg)
|
||||
|
||||
def _send_link(self, basename, linkpoint):
|
||||
self.links.append(("link", basename, linkpoint))
|
||||
|
||||
def _send_directory_structure(self, path):
|
||||
st = os.lstat(path)
|
||||
if stat.S_ISREG(st.st_mode):
|
||||
# regular file: send a timestamp/size pair
|
||||
self._broadcast((st.st_mtime, st.st_size))
|
||||
elif stat.S_ISDIR(st.st_mode):
|
||||
# dir: send a list of entries
|
||||
names = []
|
||||
subpaths = []
|
||||
for name in os.listdir(path):
|
||||
p = os.path.join(path, name)
|
||||
if self.filter(p):
|
||||
names.append(name)
|
||||
subpaths.append(p)
|
||||
self._broadcast(names)
|
||||
for p in subpaths:
|
||||
self._send_directory_structure(p)
|
||||
elif stat.S_ISLNK(st.st_mode):
|
||||
linkpoint = os.readlink(path)
|
||||
basename = path[len(self.sourcedir) + 1:]
|
||||
if not linkpoint.startswith(os.sep):
|
||||
# relative link, just send it
|
||||
self._send_link(basename, linkpoint)
|
||||
elif linkpoint.startswith(self.sourcedir):
|
||||
self._send_link(basename, linkpoint[len(self.sourcedir) + 1:])
|
||||
else:
|
||||
self._send_link(basename, linkpoint)
|
||||
self._broadcast(None)
|
||||
else:
|
||||
raise ValueError, "cannot sync %r" % (path,)
|
||||
|
||||
REMOTE_SOURCE = py.path.local(__file__).dirpath().\
|
||||
join('rsync_remote.py').open().read() + "\nf()"
|
||||
|
||||
86
py/test/rsession/rsync_remote.py
Normal file
86
py/test/rsession/rsync_remote.py
Normal file
@@ -0,0 +1,86 @@
|
||||
|
||||
def f():
|
||||
import os, stat, shutil, md5
|
||||
destdir, options = channel.receive()
|
||||
modifiedfiles = []
|
||||
|
||||
def remove(path):
|
||||
assert path.startswith(destdir)
|
||||
try:
|
||||
os.unlink(path)
|
||||
except OSError:
|
||||
# assume it's a dir
|
||||
shutil.rmtree(path)
|
||||
|
||||
def receive_directory_structure(path, relcomponents):
|
||||
try:
|
||||
st = os.lstat(path)
|
||||
except OSError:
|
||||
st = None
|
||||
msg = channel.receive()
|
||||
if isinstance(msg, list):
|
||||
if st and not stat.S_ISDIR(st.st_mode):
|
||||
os.unlink(path)
|
||||
st = None
|
||||
if not st:
|
||||
os.makedirs(path)
|
||||
entrynames = {}
|
||||
for entryname in msg:
|
||||
receive_directory_structure(os.path.join(path, entryname),
|
||||
relcomponents + [entryname])
|
||||
entrynames[entryname] = True
|
||||
if options.get('delete'):
|
||||
for othername in os.listdir(path):
|
||||
if othername not in entrynames:
|
||||
otherpath = os.path.join(path, othername)
|
||||
remove(otherpath)
|
||||
elif msg is not None:
|
||||
checksum = None
|
||||
if st:
|
||||
if stat.S_ISREG(st.st_mode):
|
||||
msg_mtime, msg_size = msg
|
||||
if msg_size != st.st_size:
|
||||
pass
|
||||
elif msg_mtime != st.st_mtime:
|
||||
f = open(path, 'rb')
|
||||
checksum = md5.md5(f.read()).digest()
|
||||
f.close()
|
||||
else:
|
||||
return # already fine
|
||||
else:
|
||||
remove(path)
|
||||
channel.send(("send", (relcomponents, checksum)))
|
||||
modifiedfiles.append((path, msg))
|
||||
receive_directory_structure(destdir, [])
|
||||
|
||||
STRICT_CHECK = False # seems most useful this way for py.test
|
||||
channel.send(("list_done", None))
|
||||
|
||||
for path, (time, size) in modifiedfiles:
|
||||
data = channel.receive()
|
||||
channel.send(("ack", path[len(destdir) + 1:]))
|
||||
if data is not None:
|
||||
if STRICT_CHECK and len(data) != size:
|
||||
raise IOError('file modified during rsync: %r' % (path,))
|
||||
f = open(path, 'wb')
|
||||
f.write(data)
|
||||
f.close()
|
||||
os.utime(path, (time, time))
|
||||
del data
|
||||
channel.send(("links", None))
|
||||
|
||||
msg = channel.receive()
|
||||
while msg is not 42:
|
||||
# we get symlink
|
||||
_type, relpath, linkpoint = msg
|
||||
assert _type == "link"
|
||||
path = os.path.join(destdir, relpath)
|
||||
try:
|
||||
os.unlink(path)
|
||||
except OSError:
|
||||
pass
|
||||
|
||||
os.symlink(os.path.join(destdir, linkpoint), path)
|
||||
msg = channel.receive()
|
||||
channel.send(("done", None))
|
||||
|
||||
113
py/test/rsession/slave.py
Normal file
113
py/test/rsession/slave.py
Normal file
@@ -0,0 +1,113 @@
|
||||
"""
|
||||
Node code for slaves.
|
||||
"""
|
||||
|
||||
import py
|
||||
from py.__.test.rsession.executor import RunExecutor, BoxExecutor, AsyncExecutor
|
||||
from py.__.test.rsession.outcome import Outcome
|
||||
|
||||
class Info:
|
||||
pid = None
|
||||
|
||||
class SlaveNode(object):
|
||||
def __init__(self, rootcollector, executor=AsyncExecutor):
|
||||
self.rootcollector = rootcollector
|
||||
self.executor = executor
|
||||
|
||||
def execute(self, itemspec):
|
||||
item = self.rootcollector.getitembynames(itemspec)
|
||||
#if isinstance(item, py.test.Function):
|
||||
# ex = Executor(item.obj, setup=item.setup)
|
||||
#else:
|
||||
ex = self.executor(item)
|
||||
if self.executor is AsyncExecutor:
|
||||
cont, pid = ex.execute()
|
||||
else:
|
||||
# for tests only
|
||||
return ex.execute()
|
||||
Info.pid = pid
|
||||
return cont()
|
||||
|
||||
def run(self, itemspec):
|
||||
#outcome = self.execute(itemspec)
|
||||
#return outcome.make_repr()
|
||||
outcome = self.execute(itemspec)
|
||||
if self.executor.wraps:
|
||||
return outcome
|
||||
else:
|
||||
return outcome.make_repr()
|
||||
|
||||
def slave_main(receive, send, path, info = None):
|
||||
import os
|
||||
assert os.path.exists(path)
|
||||
path = os.path.abspath(path)
|
||||
nodes = {}
|
||||
def getnode(item):
|
||||
node = nodes.get(item[0], None)
|
||||
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(col)
|
||||
return node
|
||||
while 1:
|
||||
nextitem = receive()
|
||||
if nextitem is None:
|
||||
break
|
||||
try:
|
||||
node = getnode(nextitem)
|
||||
res = node.run(nextitem[1:])
|
||||
except py.test.Item.Skipped, s:
|
||||
send(Outcome(skipped=str(s)).make_repr())
|
||||
except:
|
||||
excinfo = py.code.ExceptionInfo()
|
||||
send(Outcome(excinfo=excinfo, is_critical=True).make_repr())
|
||||
else:
|
||||
from py.__.test.rsession.rsession import remote_options
|
||||
if not res[0] and not res[3] and remote_options.exitfirst:
|
||||
# we're finished, but need to eat what we can
|
||||
send(res)
|
||||
break
|
||||
send(res)
|
||||
|
||||
while nextitem is not None:
|
||||
nextitem = receive()
|
||||
|
||||
def setup():
|
||||
def callback_gen(queue):
|
||||
from py.__.test.rsession.slave import Info
|
||||
def callback(item):
|
||||
if item == 42: # magic call-cleanup
|
||||
# XXX should kill a pid here
|
||||
if Info.pid:
|
||||
os.kill(Info.pid, 15)
|
||||
sys.exit(0)
|
||||
queue.put(item)
|
||||
return callback
|
||||
|
||||
import os, sys
|
||||
from Queue import Queue
|
||||
pkgdir = channel.receive() # path is ready
|
||||
options = channel.receive() # options stuff, should be dictionary
|
||||
basedir = os.path.dirname(pkgdir)
|
||||
pkgname = os.path.basename(pkgdir)
|
||||
# setup defaults...
|
||||
sys.path.insert(0, basedir)
|
||||
import py
|
||||
options['we_are_remote'] = True
|
||||
from py.__.test.rsession.rsession import RemoteOptions
|
||||
from py.__.test.rsession.slave import Info
|
||||
Info.pid = 0
|
||||
from py.__.test.rsession import rsession
|
||||
rsession.remote_options = RemoteOptions(options)
|
||||
# XXX the following assumes that py lib is there, a bit
|
||||
# much of an assumtion
|
||||
if not rsession.remote_options.nomagic:
|
||||
py.magic.invoke(assertion=1)
|
||||
mod = __import__(pkgname)
|
||||
assert py.path.local(mod.__file__).dirpath() == py.path.local(pkgdir)
|
||||
from py.__.test.rsession.slave import slave_main
|
||||
queue = Queue()
|
||||
channel.setcallback(callback_gen(queue))
|
||||
slave_main(queue.get, channel.send, basedir)
|
||||
if not rsession.remote_options.nomagic:
|
||||
py.magic.revoke(assertion=1)
|
||||
1
py/test/rsession/testing/__init__.py
Normal file
1
py/test/rsession/testing/__init__.py
Normal file
@@ -0,0 +1 @@
|
||||
#
|
||||
12
py/test/rsession/testing/example1.py
Normal file
12
py/test/rsession/testing/example1.py
Normal file
@@ -0,0 +1,12 @@
|
||||
|
||||
def f1():
|
||||
f2()
|
||||
|
||||
def f2():
|
||||
pass
|
||||
|
||||
def g1():
|
||||
g2()
|
||||
|
||||
def g2():
|
||||
raise ValueError()
|
||||
30
py/test/rsession/testing/example2.py
Normal file
30
py/test/rsession/testing/example2.py
Normal file
@@ -0,0 +1,30 @@
|
||||
|
||||
""" some example for running box stuff inside
|
||||
"""
|
||||
|
||||
import sys
|
||||
import py, os
|
||||
|
||||
def boxf1():
|
||||
print "some out"
|
||||
print >>sys.stderr, "some err"
|
||||
return 1
|
||||
|
||||
def boxf2():
|
||||
os.write(1, "someout")
|
||||
os.write(2, "someerr")
|
||||
return 2
|
||||
|
||||
def boxseg():
|
||||
os.kill(os.getpid(), 11)
|
||||
|
||||
def boxhuge():
|
||||
os.write(1, " " * 10000)
|
||||
os.write(2, " " * 10000)
|
||||
os.write(1, " " * 10000)
|
||||
|
||||
os.write(1, " " * 10000)
|
||||
os.write(2, " " * 10000)
|
||||
os.write(2, " " * 10000)
|
||||
os.write(1, " " * 10000)
|
||||
return 3
|
||||
94
py/test/rsession/testing/test_boxing.py
Normal file
94
py/test/rsession/testing/test_boxing.py
Normal file
@@ -0,0 +1,94 @@
|
||||
|
||||
""" test boxing functionality
|
||||
"""
|
||||
|
||||
import py, sys, os
|
||||
|
||||
if sys.platform == 'win32':
|
||||
py.test.skip("rsession is unsupported on Windows.")
|
||||
|
||||
from py.__.test.rsession.box import Box
|
||||
from py.__.test.rsession.testing import example2
|
||||
from py.__.test.rsession.conftest import option
|
||||
|
||||
def setup_module(mod):
|
||||
from py.__.test.rsession.rsession import remote_options
|
||||
remote_options['nice_level'] = 0
|
||||
|
||||
def test_basic_boxing():
|
||||
# XXX: because we do not have option transfer
|
||||
## if not hasattr(option, 'nocapture') or not option.nocapture:
|
||||
## py.test.skip("Interacts with pylib i/o skipping which is bad actually")
|
||||
b = Box(example2.boxf1)
|
||||
b.run()
|
||||
assert b.stdoutrepr == "some out\n"
|
||||
assert b.stderrrepr == "some err\n"
|
||||
assert b.exitstat == 0
|
||||
assert b.signal == 0
|
||||
assert b.retval == 1
|
||||
|
||||
def test_boxing_on_fds():
|
||||
b = Box(example2.boxf2)
|
||||
b.run()
|
||||
assert b.stdoutrepr == "someout"
|
||||
assert b.stderrrepr == "someerr"
|
||||
assert b.exitstat == 0
|
||||
assert b.signal == 0
|
||||
assert b.retval == 2
|
||||
|
||||
def test_boxing_signal():
|
||||
b = Box(example2.boxseg)
|
||||
b.run()
|
||||
assert b.signal == 11
|
||||
assert b.retval is None
|
||||
|
||||
def test_boxing_huge_data():
|
||||
b = Box(example2.boxhuge)
|
||||
b.run()
|
||||
assert b.stdoutrepr
|
||||
assert b.exitstat == 0
|
||||
assert b.signal == 0
|
||||
assert b.retval == 3
|
||||
|
||||
def test_box_seq():
|
||||
# we run many boxes with huge data, just one after another
|
||||
for i in xrange(100):
|
||||
b = Box(example2.boxhuge)
|
||||
b.run()
|
||||
assert b.stdoutrepr
|
||||
assert b.exitstat == 0
|
||||
assert b.signal == 0
|
||||
assert b.retval == 3
|
||||
|
||||
def test_box_in_a_box():
|
||||
def boxfun():
|
||||
b = Box(example2.boxf2)
|
||||
b.run()
|
||||
print b.stdoutrepr
|
||||
print >>sys.stderr, b.stderrrepr
|
||||
return b.retval
|
||||
|
||||
b = Box(boxfun)
|
||||
b.run()
|
||||
assert b.stdoutrepr == "someout\n"
|
||||
assert b.stderrrepr == "someerr\n"
|
||||
assert b.exitstat == 0
|
||||
assert b.signal == 0
|
||||
assert b.retval == 2
|
||||
|
||||
def test_box_killer():
|
||||
if option.skip_kill_test:
|
||||
py.test.skip('skipping kill test')
|
||||
class A:
|
||||
pass
|
||||
info = A()
|
||||
import time
|
||||
|
||||
def box_fun():
|
||||
time.sleep(10) # we don't want to last forever here
|
||||
|
||||
b = Box(box_fun)
|
||||
par, pid = b.run(continuation=True)
|
||||
os.kill(pid, 15)
|
||||
par(pid)
|
||||
assert b.signal == 15
|
||||
24
py/test/rsession/testing/test_config.py
Normal file
24
py/test/rsession/testing/test_config.py
Normal file
@@ -0,0 +1,24 @@
|
||||
|
||||
""" test session config options
|
||||
"""
|
||||
|
||||
import py
|
||||
from py.__.test.rsession.rsession import session_options, SessionOptions,\
|
||||
remote_options
|
||||
|
||||
def test_session_opts():
|
||||
tmpdir = py.test.ensuretemp("sessionopts")
|
||||
tmpdir.ensure("conftest.py").write("""class SessionOptions:
|
||||
max_tasks_per_node = 5
|
||||
nice_level = 10
|
||||
""")
|
||||
tmp2 = py.test.ensuretemp("xxx")
|
||||
args = [str(tmpdir)]
|
||||
config = py.test.config._reparse(args)
|
||||
session_options.bind_config(config)
|
||||
assert session_options.max_tasks_per_node == 5
|
||||
assert remote_options.nice_level == 10
|
||||
config = py.test.config._reparse([str(tmp2)])
|
||||
session_options.bind_config(config)
|
||||
assert session_options.max_tasks_per_node == \
|
||||
SessionOptions.defaults['max_tasks_per_node']
|
||||
123
py/test/rsession/testing/test_executor.py
Normal file
123
py/test/rsession/testing/test_executor.py
Normal file
@@ -0,0 +1,123 @@
|
||||
|
||||
import py
|
||||
import example1
|
||||
|
||||
from py.__.test.rsession.executor import RunExecutor, BoxExecutor,\
|
||||
AsyncExecutor
|
||||
from py.__.test.rsession.outcome import ReprOutcome
|
||||
from py.__.test.rsession.testing.test_slave import funcprint_spec, \
|
||||
funcprintfail_spec
|
||||
|
||||
def setup_module(mod):
|
||||
mod.rootdir = py.path.local(py.__file__).dirpath().dirpath()
|
||||
from py.__.test.rsession.rsession import remote_options
|
||||
remote_options['nice_level'] = 0
|
||||
|
||||
def XXXtest_executor_passing_function():
|
||||
ex = Executor(example1.f1)
|
||||
outcome = ex.execute()
|
||||
assert outcome.passed
|
||||
|
||||
def XXXtest_executor_raising_function():
|
||||
ex = Executor(example1.g1)
|
||||
outcome = ex.execute()
|
||||
assert not outcome.passed
|
||||
excinfo = outcome.excinfo
|
||||
assert excinfo.type == ValueError
|
||||
|
||||
def XXXtest_executor_traceback():
|
||||
ex = Executor(example1.g1)
|
||||
outcome = ex.execute()
|
||||
excinfo = outcome.excinfo
|
||||
assert len(excinfo.traceback) == 2
|
||||
assert excinfo.traceback[1].frame.code.name == 'g2'
|
||||
assert excinfo.traceback[0].frame.code.name == 'g1'
|
||||
|
||||
def XXX_test_executor_setup_passing():
|
||||
ex = Executor(example1.f1, setup=lambda: None)
|
||||
outcome = ex.execute()
|
||||
assert outcome.passed
|
||||
assert not outcome.setupfailure
|
||||
|
||||
def XXX_test_executor_setup_failing():
|
||||
def failingsetup():
|
||||
raise ValueError
|
||||
ex = Executor(example1.f1, setup=failingsetup)
|
||||
outcome = ex.execute()
|
||||
assert not outcome.passed
|
||||
assert outcome.setupfailure
|
||||
assert outcome.excinfo.traceback[-1].frame.code.name == 'failingsetup'
|
||||
|
||||
class ItemTestPassing(py.test.Item):
|
||||
def run(self):
|
||||
return None
|
||||
|
||||
class ItemTestFailing(py.test.Item):
|
||||
def run(self):
|
||||
assert 0 == 1
|
||||
|
||||
class ItemTestSkipping(py.test.Item):
|
||||
def run(self):
|
||||
py.test.skip("hello")
|
||||
|
||||
def test_run_executor():
|
||||
ex = RunExecutor(ItemTestPassing("pass"))
|
||||
outcome = ex.execute()
|
||||
assert outcome.passed
|
||||
|
||||
ex = RunExecutor(ItemTestFailing("fail"))
|
||||
outcome = ex.execute()
|
||||
assert not outcome.passed
|
||||
|
||||
ex = RunExecutor(ItemTestSkipping("skip"))
|
||||
outcome = ex.execute()
|
||||
assert outcome.skipped
|
||||
assert not outcome.passed
|
||||
assert not outcome.excinfo
|
||||
|
||||
def test_box_executor():
|
||||
ex = BoxExecutor(ItemTestPassing("pass"))
|
||||
outcome_repr = ex.execute()
|
||||
outcome = ReprOutcome(outcome_repr)
|
||||
assert outcome.passed
|
||||
|
||||
ex = BoxExecutor(ItemTestFailing("fail"))
|
||||
outcome_repr = ex.execute()
|
||||
outcome = ReprOutcome(outcome_repr)
|
||||
assert not outcome.passed
|
||||
|
||||
ex = BoxExecutor(ItemTestSkipping("skip"))
|
||||
outcome_repr = ex.execute()
|
||||
outcome = ReprOutcome(outcome_repr)
|
||||
assert outcome.skipped
|
||||
assert not outcome.passed
|
||||
assert not outcome.excinfo
|
||||
|
||||
def test_box_executor_stdout():
|
||||
rootcol = py.test.collect.Directory(rootdir)
|
||||
item = rootcol.getitembynames(funcprint_spec)
|
||||
ex = BoxExecutor(item)
|
||||
outcome_repr = ex.execute()
|
||||
outcome = ReprOutcome(outcome_repr)
|
||||
assert outcome.passed
|
||||
assert outcome.stdout.find("samfing") != -1
|
||||
|
||||
def test_box_executor_stdout_error():
|
||||
rootcol = py.test.collect.Directory(rootdir)
|
||||
item = rootcol.getitembynames(funcprintfail_spec)
|
||||
ex = BoxExecutor(item)
|
||||
outcome_repr = ex.execute()
|
||||
outcome = ReprOutcome(outcome_repr)
|
||||
assert not outcome.passed
|
||||
assert outcome.stdout.find("samfing elz") != -1
|
||||
|
||||
def test_cont_executor():
|
||||
rootcol = py.test.collect.Directory(rootdir)
|
||||
item = rootcol.getitembynames(funcprintfail_spec)
|
||||
ex = AsyncExecutor(item)
|
||||
cont, pid = ex.execute()
|
||||
assert pid
|
||||
outcome_repr = cont()
|
||||
outcome = ReprOutcome(outcome_repr)
|
||||
assert not outcome.passed
|
||||
assert outcome.stdout.find("samfing elz") != -1
|
||||
264
py/test/rsession/testing/test_lsession.py
Normal file
264
py/test/rsession/testing/test_lsession.py
Normal file
@@ -0,0 +1,264 @@
|
||||
|
||||
""" test of local version of py.test distributed
|
||||
"""
|
||||
|
||||
import py
|
||||
from py.__.test.rsession.rsession import LSession
|
||||
from py.__.test.rsession import report
|
||||
from py.__.test.rsession.local import box_runner, plain_runner
|
||||
|
||||
def setup_module(mod):
|
||||
mod.tmp = py.test.ensuretemp("lsession_module")
|
||||
|
||||
class TestLSession(object):
|
||||
# XXX: Some tests of that should be run as well on RSession, while
|
||||
# some not at all
|
||||
def example_distribution(self, runner):
|
||||
# XXX find a better way for the below
|
||||
tmpdir = tmp
|
||||
dirname = "sub_lsession"+runner.func_name
|
||||
tmpdir.ensure(dirname, "__init__.py")
|
||||
tmpdir.ensure(dirname, "test_one.py").write(py.code.Source("""
|
||||
def test_1():
|
||||
pass
|
||||
def test_2():
|
||||
assert 0
|
||||
def test_3():
|
||||
raise ValueError(23)
|
||||
def test_4(someargs):
|
||||
pass
|
||||
#def test_5():
|
||||
# import os
|
||||
# os.kill(os.getpid(), 11)
|
||||
"""))
|
||||
args = [str(tmpdir.join(dirname))]
|
||||
config = py.test.config._reparse(args)
|
||||
lsession = LSession(config)
|
||||
allevents = []
|
||||
lsession.main(reporter=allevents.append, runner=runner)
|
||||
testevents = [x for x in allevents
|
||||
if isinstance(x, report.ReceivedItemOutcome)]
|
||||
assert len(testevents)
|
||||
passevents = [i for i in testevents if i.outcome.passed]
|
||||
failevents = [i for i in testevents if i.outcome.excinfo]
|
||||
skippedevents = [i for i in testevents if i.outcome.skipped]
|
||||
signalevents = [i for i in testevents if i.outcome.signal]
|
||||
assert len(passevents) == 1
|
||||
assert len(failevents) == 3
|
||||
assert len(skippedevents) == 0
|
||||
#assert len(signalevents) == 1
|
||||
tb = failevents[0].outcome.excinfo.traceback
|
||||
assert str(tb[0].path).find("test_one") != -1
|
||||
assert str(tb[0].source).find("test_2") != -1
|
||||
assert failevents[0].outcome.excinfo.typename == 'AssertionError'
|
||||
tb = failevents[1].outcome.excinfo.traceback
|
||||
assert str(tb[0].path).find("test_one") != -1
|
||||
assert str(tb[0].source).find("test_3") != -1
|
||||
assert failevents[1].outcome.excinfo.typename == 'ValueError'
|
||||
assert str(failevents[1].outcome.excinfo.value) == '23'
|
||||
tb = failevents[2].outcome.excinfo.traceback
|
||||
assert failevents[2].outcome.excinfo.typename == 'TypeError'
|
||||
assert str(tb[0].path).find("executor") != -1
|
||||
assert str(tb[0].source).find("execute") != -1
|
||||
|
||||
def test_normal(self):
|
||||
if not hasattr(py.std.os, 'fork'):
|
||||
py.test.skip('operating system not supported')
|
||||
self.example_distribution(box_runner)
|
||||
|
||||
def test_plain(self):
|
||||
self.example_distribution(plain_runner)
|
||||
|
||||
def test_pdb_run(self):
|
||||
# we make sure that pdb is engaged
|
||||
tmpdir = tmp
|
||||
subdir = "sub_pdb_run"
|
||||
tmpdir.ensure(subdir, "__init__.py")
|
||||
tmpdir.ensure(subdir, "test_one.py").write(py.code.Source("""
|
||||
def test_1():
|
||||
assert 0
|
||||
"""))
|
||||
import pdb
|
||||
l = []
|
||||
def some_fun(*args):
|
||||
l.append(args)
|
||||
|
||||
pdb.post_mortem = some_fun
|
||||
args = [str(tmpdir.join(subdir)), '--pdb']
|
||||
config = py.test.config._reparse(args)
|
||||
lsession = LSession(config)
|
||||
allevents = []
|
||||
try:
|
||||
lsession.main(reporter=allevents.append, runner=plain_runner)
|
||||
except SystemExit:
|
||||
pass
|
||||
else:
|
||||
py.test.fail("Didn't raise system exit")
|
||||
failure_events = [event for event in allevents if isinstance(event,
|
||||
report.ImmediateFailure)]
|
||||
assert len(failure_events) == 1
|
||||
assert len(l) == 1
|
||||
|
||||
def test_minus_x(self):
|
||||
if not hasattr(py.std.os, 'fork'):
|
||||
py.test.skip('operating system not supported')
|
||||
tmpdir = tmp
|
||||
subdir = "sub_lsession_minus_x"
|
||||
tmpdir.ensure(subdir, "__init__.py")
|
||||
tmpdir.ensure(subdir, "test_one.py").write(py.code.Source("""
|
||||
def test_1():
|
||||
pass
|
||||
def test_2():
|
||||
assert 0
|
||||
def test_3():
|
||||
raise ValueError(23)
|
||||
def test_4(someargs):
|
||||
pass
|
||||
"""))
|
||||
args = [str(tmpdir.join(subdir)), '-x']
|
||||
config = py.test.config._reparse(args)
|
||||
assert config.option.exitfirst
|
||||
lsession = LSession(config)
|
||||
allevents = []
|
||||
|
||||
lsession.main(reporter=allevents.append, runner=box_runner)
|
||||
testevents = [x for x in allevents
|
||||
if isinstance(x, report.ReceivedItemOutcome)]
|
||||
assert len(testevents)
|
||||
passevents = [i for i in testevents if i.outcome.passed]
|
||||
failevents = [i for i in testevents if i.outcome.excinfo]
|
||||
assert len(passevents) == 1
|
||||
assert len(failevents) == 1
|
||||
|
||||
def test_minus_k(self):
|
||||
if not hasattr(py.std.os, 'fork'):
|
||||
py.test.skip('operating system not supported')
|
||||
tmpdir = tmp
|
||||
tmpdir.ensure("sub3", "__init__.py")
|
||||
tmpdir.ensure("sub3", "test_some.py").write(py.code.Source("""
|
||||
def test_one():
|
||||
pass
|
||||
def test_one_one():
|
||||
assert 0
|
||||
def test_other():
|
||||
raise ValueError(23)
|
||||
def test_two(someargs):
|
||||
pass
|
||||
"""))
|
||||
args = [str(tmpdir.join("sub3")), '-k', 'test_one']
|
||||
config = py.test.config._reparse(args)
|
||||
lsession = LSession(config)
|
||||
allevents = []
|
||||
|
||||
lsession.main(reporter=allevents.append, runner=box_runner)
|
||||
testevents = [x for x in allevents
|
||||
if isinstance(x, report.ReceivedItemOutcome)]
|
||||
assert len(testevents)
|
||||
passevents = [i for i in testevents if i.outcome.passed]
|
||||
failevents = [i for i in testevents if i.outcome.excinfo]
|
||||
assert len(passevents) == 1
|
||||
assert len(failevents) == 1
|
||||
|
||||
def test_lsession(self):
|
||||
tmpdir = tmp
|
||||
tmpdir.ensure("sub4", "__init__.py")
|
||||
tmpdir.ensure("sub4", "test_some.py").write(py.code.Source("""
|
||||
def test_one():
|
||||
pass
|
||||
def test_one_one():
|
||||
assert 0
|
||||
def test_other():
|
||||
raise ValueError(23)
|
||||
def test_two(someargs):
|
||||
pass
|
||||
"""))
|
||||
|
||||
args = [str(tmpdir.join("sub4"))]
|
||||
config = py.test.config._reparse(args)
|
||||
lsession = LSession(config)
|
||||
allevents = []
|
||||
allruns = []
|
||||
def dummy_runner(item, config, reporter):
|
||||
allruns.append(item)
|
||||
item.passed = True
|
||||
return item
|
||||
lsession.main(reporter=allevents.append, runner=dummy_runner)
|
||||
|
||||
assert len(allruns) == 4
|
||||
testevents = [x for x in allevents
|
||||
if isinstance(x, report.ReceivedItemOutcome)]
|
||||
assert len(testevents) == 4
|
||||
lst = ['test_one', 'test_one_one', 'test_other', 'test_two']
|
||||
for num, i in enumerate(testevents):
|
||||
assert i.item == i.outcome
|
||||
assert i.item.name == lst[num]
|
||||
|
||||
def test_module_raising(self):
|
||||
tmpdir = tmp
|
||||
tmpdir.ensure("sub5", "__init__.py")
|
||||
tmpdir.ensure("sub5", "test_some.py").write(py.code.Source("""
|
||||
1/0
|
||||
"""))
|
||||
tmpdir.ensure("sub5", "test_other.py").write(py.code.Source("""
|
||||
import py
|
||||
py.test.skip("reason")
|
||||
"""))
|
||||
|
||||
args = [str(tmpdir.join("sub5"))]
|
||||
config = py.test.config._reparse(args)
|
||||
lsession = LSession(config)
|
||||
allevents = []
|
||||
lsession.main(reporter=allevents.append, runner=box_runner)
|
||||
testevents = [x for x in allevents
|
||||
if isinstance(x, report.ReceivedItemOutcome)]
|
||||
assert len(testevents) == 0
|
||||
failedtryiter = [x for x in allevents
|
||||
if isinstance(x, report.FailedTryiter)]
|
||||
assert len(failedtryiter) == 1
|
||||
skippedtryiter = [x for x in allevents
|
||||
if isinstance(x, report.SkippedTryiter)]
|
||||
assert len(skippedtryiter) == 1
|
||||
|
||||
|
||||
def test_assert_reinterpret(self):
|
||||
if not hasattr(py.std.os, 'fork'):
|
||||
py.test.skip('operating system not supported')
|
||||
tmpdir = tmp
|
||||
tmpdir.ensure("sub6", "__init__.py")
|
||||
tmpdir.ensure("sub6", "test_some.py").write(py.code.Source("""
|
||||
def test_one():
|
||||
x = [1, 2]
|
||||
assert [0] == x
|
||||
"""))
|
||||
args = [str(tmpdir.join("sub6"))]
|
||||
config = py.test.config._reparse(args)
|
||||
lsession = LSession(config)
|
||||
allevents = []
|
||||
lsession.main(reporter=allevents.append, runner=box_runner)
|
||||
testevents = [x for x in allevents
|
||||
if isinstance(x, report.ReceivedItemOutcome)]
|
||||
failevents = [i for i in testevents if i.outcome.excinfo]
|
||||
assert len(failevents) == 1
|
||||
assert len(testevents) == 1
|
||||
assert failevents[0].outcome.excinfo.value == 'assert [0] == [1, 2]'
|
||||
|
||||
def test_nocapture(self):
|
||||
tmpdir = tmp
|
||||
tmpdir.ensure("sub7", "__init__.py")
|
||||
tmpdir.ensure("sub7", "test_nocap.py").write(py.code.Source("""
|
||||
def test_one():
|
||||
print 1
|
||||
print 2
|
||||
print 3
|
||||
"""))
|
||||
args = [str(tmpdir.join("sub7"))]
|
||||
config = py.test.config._reparse(args)
|
||||
lsession = LSession(config)
|
||||
allevents = []
|
||||
lsession.main(reporter=allevents.append, runner=plain_runner)
|
||||
testevents = [x for x in allevents
|
||||
if isinstance(x, report.ReceivedItemOutcome)]
|
||||
assert len(testevents) == 1
|
||||
assert testevents[0].outcome.passed
|
||||
assert testevents[0].outcome.stderr == ""
|
||||
assert testevents[0].outcome.stdout == "1\n2\n3\n"
|
||||
141
py/test/rsession/testing/test_master.py
Normal file
141
py/test/rsession/testing/test_master.py
Normal file
@@ -0,0 +1,141 @@
|
||||
""" master code and test dispatching for
|
||||
making 1-n master -> slave connection
|
||||
and test it locally.
|
||||
"""
|
||||
|
||||
import time, threading
|
||||
import py, sys
|
||||
|
||||
if sys.platform == 'win32':
|
||||
py.test.skip("rsession is unsupported on Windows.")
|
||||
|
||||
from py.__.test.rsession.master import dispatch_loop, setup_slave, MasterNode, randomgen
|
||||
from py.__.test.rsession.outcome import ReprOutcome, Outcome
|
||||
from py.__.test.rsession.testing.test_slave import funcpass_spec, funcfail_spec
|
||||
from py.__.test.rsession import report
|
||||
from py.__.test.rsession.rsession import session_options, remote_options
|
||||
from py.__.test.rsession.hostmanage import HostInfo
|
||||
|
||||
def setup_module(mod):
|
||||
# bind an empty config
|
||||
config = py.test.config._reparse([])
|
||||
config.option.max_tasks_per_node = 10
|
||||
session_options.bind_config(config)
|
||||
#assert not remote_options.exitfirst
|
||||
mod.pkgdir = py.path.local(py.__file__).dirpath()
|
||||
|
||||
class DummyGateway(object):
|
||||
def __init__(self):
|
||||
self.host = HostInfo("localhost")
|
||||
|
||||
class DummyChannel(object):
|
||||
def __init__(self):
|
||||
self.sent = []
|
||||
self.gateway = DummyGateway()
|
||||
|
||||
def setcallback(self, func):
|
||||
self.callback = func
|
||||
|
||||
def send(self, item):
|
||||
assert py.std.marshal.dumps(item)
|
||||
self.sent.append(item)
|
||||
|
||||
def test_masternode():
|
||||
try:
|
||||
raise ValueError()
|
||||
except ValueError:
|
||||
excinfo = py.code.ExceptionInfo()
|
||||
|
||||
ch = DummyChannel()
|
||||
reportlist = []
|
||||
mnode = MasterNode(ch, reportlist.append, {})
|
||||
mnode.send(py.test.Item("ok"))
|
||||
mnode.send(py.test.Item("notok"))
|
||||
ch.callback(Outcome().make_repr())
|
||||
ch.callback(Outcome(excinfo=excinfo).make_repr())
|
||||
assert len(reportlist) == 4
|
||||
received = [i for i in reportlist
|
||||
if isinstance(i, report.ReceivedItemOutcome)]
|
||||
assert received[0].outcome.passed
|
||||
assert not received[1].outcome.passed
|
||||
|
||||
def test_unique_nodes():
|
||||
ch = DummyChannel()
|
||||
reportlist = []
|
||||
mnode = MasterNode(ch, reportlist.append, {})
|
||||
mnode.send(py.test.Item("ok"))
|
||||
mnode.send(py.test.Item("ok"))
|
||||
ch.callback(Outcome().make_repr())
|
||||
ch.callback(Outcome().make_repr())
|
||||
assert len(reportlist) == 3
|
||||
|
||||
def test_outcome_repr():
|
||||
out = ReprOutcome(Outcome(skipped=True).make_repr())
|
||||
s = repr(out)
|
||||
assert s.lower().find("skip") != -1
|
||||
|
||||
class DummyMasterNode(object):
|
||||
def __init__(self):
|
||||
self.pending = []
|
||||
|
||||
def send(self, data):
|
||||
self.pending.append(data)
|
||||
|
||||
def test_dispatch_loop():
|
||||
masternodes = [DummyMasterNode(), DummyMasterNode()]
|
||||
itemgenerator = iter(range(100))
|
||||
shouldstop = lambda : False
|
||||
def waiter():
|
||||
for node in masternodes:
|
||||
node.pending.pop()
|
||||
dispatch_loop(masternodes, itemgenerator, shouldstop, waiter=waiter)
|
||||
|
||||
def test_slave_setup():
|
||||
gw = py.execnet.PopenGateway()
|
||||
channel = setup_slave(gw, pkgdir, remote_options.d)
|
||||
channel.send(funcpass_spec)
|
||||
output = ReprOutcome(channel.receive())
|
||||
assert output.passed
|
||||
channel.send(None)
|
||||
channel.waitclose(10)
|
||||
gw.exit()
|
||||
|
||||
def test_slave_running():
|
||||
def simple_report(event):
|
||||
if not isinstance(event, report.ReceivedItemOutcome):
|
||||
return
|
||||
item = event.item
|
||||
if item.code.name == 'funcpass':
|
||||
assert event.outcome.passed
|
||||
else:
|
||||
assert not event.outcome.passed
|
||||
|
||||
def open_gw():
|
||||
gw = py.execnet.PopenGateway()
|
||||
gw.host = HostInfo("localhost")
|
||||
gw.host.gw = gw
|
||||
channel = setup_slave(gw, pkgdir, remote_options.d)
|
||||
mn = MasterNode(channel, simple_report, {})
|
||||
return mn
|
||||
|
||||
master_nodes = [open_gw(), open_gw(), open_gw()]
|
||||
rootcol = py.test.collect.Directory(pkgdir.dirpath())
|
||||
funcpass_item = rootcol.getitembynames(funcpass_spec)
|
||||
funcfail_item = rootcol.getitembynames(funcfail_spec)
|
||||
itemgenerator = iter([funcfail_item] +
|
||||
[funcpass_item] * 5 + [funcfail_item] * 5)
|
||||
shouldstop = lambda : False
|
||||
dispatch_loop(master_nodes, itemgenerator, shouldstop)
|
||||
|
||||
def test_randomgen():
|
||||
d = {}
|
||||
gen = randomgen({1:True, 2:True, 3:True}, d)
|
||||
for i in range(100):
|
||||
assert gen.next() in [1,2,3]
|
||||
d[3] = True
|
||||
for i in range(100):
|
||||
assert gen.next() in [1,2]
|
||||
d[2] = True
|
||||
d[1] = True
|
||||
py.test.raises(StopIteration, "gen.next()")
|
||||
|
||||
50
py/test/rsession/testing/test_outcome.py
Normal file
50
py/test/rsession/testing/test_outcome.py
Normal file
@@ -0,0 +1,50 @@
|
||||
|
||||
import py
|
||||
from py.__.test.rsession.outcome import Outcome, ReprOutcome, ExcInfoRepr
|
||||
|
||||
import marshal
|
||||
|
||||
def test_critical_debugging_flag():
|
||||
outcome = Outcome(is_critical=True)
|
||||
r = ReprOutcome(outcome.make_repr())
|
||||
assert r.is_critical
|
||||
|
||||
def f1():
|
||||
1
|
||||
2
|
||||
3
|
||||
4
|
||||
raise ValueError(42)
|
||||
|
||||
def f2():
|
||||
f1()
|
||||
|
||||
def f3():
|
||||
f2()
|
||||
|
||||
def test_exception_info_repr():
|
||||
try:
|
||||
f3()
|
||||
except:
|
||||
outcome = Outcome(excinfo=py.code.ExceptionInfo())
|
||||
|
||||
repr = outcome.make_excinfo_repr()
|
||||
assert marshal.dumps(repr)
|
||||
excinfo = ExcInfoRepr(repr)
|
||||
|
||||
assert str(excinfo.typename) == "ValueError"
|
||||
assert str(excinfo.value) == "42"
|
||||
assert len(excinfo.traceback) == 4
|
||||
myfile = py.magic.autopath()
|
||||
assert excinfo.traceback[3].path == myfile
|
||||
assert excinfo.traceback[3].lineno == f1.func_code.co_firstlineno + 4
|
||||
assert excinfo.traceback[3].relline == 5
|
||||
assert excinfo.traceback[2].path == myfile
|
||||
assert excinfo.traceback[2].lineno == f2.func_code.co_firstlineno
|
||||
assert excinfo.traceback[2].relline == 1
|
||||
assert excinfo.traceback[1].path == myfile
|
||||
assert excinfo.traceback[1].lineno == f3.func_code.co_firstlineno
|
||||
assert excinfo.traceback[1].relline == 1
|
||||
|
||||
#def test_f3():
|
||||
# f3()
|
||||
36
py/test/rsession/testing/test_report.py
Normal file
36
py/test/rsession/testing/test_report.py
Normal file
@@ -0,0 +1,36 @@
|
||||
""" test reporting functionality. """
|
||||
|
||||
import py
|
||||
from py.__.test.rsession import report
|
||||
|
||||
def test_wrapcall_ok():
|
||||
l = []
|
||||
def ok(x):
|
||||
return x+1
|
||||
i = report.wrapcall(l.append, ok, 1)
|
||||
assert i == 2
|
||||
assert len(l) == 2
|
||||
assert isinstance(l[0], report.CallStart)
|
||||
assert isinstance(l[1], report.CallFinish)
|
||||
assert repr(l[0])
|
||||
assert repr(l[1])
|
||||
|
||||
def test_wrapcall_exception():
|
||||
l = []
|
||||
def fail(x):
|
||||
raise ValueError
|
||||
py.test.raises(ValueError, "report.wrapcall(l.append, fail, 1)")
|
||||
assert len(l) == 2
|
||||
assert isinstance(l[0], report.CallStart)
|
||||
assert isinstance(l[1], report.CallException)
|
||||
|
||||
def test_reporter_methods_sanity():
|
||||
""" Checks if all the methods of reporter are sane
|
||||
"""
|
||||
from py.__.test.rsession.rsession import RemoteReporter
|
||||
from py.__.test.rsession import report
|
||||
|
||||
for method in dir(RemoteReporter):
|
||||
|
||||
if method.startswith("report_") and method != "report_unknown":
|
||||
assert method[len('report_'):] in report.__dict__
|
||||
226
py/test/rsession/testing/test_reporter.py
Normal file
226
py/test/rsession/testing/test_reporter.py
Normal file
@@ -0,0 +1,226 @@
|
||||
|
||||
""" reporter tests.
|
||||
|
||||
XXX there are a few disabled reporting tests because
|
||||
they test for exact formatting as far as i can see.
|
||||
I think it's rather better to directly invoke a
|
||||
reporter and pass it some hand-prepared events to see
|
||||
that running the reporter doesn't break shallowly.
|
||||
|
||||
Otherwise, i suppose that some "visual" testing can usually be driven
|
||||
manually by user-input. And when passing particular events
|
||||
to a reporter it's also easier to check for one line
|
||||
instead of having to know the order in which things are printed
|
||||
etc.
|
||||
|
||||
|
||||
"""
|
||||
|
||||
|
||||
import py, os
|
||||
#py.test.skip("in progress")
|
||||
from py.__.test.rsession.rsession import LocalReporter, AbstractSession,\
|
||||
RemoteReporter
|
||||
from py.__.test.rsession import report
|
||||
from py.__.test.rsession.outcome import ReprOutcome, Outcome
|
||||
from py.__.test.rsession.testing.test_slave import funcpass_spec, mod_spec
|
||||
from py.__.test.rsession.hostmanage import HostInfo
|
||||
from py.__.test.rsession.box import Box
|
||||
#from py.__.test.
|
||||
import sys
|
||||
from StringIO import StringIO
|
||||
|
||||
class DummyGateway(object):
|
||||
def __init__(self, host):
|
||||
self.host = host
|
||||
|
||||
class DummyChannel(object):
|
||||
def __init__(self, host):
|
||||
self.gateway = DummyGateway(host)
|
||||
|
||||
class AbstractTestReporter(object):
|
||||
def setup_class(cls):
|
||||
cls.pkgdir = py.path.local(py.__file__).dirpath()
|
||||
|
||||
def prepare_outcomes(self):
|
||||
# possible outcomes
|
||||
try:
|
||||
1/0
|
||||
except:
|
||||
exc = py.code.ExceptionInfo()
|
||||
|
||||
outcomes = [Outcome(()),
|
||||
Outcome(skipped=True),
|
||||
Outcome(excinfo=exc),
|
||||
Outcome()]
|
||||
|
||||
outcomes = [ReprOutcome(outcome.make_repr()) for outcome in outcomes]
|
||||
outcomes[3].signal = 11
|
||||
outcomes[0].passed = False
|
||||
|
||||
return outcomes
|
||||
|
||||
def report_received_item_outcome(self):
|
||||
config = py.test.config._reparse(["some_sub"])
|
||||
# we just go...
|
||||
rootcol = py.test.collect.Directory(self.pkgdir.dirpath())
|
||||
item = rootcol.getitembynames(funcpass_spec)
|
||||
outcomes = self.prepare_outcomes()
|
||||
|
||||
def boxfun(config, item, outcomes):
|
||||
hosts = [HostInfo("localhost")]
|
||||
r = self.reporter(config, hosts)
|
||||
ch = DummyChannel(hosts[0])
|
||||
for outcome in outcomes:
|
||||
r.report(report.ReceivedItemOutcome(ch, item, outcome))
|
||||
|
||||
cap = py.io.OutErrCapture()
|
||||
boxfun(config, item, outcomes)
|
||||
out, err = cap.reset()
|
||||
assert not err
|
||||
return out
|
||||
|
||||
def _test_module(self):
|
||||
config = py.test.config._reparse(["some_sub"])
|
||||
# we just go...
|
||||
rootcol = py.test.collect.Directory(self.pkgdir.dirpath())
|
||||
funcitem = rootcol.getitembynames(funcpass_spec)
|
||||
moditem = rootcol.getitembynames(mod_spec)
|
||||
outcomes = self.prepare_outcomes()
|
||||
|
||||
def boxfun(pkgdir, config, item, funcitem, outcomes):
|
||||
hosts = [HostInfo('localhost')]
|
||||
r = self.reporter(config, hosts)
|
||||
#r.pkgdir = pkdgir
|
||||
r.report(report.ItemStart(item))
|
||||
ch = DummyChannel(hosts[0])
|
||||
for outcome in outcomes:
|
||||
r.report(report.ReceivedItemOutcome(ch, funcitem, outcome))
|
||||
|
||||
cap = py.io.OutErrCapture()
|
||||
boxfun(self.pkgdir, config, moditem, funcitem, outcomes)
|
||||
out, err = cap.reset()
|
||||
assert not err
|
||||
return out
|
||||
|
||||
def _test_full_module(self):
|
||||
tmpdir = py.test.ensuretemp("repmod")
|
||||
tmpdir.ensure("__init__.py")
|
||||
tmpdir.ensure("test_one.py").write(py.code.Source("""
|
||||
def test_x():
|
||||
pass
|
||||
"""))
|
||||
tmpdir.ensure("test_two.py").write(py.code.Source("""
|
||||
import py
|
||||
py.test.skip("reason")
|
||||
"""))
|
||||
tmpdir.ensure("test_three.py").write(py.code.Source("""
|
||||
sadsadsa
|
||||
"""))
|
||||
|
||||
def boxfun():
|
||||
config = py.test.config._reparse([str(tmpdir)])
|
||||
rootcol = py.test.collect.Directory(tmpdir)
|
||||
hosts = [HostInfo('localhost')]
|
||||
r = self.reporter(config, hosts)
|
||||
list(rootcol.tryiter(reporterror=lambda x : AbstractSession.reporterror(r.report, x)))
|
||||
|
||||
cap = py.io.OutErrCapture()
|
||||
boxfun()
|
||||
out, err = cap.reset()
|
||||
assert not err
|
||||
return out
|
||||
|
||||
def test_failed_to_load(self):
|
||||
tmpdir = py.test.ensuretemp("failedtoload")
|
||||
tmpdir.ensure("__init__.py")
|
||||
tmpdir.ensure("test_three.py").write(py.code.Source("""
|
||||
sadsadsa
|
||||
"""))
|
||||
def boxfun():
|
||||
config = py.test.config._reparse([str(tmpdir)])
|
||||
rootcol = py.test.collect.Directory(tmpdir)
|
||||
host = HostInfo('localhost')
|
||||
r = self.reporter(config, [host])
|
||||
r.report(report.TestStarted([host]))
|
||||
r.report(report.RsyncFinished())
|
||||
list(rootcol.tryiter(reporterror=lambda x : AbstractSession.reporterror(r.report, x)))
|
||||
r.report(report.TestFinished())
|
||||
|
||||
cap = py.io.OutErrCapture()
|
||||
boxfun()
|
||||
out, err = cap.reset()
|
||||
assert not err
|
||||
assert out.find("NameError: name 'sadsadsa' is not defined") != -1
|
||||
|
||||
def _test_still_to_go(self):
|
||||
tmpdir = py.test.ensuretemp("stilltogo")
|
||||
tmpdir.ensure("__init__.py")
|
||||
cap = py.io.OutErrCapture()
|
||||
config = py.test.config._reparse([str(tmpdir)])
|
||||
hosts = [HostInfo(i) for i in ["host1", "host2", "host3"]]
|
||||
r = self.reporter(config, hosts)
|
||||
r.report(report.TestStarted(hosts))
|
||||
r.report(report.HostReady(hosts[0]))
|
||||
r.report(report.HostReady(hosts[1]))
|
||||
r.report(report.HostReady(hosts[2]))
|
||||
out, err = cap.reset()
|
||||
assert not err
|
||||
expected = """================= Test started, hosts: host1, host2, host3 ==================
|
||||
host1: READY (still 2 to go)
|
||||
host2: READY (still 1 to go)
|
||||
host3: READY"""
|
||||
assert out.find(expected) != -1
|
||||
|
||||
class TestLocalReporter(AbstractTestReporter):
|
||||
reporter = LocalReporter
|
||||
|
||||
def test_report_received_item_outcome(self):
|
||||
#py.test.skip("XXX rewrite test to not rely on exact formatting")
|
||||
assert self.report_received_item_outcome() == 'FsF.'
|
||||
|
||||
def test_module(self):
|
||||
#py.test.skip("XXX rewrite test to not rely on exact formatting")
|
||||
assert self._test_module().endswith("test_slave.py[9] FsF."),\
|
||||
self._test_module()
|
||||
|
||||
def test_full_module(self):
|
||||
#py.test.skip("XXX rewrite test to not rely on exact formatting")
|
||||
received = self._test_full_module()
|
||||
expected = """
|
||||
repmod/test_one.py[1]
|
||||
repmod/test_three.py[0] - FAILED TO LOAD MODULE
|
||||
repmod/test_two.py[0] - skipped (reason)"""
|
||||
assert received.find(expected) != -1
|
||||
|
||||
class TestRemoteReporter(AbstractTestReporter):
|
||||
reporter = RemoteReporter
|
||||
|
||||
def test_still_to_go(self):
|
||||
self._test_still_to_go()
|
||||
|
||||
def test_report_received_item_outcome(self):
|
||||
#py.test.skip("XXX rewrite test to not rely on exact formatting")
|
||||
val = self.report_received_item_outcome()
|
||||
expected = """ localhost: FAILED py test rsession testing test_slave.py funcpass
|
||||
localhost: SKIPPED py test rsession testing test_slave.py funcpass
|
||||
localhost: FAILED py test rsession testing test_slave.py funcpass
|
||||
localhost: PASSED py test rsession testing test_slave.py funcpass
|
||||
"""
|
||||
assert val.find(expected) != -1
|
||||
|
||||
def test_module(self):
|
||||
val = self._test_module()
|
||||
print val
|
||||
expected = """ localhost: FAILED py test rsession testing test_slave.py funcpass
|
||||
localhost: SKIPPED py test rsession testing test_slave.py funcpass
|
||||
localhost: FAILED py test rsession testing test_slave.py funcpass
|
||||
localhost: PASSED py test rsession testing test_slave.py funcpass
|
||||
"""
|
||||
assert val.find(expected) != -1
|
||||
|
||||
def test_full_module(self):
|
||||
#py.test.skip("XXX rewrite test to not rely on exact formatting")
|
||||
val = self._test_full_module()
|
||||
assert val.find('FAILED TO LOAD MODULE: repmod/test_three.py\n'\
|
||||
'\nSkipped (reason) repmod/test_two.py') != -1
|
||||
303
py/test/rsession/testing/test_rest.py
Normal file
303
py/test/rsession/testing/test_rest.py
Normal file
@@ -0,0 +1,303 @@
|
||||
|
||||
""" tests of rest reporter backend
|
||||
"""
|
||||
|
||||
import py
|
||||
from py.__.test.rsession.testing.test_reporter import AbstractTestReporter,\
|
||||
DummyChannel
|
||||
from py.__.test.rsession import report
|
||||
from py.__.test.rsession.rest import RestReporter, NoLinkWriter
|
||||
from py.__.rest.rst import *
|
||||
from py.__.test.rsession.hostmanage import HostInfo
|
||||
|
||||
class RestTestReporter(RestReporter):
|
||||
def __init__(self, *args, **kwargs):
|
||||
if args:
|
||||
super(RestReporter, self).__init__(*args, **kwargs)
|
||||
|
||||
class Container(object):
|
||||
def __init__(self, **kwargs):
|
||||
self.__dict__.update(kwargs)
|
||||
|
||||
class FakeOutcome(Container, report.ReceivedItemOutcome):
|
||||
pass
|
||||
|
||||
class FakeTryiter(Container, report.SkippedTryiter):
|
||||
pass
|
||||
|
||||
class TestRestUnits(object):
|
||||
def setup_method(self, method):
|
||||
config = py.test.config._reparse(["some_sub"])
|
||||
config.option.verbose = False
|
||||
self.config = config
|
||||
hosts = [HostInfo('localhost')]
|
||||
method.im_func.func_globals['ch'] = DummyChannel(hosts[0])
|
||||
method.im_func.func_globals['reporter'] = r = RestReporter(config,
|
||||
hosts)
|
||||
method.im_func.func_globals['stdout'] = s = py.std.StringIO.StringIO()
|
||||
r.out = s # XXX will need to become a real reporter some time perhaps?
|
||||
r.linkwriter = NoLinkWriter()
|
||||
|
||||
def test_report_unknown(self):
|
||||
self.config.option.verbose = True
|
||||
reporter.report_unknown('foo')
|
||||
assert stdout.getvalue() == 'Unknown report\\: foo\n\n'
|
||||
self.config.option.verbose = False
|
||||
|
||||
def test_report_SendItem(self):
|
||||
item = Container(item='foo/bar.py', channel=ch)
|
||||
reporter.report_SendItem(item)
|
||||
assert stdout.getvalue() == ''
|
||||
stdout.seek(0)
|
||||
stdout.truncate()
|
||||
reporter.config.option.verbose = True
|
||||
reporter.report_SendItem(item)
|
||||
assert stdout.getvalue() == ('sending item foo/bar.py to '
|
||||
'localhost\n\n')
|
||||
|
||||
def test_report_HostRSyncing(self):
|
||||
item = Container(hostname='localhost', remoterootpath='/foo/bar')
|
||||
reporter.report_HostRSyncing(item)
|
||||
assert stdout.getvalue() == ('::\n\n localhost: RSYNC ==> '
|
||||
'/foo/bar\n\n')
|
||||
|
||||
def test_report_HostReady(self):
|
||||
item = Container(hostname='localhost')
|
||||
reporter.report_HostReady(item)
|
||||
assert stdout.getvalue() == '::\n\n localhost: READY\n\n'
|
||||
|
||||
def test_report_TestStarted(self):
|
||||
event = Container(hosts=['localhost', 'foo.com'], timestart=0)
|
||||
reporter.report_TestStarted(event)
|
||||
assert stdout.getvalue() == """\
|
||||
===========================================
|
||||
Running tests on hosts\: localhost, foo.com
|
||||
===========================================
|
||||
|
||||
"""
|
||||
|
||||
def test_report_ItemStart(self):
|
||||
class FakeModule(py.test.collect.Module):
|
||||
def __init__(self, parent):
|
||||
self.parent = parent
|
||||
self.fspath = py.path.local('.')
|
||||
def tryiter(self):
|
||||
return ['test_foo', 'test_bar']
|
||||
def listnames(self):
|
||||
return ['package', 'foo', 'bar.py']
|
||||
parent = Container(parent=None, fspath=py.path.local('.'))
|
||||
event = Container(item=FakeModule(parent))
|
||||
reporter.report_ItemStart(event)
|
||||
assert stdout.getvalue() == """\
|
||||
Testing module foo/bar.py (2 items)
|
||||
-----------------------------------
|
||||
|
||||
"""
|
||||
|
||||
def test_print_summary(self):
|
||||
reporter.timestart = 10
|
||||
reporter.timeend = 20
|
||||
reporter.timersync = 15
|
||||
reporter.print_summary(10, '', '')
|
||||
assert stdout.getvalue() == """\
|
||||
10 tests run in 10.00s (rsync\: 5.00)
|
||||
-------------------------------------
|
||||
|
||||
"""
|
||||
|
||||
def test_ReceivedItemOutcome_PASSED(self):
|
||||
outcome = Container(passed=True, excinfo=False)
|
||||
item = Container(listnames=lambda: ['', 'foo.py', 'bar', '()', 'baz'])
|
||||
event = Container(channel=ch, outcome=outcome, item=item)
|
||||
reporter.report_ReceivedItemOutcome(event)
|
||||
assert stdout.getvalue() == ('* localhost\\: **PASSED** '
|
||||
'foo.py/bar()/baz\n\n')
|
||||
|
||||
def test_ReceivedItemOutcome_SKIPPED(self):
|
||||
outcome = Container(passed=False, skipped=True, excinfo=False)
|
||||
item = Container(listnames=lambda: ['', 'foo.py', 'bar', '()', 'baz'])
|
||||
event = Container(channel=ch, outcome=outcome, item=item)
|
||||
reporter.report_ReceivedItemOutcome(event)
|
||||
assert stdout.getvalue() == ('* localhost\\: **SKIPPED** '
|
||||
'foo.py/bar()/baz\n\n')
|
||||
|
||||
def test_ReceivedItemOutcome_FAILED(self):
|
||||
outcome = Container(passed=False, skipped=False)
|
||||
item = Container(listnames=lambda: ['', 'foo.py', 'bar', '()', 'baz'])
|
||||
event = Container(channel=ch, outcome=outcome, item=item)
|
||||
reporter.report_ReceivedItemOutcome(event)
|
||||
assert stdout.getvalue() == """\
|
||||
* localhost\: **FAILED** `traceback0`_ foo.py/bar()/baz
|
||||
|
||||
"""
|
||||
|
||||
def test_skips(self):
|
||||
reporter.skips()
|
||||
assert stdout.getvalue() == ''
|
||||
reporter.skipped_tests_outcome = [
|
||||
FakeOutcome(outcome=Container(skipped='problem X'),
|
||||
item=Container(listnames=lambda: ['foo', 'bar.py'])),
|
||||
FakeTryiter(excinfo=Container(value='problem Y'),
|
||||
item=Container(listnames=lambda: ['foo', 'baz.py']))]
|
||||
reporter.skips()
|
||||
assert stdout.getvalue() == """\
|
||||
Reasons for skipped tests\:
|
||||
+++++++++++++++++++++++++++
|
||||
|
||||
* foo/bar.py\: problem X
|
||||
|
||||
* foo/baz.py\: problem Y
|
||||
|
||||
"""
|
||||
|
||||
def test_failures(self):
|
||||
parent = Container(parent=None, fspath=py.path.local('.'))
|
||||
reporter.failed_tests_outcome = [
|
||||
FakeOutcome(
|
||||
outcome=Container(
|
||||
signal=False,
|
||||
excinfo=Container(
|
||||
typename='FooError',
|
||||
value='A foo has occurred',
|
||||
traceback=[
|
||||
Container(
|
||||
path='foo/bar.py',
|
||||
lineno=1,
|
||||
relline=1,
|
||||
source='foo()',
|
||||
),
|
||||
Container(
|
||||
path='foo/baz.py',
|
||||
lineno=4,
|
||||
relline=1,
|
||||
source='raise FooError("A foo has occurred")',
|
||||
),
|
||||
]
|
||||
),
|
||||
stdout='',
|
||||
stderr='',
|
||||
),
|
||||
item=Container(
|
||||
listnames=lambda: ['package', 'foo', 'bar.py',
|
||||
'baz', '()'],
|
||||
parent=parent,
|
||||
fspath=py.path.local('.'),
|
||||
),
|
||||
channel=ch,
|
||||
),
|
||||
]
|
||||
reporter.config.option.tbstyle = 'no'
|
||||
reporter.failures()
|
||||
expected = """\
|
||||
Exceptions\:
|
||||
++++++++++++
|
||||
|
||||
foo/bar.py/baz() on localhost
|
||||
+++++++++++++++++++++++++++++
|
||||
|
||||
.. _`traceback0`:
|
||||
|
||||
|
||||
FooError
|
||||
++++++++
|
||||
|
||||
::
|
||||
|
||||
A foo has occurred
|
||||
|
||||
"""
|
||||
assert stdout.getvalue() == expected
|
||||
|
||||
reporter.config.option.tbstyle = 'short'
|
||||
stdout.seek(0)
|
||||
stdout.truncate()
|
||||
reporter.failures()
|
||||
expected = """\
|
||||
Exceptions\:
|
||||
++++++++++++
|
||||
|
||||
foo/bar.py/baz() on localhost
|
||||
+++++++++++++++++++++++++++++
|
||||
|
||||
.. _`traceback0`:
|
||||
|
||||
|
||||
::
|
||||
|
||||
foo/bar.py line 1
|
||||
foo()
|
||||
foo/baz.py line 4
|
||||
raise FooError("A foo has occurred")
|
||||
|
||||
FooError
|
||||
++++++++
|
||||
|
||||
::
|
||||
|
||||
A foo has occurred
|
||||
|
||||
"""
|
||||
assert stdout.getvalue() == expected
|
||||
|
||||
reporter.config.option.tbstyle = 'long'
|
||||
stdout.seek(0)
|
||||
stdout.truncate()
|
||||
reporter.failures()
|
||||
expected = """\
|
||||
Exceptions\:
|
||||
++++++++++++
|
||||
|
||||
foo/bar.py/baz() on localhost
|
||||
+++++++++++++++++++++++++++++
|
||||
|
||||
.. _`traceback0`:
|
||||
|
||||
|
||||
+++++++++++++++++
|
||||
foo/bar.py line 1
|
||||
+++++++++++++++++
|
||||
|
||||
::
|
||||
|
||||
foo()
|
||||
|
||||
+++++++++++++++++
|
||||
foo/baz.py line 4
|
||||
+++++++++++++++++
|
||||
|
||||
::
|
||||
|
||||
raise FooError("A foo has occurred")
|
||||
|
||||
FooError
|
||||
++++++++
|
||||
|
||||
::
|
||||
|
||||
A foo has occurred
|
||||
|
||||
"""
|
||||
assert stdout.getvalue() == expected
|
||||
|
||||
|
||||
class TestRestReporter(AbstractTestReporter):
|
||||
reporter = RestReporter
|
||||
|
||||
def test_failed_to_load(self):
|
||||
py.test.skip("Not implemented")
|
||||
|
||||
def test_report_received_item_outcome(self):
|
||||
val = self.report_received_item_outcome()
|
||||
expected = """\
|
||||
* localhost\: **FAILED** `traceback0`_\n py/test/rsession/testing/test\_slave.py/funcpass
|
||||
|
||||
* localhost\: **SKIPPED** py/test/rsession/testing/test\_slave.py/funcpass
|
||||
|
||||
* localhost\: **FAILED** `traceback1`_\n py/test/rsession/testing/test\_slave.py/funcpass
|
||||
|
||||
* localhost\: **PASSED** py/test/rsession/testing/test\_slave.py/funcpass
|
||||
|
||||
"""
|
||||
print val
|
||||
assert val == expected
|
||||
|
||||
312
py/test/rsession/testing/test_rsession.py
Normal file
312
py/test/rsession/testing/test_rsession.py
Normal file
@@ -0,0 +1,312 @@
|
||||
|
||||
""" Tests various aspects of rsession, like ssh hosts setup/teardown
|
||||
"""
|
||||
|
||||
import py
|
||||
from py.__.test.rsession import report
|
||||
from py.__.test.rsession.rsession import RSession, parse_directories,\
|
||||
session_options, remote_options, parse_directories
|
||||
from py.__.test.rsession.hostmanage import init_hosts, teardown_hosts,\
|
||||
HostInfo
|
||||
from py.__.test.rsession.testing.test_slave import funcfail_spec,\
|
||||
funcpass_spec, funcskip_spec, funcprint_spec, funcprintfail_spec, \
|
||||
funcoptioncustom_spec, funcoption_spec
|
||||
|
||||
def setup_module(mod):
|
||||
mod.pkgdir = py.path.local(py.__file__).dirpath()
|
||||
|
||||
def test_setup_non_existing_hosts():
|
||||
setup_events = []
|
||||
hosts = [HostInfo("alskdjalsdkjasldkajlsd")]
|
||||
cmd = "init_hosts(setup_events.append, hosts, pkgdir)"
|
||||
py.test.raises((py.process.cmdexec.Error, IOError, EOFError), cmd)
|
||||
#assert setup_events
|
||||
|
||||
def test_getpkdir():
|
||||
one = pkgdir.join("initpkg.py")
|
||||
two = pkgdir.join("path", "__init__.py")
|
||||
p1 = RSession.getpkgdir(one)
|
||||
p2 = RSession.getpkgdir(two)
|
||||
assert p1 == p2
|
||||
assert p1 == pkgdir
|
||||
|
||||
def test_getpkdir_no_inits():
|
||||
tmp = py.test.ensuretemp("getpkdir1")
|
||||
fn = tmp.ensure("hello.py")
|
||||
assert RSession.getpkgdir(fn) == fn
|
||||
|
||||
def test_make_colitems():
|
||||
one = pkgdir.join("initpkg.py")
|
||||
two = pkgdir.join("path", "__init__.py")
|
||||
|
||||
cols = RSession.make_colitems([one, two], baseon=pkgdir)
|
||||
assert len(cols) == 2
|
||||
col_one, col_two = cols
|
||||
assert col_one.listnames() == ["py", "initpkg.py"]
|
||||
assert col_two.listnames() == ["py", "path", "__init__.py"]
|
||||
|
||||
cols = RSession.make_colitems([one, two], baseon=pkgdir.dirpath())
|
||||
assert len(cols) == 2
|
||||
col_one, col_two = cols
|
||||
assert col_one.listnames() == [pkgdir.dirpath().basename,
|
||||
"py", "initpkg.py"]
|
||||
assert col_two.listnames() == [pkgdir.dirpath().basename,
|
||||
"py", "path", "__init__.py"]
|
||||
|
||||
def test_example_tryiter():
|
||||
events = []
|
||||
tmpdir = py.test.ensuretemp("tryitertest")
|
||||
tmpdir.ensure("a", "__init__.py")
|
||||
tmpdir.ensure("conftest.py").write(py.code.Source("""
|
||||
import py
|
||||
py.test.skip("Reason")
|
||||
"""))
|
||||
tmpdir.ensure("a", "test_empty.py").write(py.code.Source("""
|
||||
def test_empty():
|
||||
pass
|
||||
"""))
|
||||
rootcol = py.test.collect.Directory(tmpdir)
|
||||
data = list(rootcol.tryiter(reporterror=events.append))
|
||||
assert len(events) == 2
|
||||
assert str(events[1][0].value) == "Reason"
|
||||
|
||||
class TestRSessionRemote:
|
||||
def test_example_distribution_minus_x(self):
|
||||
tmpdir = py.test.ensuretemp("example_distribution_minus_x")
|
||||
tmpdir.ensure("sub", "conftest.py").write(py.code.Source("""
|
||||
disthosts = [%r]
|
||||
""" % ('localhost',)))
|
||||
tmpdir.ensure("sub", "__init__.py")
|
||||
tmpdir.ensure("sub", "test_one.py").write(py.code.Source("""
|
||||
def test_1():
|
||||
pass
|
||||
def test_x():
|
||||
import py
|
||||
py.test.skip("aaa")
|
||||
def test_2():
|
||||
assert 0
|
||||
def test_3():
|
||||
raise ValueError(23)
|
||||
def test_4(someargs):
|
||||
pass
|
||||
"""))
|
||||
args = [str(tmpdir.join("sub")), "-x"]
|
||||
config = py.test.config._reparse(args)
|
||||
rsession = RSession(config)
|
||||
allevents = []
|
||||
rsession.main(reporter=allevents.append)
|
||||
testevents = [x for x in allevents
|
||||
if isinstance(x, report.ReceivedItemOutcome)]
|
||||
assert len(testevents) == 3
|
||||
assert rsession.checkfun()
|
||||
|
||||
def test_example_distribution(self):
|
||||
subdir = "sub_example_dist"
|
||||
tmpdir = py.test.ensuretemp("example_distribution")
|
||||
tmpdir.ensure(subdir, "conftest.py").write(py.code.Source("""
|
||||
disthosts = [%r]
|
||||
distrsync_roots = ["%s"]
|
||||
""" % ('localhost', subdir)))
|
||||
tmpdir.ensure(subdir, "__init__.py")
|
||||
tmpdir.ensure(subdir, "test_one.py").write(py.code.Source("""
|
||||
def test_1():
|
||||
pass
|
||||
def test_2():
|
||||
assert 0
|
||||
def test_3():
|
||||
raise ValueError(23)
|
||||
def test_4(someargs):
|
||||
pass
|
||||
def test_5():
|
||||
assert __file__ != '%s'
|
||||
""" % str(tmpdir.join(subdir))))
|
||||
args = [str(tmpdir.join(subdir))]
|
||||
config = py.test.config._reparse(args)
|
||||
rsession = RSession(config, optimise_localhost=False)
|
||||
allevents = []
|
||||
rsession.main(reporter=allevents.append)
|
||||
testevents = [x for x in allevents
|
||||
if isinstance(x, report.ReceivedItemOutcome)]
|
||||
assert len(testevents)
|
||||
passevents = [i for i in testevents if i.outcome.passed]
|
||||
failevents = [i for i in testevents if i.outcome.excinfo]
|
||||
skippedevents = [i for i in testevents if i.outcome.skipped]
|
||||
assert len(testevents) == 5
|
||||
assert len(passevents) == 2
|
||||
assert len(failevents) == 3
|
||||
tb = failevents[0].outcome.excinfo.traceback
|
||||
assert tb[0].path.find("test_one") != -1
|
||||
assert tb[0].source.find("test_2") != -1
|
||||
assert failevents[0].outcome.excinfo.typename == 'AssertionError'
|
||||
tb = failevents[1].outcome.excinfo.traceback
|
||||
assert tb[0].path.find("test_one") != -1
|
||||
assert tb[0].source.find("test_3") != -1
|
||||
assert failevents[1].outcome.excinfo.typename == 'ValueError'
|
||||
assert failevents[1].outcome.excinfo.value == '23'
|
||||
tb = failevents[2].outcome.excinfo.traceback
|
||||
assert failevents[2].outcome.excinfo.typename == 'TypeError'
|
||||
assert tb[0].path.find("executor") != -1
|
||||
assert tb[0].source.find("execute") != -1
|
||||
|
||||
def test_setup_teardown_ssh(self):
|
||||
hosts = [HostInfo('localhost')]
|
||||
parse_directories(hosts)
|
||||
setup_events = []
|
||||
teardown_events = []
|
||||
|
||||
config = py.test.config._reparse([])
|
||||
session_options.bind_config(config)
|
||||
nodes = init_hosts(setup_events.append, hosts, pkgdir,
|
||||
rsync_roots=["py"], optimise_localhost=False, remote_options=remote_options.d)
|
||||
teardown_hosts(teardown_events.append,
|
||||
[node.channel for node in nodes], nodes)
|
||||
|
||||
count_rsyn_calls = [i for i in setup_events
|
||||
if isinstance(i, report.HostRSyncing)]
|
||||
assert len(count_rsyn_calls) == len([i for i in hosts])
|
||||
count_ready_calls = [i for i in setup_events
|
||||
if isinstance(i, report.HostReady)]
|
||||
assert len(count_ready_calls) == len([i for i in hosts])
|
||||
|
||||
# same for teardown events
|
||||
teardown_wait_starts = [i for i in teardown_events
|
||||
if isinstance(i, report.CallStart)]
|
||||
teardown_wait_ends = [i for i in teardown_events
|
||||
if isinstance(i, report.CallFinish)]
|
||||
assert len(teardown_wait_starts) == len(hosts)
|
||||
assert len(teardown_wait_ends) == len(hosts)
|
||||
|
||||
def test_setup_teardown_run_ssh(self):
|
||||
hosts = [HostInfo('localhost')]
|
||||
parse_directories(hosts)
|
||||
allevents = []
|
||||
|
||||
config = py.test.config._reparse([])
|
||||
session_options.bind_config(config)
|
||||
nodes = init_hosts(allevents.append, hosts, pkgdir,
|
||||
rsync_roots=["py"], optimise_localhost=False, remote_options=remote_options.d)
|
||||
|
||||
from py.__.test.rsession.testing.test_executor \
|
||||
import ItemTestPassing, ItemTestFailing, ItemTestSkipping
|
||||
|
||||
rootcol = py.test.collect.Directory(pkgdir.dirpath())
|
||||
itempass = rootcol.getitembynames(funcpass_spec)
|
||||
itemfail = rootcol.getitembynames(funcfail_spec)
|
||||
itemskip = rootcol.getitembynames(funcskip_spec)
|
||||
itemprint = rootcol.getitembynames(funcprint_spec)
|
||||
|
||||
# actually run some tests
|
||||
for node in nodes:
|
||||
node.send(itempass)
|
||||
node.send(itemfail)
|
||||
node.send(itemskip)
|
||||
node.send(itemprint)
|
||||
|
||||
teardown_hosts(allevents.append, [node.channel for node in nodes], nodes)
|
||||
|
||||
events = [i for i in allevents
|
||||
if isinstance(i, report.ReceivedItemOutcome)]
|
||||
passed = [i for i in events
|
||||
if i.outcome.passed]
|
||||
skipped = [i for i in events
|
||||
if i.outcome.skipped]
|
||||
assert len(passed) == 2 * len(nodes)
|
||||
assert len(skipped) == len(nodes)
|
||||
assert len(events) == 4 * len(nodes)
|
||||
# one of passed for each node has non-empty stdout
|
||||
passed_stdout = [i for i in passed if i.outcome.stdout.find('samfing') != -1]
|
||||
assert len(passed_stdout) == len(nodes), passed
|
||||
|
||||
def test_config_pass(self):
|
||||
""" Tests options object passing master -> server
|
||||
"""
|
||||
allevents = []
|
||||
hosts = [HostInfo('localhost')]
|
||||
parse_directories(hosts)
|
||||
config = py.test.config._reparse([])
|
||||
session_options.bind_config(config)
|
||||
d = remote_options.d.copy()
|
||||
d['custom'] = 'custom'
|
||||
nodes = init_hosts(allevents.append, hosts, pkgdir,
|
||||
rsync_roots=["py"], remote_options=d,
|
||||
optimise_localhost=False)
|
||||
|
||||
rootcol = py.test.collect.Directory(pkgdir.dirpath())
|
||||
itempass = rootcol.getitembynames(funcoption_spec)
|
||||
itempassaswell = rootcol.getitembynames(funcoptioncustom_spec)
|
||||
|
||||
for node in nodes:
|
||||
node.send(itempass)
|
||||
node.send(itempassaswell)
|
||||
|
||||
teardown_hosts(allevents.append, [node.channel for node in nodes], nodes)
|
||||
events = [i for i in allevents
|
||||
if isinstance(i, report.ReceivedItemOutcome)]
|
||||
passed = [i for i in events
|
||||
if i.outcome.passed]
|
||||
skipped = [i for i in events
|
||||
if i.outcome.skipped]
|
||||
assert len(passed) == 2 * len(nodes)
|
||||
assert len(skipped) == 0
|
||||
assert len(events) == len(passed)
|
||||
|
||||
def test_nice_level(self):
|
||||
""" Tests if nice level behaviour is ok
|
||||
"""
|
||||
allevents = []
|
||||
hosts = [HostInfo('localhost')]
|
||||
parse_directories(hosts)
|
||||
tmpdir = py.test.ensuretemp("nice")
|
||||
tmpdir.ensure("__init__.py")
|
||||
tmpdir.ensure("conftest.py").write("""disthosts = ['localhost']""")
|
||||
tmpdir.ensure("test_one.py").write("""def test_nice():
|
||||
import os
|
||||
assert os.nice(0) == 10
|
||||
""")
|
||||
|
||||
config = py.test.config._reparse([tmpdir])
|
||||
config.option.nice_level = 10
|
||||
rsession = RSession(config)
|
||||
allevents = []
|
||||
rsession.main(reporter=allevents.append)
|
||||
testevents = [x for x in allevents
|
||||
if isinstance(x, report.ReceivedItemOutcome)]
|
||||
passevents = [x for x in testevents if x.outcome.passed]
|
||||
assert len(passevents) == 1
|
||||
|
||||
class XxxTestDirectories(object):
|
||||
# need complete rewrite, and unsure if it makes sense at all
|
||||
def test_simple_parse(self):
|
||||
sshhosts = [HostInfo(i) for i in ['h1', 'h2', 'h3']]
|
||||
parse_directories(sshhosts)
|
||||
|
||||
def test_sophisticated_parse(self):
|
||||
sshhosts = ['a@h1:/tmp', 'h2:tmp', 'h3']
|
||||
dirs = parse_directories(sshhosts)
|
||||
assert py.builtin.sorted(
|
||||
dirs.values()) == ['/tmp', 'pytestcache', 'tmp']
|
||||
|
||||
def test_parse_multiple_hosts(self):
|
||||
hosts = ['h1', 'h1', 'h1:/tmp']
|
||||
dirs = parse_directories(hosts)
|
||||
assert dirs == {(0, 'h1'): 'pytestcache', (1, 'h1'): 'pytestcache',
|
||||
(2, 'h1'):'/tmp'}
|
||||
|
||||
class TestInithosts(object):
|
||||
def test_inithosts(self):
|
||||
testevents = []
|
||||
hostnames = ['h1:/tmp', 'h1:/tmp', 'h1:/other', 'h2', 'h2:home']
|
||||
hosts = [HostInfo(i) for i in hostnames]
|
||||
parse_directories(hosts)
|
||||
init_hosts(testevents.append, hosts, pkgdir, do_sync=False)
|
||||
events = [i for i in testevents if isinstance(i, report.HostRSyncing)]
|
||||
assert len(events) == 4
|
||||
assert events[0].host.hostname == 'h1'
|
||||
assert events[0].host.relpath == '/tmp-h1'
|
||||
assert events[1].host.hostname == 'h1'
|
||||
assert events[1].host.relpath == '/other-h1'
|
||||
assert events[2].host.hostname == 'h2'
|
||||
assert events[2].host.relpath == 'pytestcache-h2'
|
||||
assert events[3].host.hostname == 'h2'
|
||||
assert events[3].host.relpath == 'home-h2'
|
||||
|
||||
80
py/test/rsession/testing/test_rsync.py
Normal file
80
py/test/rsession/testing/test_rsync.py
Normal file
@@ -0,0 +1,80 @@
|
||||
import py
|
||||
from py.__.test.rsession.rsync import RSync
|
||||
|
||||
|
||||
def setup_module(mod):
|
||||
mod.gw = py.execnet.PopenGateway()
|
||||
mod.gw2 = py.execnet.PopenGateway()
|
||||
|
||||
def teardown_module(mod):
|
||||
mod.gw.exit()
|
||||
mod.gw2.exit()
|
||||
|
||||
|
||||
def test_dirsync():
|
||||
base = py.test.ensuretemp('dirsync')
|
||||
dest = base.join('dest')
|
||||
dest2 = base.join('dest2')
|
||||
source = base.mkdir('source')
|
||||
|
||||
for s in ('content1', 'content2-a-bit-longer'):
|
||||
source.ensure('subdir', 'file1').write(s)
|
||||
rsync = RSync()
|
||||
rsync.add_target(gw, dest)
|
||||
rsync.add_target(gw2, dest2)
|
||||
rsync.send(source)
|
||||
assert dest.join('subdir').check(dir=1)
|
||||
assert dest.join('subdir', 'file1').check(file=1)
|
||||
assert dest.join('subdir', 'file1').read() == s
|
||||
assert dest2.join('subdir').check(dir=1)
|
||||
assert dest2.join('subdir', 'file1').check(file=1)
|
||||
assert dest2.join('subdir', 'file1').read() == s
|
||||
|
||||
source.join('subdir').remove('file1')
|
||||
rsync = RSync()
|
||||
rsync.add_target(gw2, dest2)
|
||||
rsync.add_target(gw, dest)
|
||||
rsync.send(source)
|
||||
assert dest.join('subdir', 'file1').check(file=1)
|
||||
assert dest2.join('subdir', 'file1').check(file=1)
|
||||
rsync = RSync(delete=True)
|
||||
rsync.add_target(gw2, dest2)
|
||||
rsync.add_target(gw, dest)
|
||||
rsync.send(source)
|
||||
assert not dest.join('subdir', 'file1').check()
|
||||
assert not dest2.join('subdir', 'file1').check()
|
||||
|
||||
def test_symlink_rsync():
|
||||
if py.std.sys.platform == 'win32':
|
||||
py.test.skip("symlinks are unsupported on Windows.")
|
||||
base = py.test.ensuretemp('symlinkrsync')
|
||||
dest = base.join('dest')
|
||||
source = base.join('source')
|
||||
source.ensure("existant")
|
||||
source.join("rellink").mksymlinkto(source.join("existant"), absolute=0)
|
||||
source.join('abslink').mksymlinkto(source.join("existant"))
|
||||
|
||||
rsync = RSync()
|
||||
rsync.add_target(gw, dest)
|
||||
rsync.send(source)
|
||||
|
||||
assert dest.join('rellink').readlink() == dest.join("existant")
|
||||
assert dest.join('abslink').readlink() == dest.join("existant")
|
||||
|
||||
def test_callback():
|
||||
base = py.test.ensuretemp('callback')
|
||||
dest = base.join("dest")
|
||||
source = base.join("source")
|
||||
source.ensure("existant").write("a" * 100)
|
||||
source.ensure("existant2").write("a" * 10)
|
||||
total = {}
|
||||
def callback(cmd, lgt, channel):
|
||||
total[(cmd, lgt)] = True
|
||||
|
||||
rsync = RSync(callback=callback)
|
||||
#rsync = RSync()
|
||||
rsync.add_target(gw, dest)
|
||||
rsync.send(source)
|
||||
|
||||
assert total == {("list", 110):True, ("ack", 100):True, ("ack", 10):True}
|
||||
|
||||
225
py/test/rsession/testing/test_slave.py
Normal file
225
py/test/rsession/testing/test_slave.py
Normal file
@@ -0,0 +1,225 @@
|
||||
|
||||
""" Testing the slave side node code (in a local way). """
|
||||
from py.__.test.rsession.slave import SlaveNode, slave_main
|
||||
from py.__.test.rsession.outcome import ReprOutcome
|
||||
import py, sys
|
||||
|
||||
modlevel = []
|
||||
import os
|
||||
|
||||
if sys.platform == 'win32':
|
||||
py.test.skip("rsession is unsupported on Windows.")
|
||||
|
||||
def setup_module(module):
|
||||
from py.__.test.rsession.rsession import session_options
|
||||
module.rootdir = py.path.local(py.__file__).dirpath().dirpath()
|
||||
config = py.test.config._reparse([])
|
||||
session_options.bind_config(config)
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
# inlined testing functions used below
|
||||
def funcpass():
|
||||
pass
|
||||
|
||||
def funcfail():
|
||||
raise AssertionError("hello world")
|
||||
|
||||
def funcskip():
|
||||
py.test.skip("skipped")
|
||||
|
||||
def funcprint():
|
||||
print "samfing"
|
||||
|
||||
def funcprintfail():
|
||||
print "samfing elz"
|
||||
asddsa
|
||||
|
||||
def funcoption():
|
||||
from py.__.test.rsession.rsession import remote_options
|
||||
assert remote_options.we_are_remote
|
||||
|
||||
def funcoptioncustom():
|
||||
from py.__.test.rsession.rsession import remote_options
|
||||
assert remote_options.custom == "custom"
|
||||
|
||||
def funchang():
|
||||
import time
|
||||
time.sleep(1000)
|
||||
|
||||
BASE = "py/test/rsession/testing/test_slave.py/"
|
||||
funcpass_spec = (BASE + "funcpass").split("/")
|
||||
funcfail_spec = (BASE + "funcfail").split("/")
|
||||
funcskip_spec = (BASE + "funcskip").split("/")
|
||||
funcprint_spec = (BASE + "funcprint").split("/")
|
||||
funcprintfail_spec = (BASE + "funcprintfail").split("/")
|
||||
funcoption_spec = (BASE + "funcoption").split("/")
|
||||
funcoptioncustom_spec = (BASE + "funcoptioncustom").split("/")
|
||||
funchang_spec = (BASE + "funchang").split("/")
|
||||
mod_spec = BASE[:-1].split("/")
|
||||
|
||||
# ----------------------------------------------------------------------
|
||||
|
||||
from py.__.test.rsession.executor import RunExecutor
|
||||
|
||||
def gettestnode():
|
||||
rootcol = py.test.collect.Directory(rootdir)
|
||||
node = SlaveNode(rootcol, executor=RunExecutor)
|
||||
return node
|
||||
|
||||
def test_slave_run_passing():
|
||||
node = gettestnode()
|
||||
outcome = node.execute(funcpass_spec)
|
||||
assert outcome.passed
|
||||
assert not outcome.setupfailure
|
||||
|
||||
ser = outcome.make_repr()
|
||||
reproutcome = ReprOutcome(ser)
|
||||
assert reproutcome.passed
|
||||
assert not reproutcome.setupfailure
|
||||
|
||||
def test_slave_run_failing():
|
||||
node = gettestnode()
|
||||
outcome = node.execute(funcfail_spec)
|
||||
assert not outcome.passed
|
||||
assert not outcome.setupfailure
|
||||
assert len(outcome.excinfo.traceback) == 1
|
||||
assert outcome.excinfo.traceback[-1].frame.code.name == 'funcfail'
|
||||
|
||||
ser = outcome.make_repr()
|
||||
reproutcome = ReprOutcome(ser)
|
||||
assert not reproutcome.passed
|
||||
assert not reproutcome.setupfailure
|
||||
assert reproutcome.excinfo
|
||||
|
||||
def test_slave_run_skipping():
|
||||
node = gettestnode()
|
||||
outcome = node.execute(funcskip_spec)
|
||||
assert not outcome.passed
|
||||
assert outcome.skipped
|
||||
|
||||
ser = outcome.make_repr()
|
||||
reproutcome = ReprOutcome(ser)
|
||||
assert not reproutcome.passed
|
||||
assert reproutcome.skipped
|
||||
|
||||
def test_slave_run_failing_wrapped():
|
||||
node = gettestnode()
|
||||
repr_outcome = node.run(funcfail_spec)
|
||||
outcome = ReprOutcome(repr_outcome)
|
||||
assert not outcome.passed
|
||||
assert not outcome.setupfailure
|
||||
assert outcome.excinfo
|
||||
|
||||
def test_slave_main_simple():
|
||||
res = []
|
||||
q = [None,
|
||||
funcpass_spec,
|
||||
funcfail_spec
|
||||
]
|
||||
slave_main(q.pop, res.append, str(rootdir))
|
||||
assert len(res) == 2
|
||||
res_repr = [ReprOutcome(r) for r in res]
|
||||
assert not res_repr[0].passed and res_repr[1].passed
|
||||
|
||||
def test_slave_run_different_stuff():
|
||||
node = gettestnode()
|
||||
node.run("py doc log.txt".split())
|
||||
|
||||
def test_slave_setup_fails_on_import_error():
|
||||
from py.__.test.rsession.slave import setup
|
||||
tmp = py.test.ensuretemp("slavesetup")
|
||||
class C:
|
||||
def __init__(self):
|
||||
self.count = 0
|
||||
|
||||
def receive(self):
|
||||
if self.count == 0:
|
||||
retval = str(tmp)
|
||||
elif self.count == 1:
|
||||
from py.__.test.rsession.rsession import remote_options
|
||||
retval = remote_options.d
|
||||
else:
|
||||
raise NotImplementedError("more data")
|
||||
self.count += 1
|
||||
return retval
|
||||
try:
|
||||
exec py.code.Source(setup, "setup()").compile() in {
|
||||
'channel': C()}
|
||||
except ImportError:
|
||||
pass # expected
|
||||
else:
|
||||
py.test.fail("missing exception")
|
||||
|
||||
def test_slave_setup_exit():
|
||||
tmp = py.test.ensuretemp("slaveexit")
|
||||
tmp.ensure("__init__.py")
|
||||
from py.__.test.rsession.slave import setup
|
||||
from Queue import Queue
|
||||
q = Queue()
|
||||
|
||||
class C:
|
||||
res = []
|
||||
def __init__(self):
|
||||
from py.__.test.rsession.rsession import remote_options
|
||||
self.q = [str(tmp),
|
||||
remote_options.d,
|
||||
funchang_spec,
|
||||
42,
|
||||
funcpass_spec]
|
||||
self.q.reverse()
|
||||
|
||||
def receive(self):
|
||||
return self.q.pop()
|
||||
|
||||
def setcallback(self, callback):
|
||||
import thread
|
||||
def f():
|
||||
while 1:
|
||||
callback(self.q.pop())
|
||||
f()
|
||||
#thread.start_new_thread(f, ())
|
||||
|
||||
send = res.append
|
||||
try:
|
||||
exec py.code.Source(setup, "setup()").compile() in {'channel':C()}
|
||||
except SystemExit:
|
||||
pass
|
||||
else:
|
||||
py.test.fail("Did not exit")
|
||||
|
||||
def test_slave_setup_fails_on_missing_pkg():
|
||||
from py.__.test.rsession.slave import setup
|
||||
tmp = py.test.ensuretemp("slavesetup2")
|
||||
x = tmp.ensure("sometestpackage", "__init__.py")
|
||||
class C:
|
||||
def __init__(self):
|
||||
self.count = 0
|
||||
|
||||
def receive(self):
|
||||
if self.count == 0:
|
||||
retval = str(x.dirpath())
|
||||
elif self.count == 1:
|
||||
from py.__.test.rsession.rsession import remote_options
|
||||
retval = remote_options.d
|
||||
else:
|
||||
raise NotImplementedError("more data")
|
||||
self.count += 1
|
||||
return retval
|
||||
try:
|
||||
exec py.code.Source(setup, "setup()").compile() in {'channel': C()}
|
||||
except AttributeError: # channel.send
|
||||
pass
|
||||
else:
|
||||
py.test.fail("missing exception")
|
||||
|
||||
# now create a parallel structure
|
||||
tmp = py.test.ensuretemp("slavesetup3")
|
||||
x = tmp.ensure("sometestpackage", "__init__.py")
|
||||
try:
|
||||
exec py.code.Source(setup, "setup()").compile() in {
|
||||
'channel': C()}
|
||||
except AssertionError:
|
||||
pass # expected
|
||||
else:
|
||||
py.test.fail("missing exception")
|
||||
|
||||
92
py/test/rsession/testing/test_web.py
Normal file
92
py/test/rsession/testing/test_web.py
Normal file
@@ -0,0 +1,92 @@
|
||||
|
||||
""" webtest
|
||||
"""
|
||||
|
||||
import py
|
||||
|
||||
try:
|
||||
from pypy.translator.js.main import rpython2javascript
|
||||
from pypy.translator.js import commproxy
|
||||
|
||||
commproxy.USE_MOCHIKIT = False
|
||||
except ImportError:
|
||||
py.test.skip("PyPy not found")
|
||||
|
||||
def setup_module(mod):
|
||||
config = py.test.config._reparse([])
|
||||
from py.__.test.rsession.rsession import session_options
|
||||
session_options.bind_config(config)
|
||||
session_options.import_pypy = True
|
||||
from py.__.test.rsession.web import TestHandler as _TestHandler
|
||||
from py.__.test.rsession.web import MultiQueue
|
||||
mod._TestHandler = _TestHandler
|
||||
mod.MultiQueue = MultiQueue
|
||||
|
||||
def test_js_generate():
|
||||
from py.__.test.rsession import webjs
|
||||
from py.__.test.rsession.web import FUNCTION_LIST, IMPORTED_PYPY
|
||||
|
||||
source = rpython2javascript(webjs, FUNCTION_LIST)
|
||||
assert source
|
||||
|
||||
def test_parse_args():
|
||||
class TestTestHandler(_TestHandler):
|
||||
def __init__(self):
|
||||
pass
|
||||
h = TestTestHandler()
|
||||
assert h.parse_args('foo=bar') == {'foo': 'bar'}
|
||||
assert h.parse_args('foo=bar%20baz') == {'foo': 'bar baz'}
|
||||
assert h.parse_args('foo%20bar=baz') == {'foo bar': 'baz'}
|
||||
assert h.parse_args('foo=bar%baz') == {'foo': 'bar\xbaz'}
|
||||
py.test.raises(ValueError, 'h.parse_args("foo")')
|
||||
|
||||
class TestMultiQueue(object):
|
||||
def test_get_one_sessid(self):
|
||||
mq = MultiQueue()
|
||||
mq.put(1)
|
||||
result = mq.get(1234)
|
||||
assert result == 1
|
||||
|
||||
def test_get_two_sessid(self):
|
||||
mq = MultiQueue()
|
||||
mq.put(1)
|
||||
result = mq.get(1234)
|
||||
assert result == 1
|
||||
mq.put(2)
|
||||
result = mq.get(1234)
|
||||
assert result == 2
|
||||
result = mq.get(5678)
|
||||
assert result == 1
|
||||
result = mq.get(5678)
|
||||
assert result == 2
|
||||
|
||||
def test_get_blocking(self):
|
||||
import thread
|
||||
result = []
|
||||
def getitem(mq, sessid):
|
||||
result.append(mq.get(sessid))
|
||||
mq = MultiQueue()
|
||||
thread.start_new_thread(getitem, (mq, 1234))
|
||||
assert not result
|
||||
mq.put(1)
|
||||
py.std.time.sleep(0.1)
|
||||
assert result == [1]
|
||||
|
||||
def test_empty(self):
|
||||
mq = MultiQueue()
|
||||
assert mq.empty()
|
||||
mq.put(1)
|
||||
assert not mq.empty()
|
||||
result = mq.get(1234)
|
||||
result == 1
|
||||
assert mq.empty()
|
||||
mq.put(2)
|
||||
result = mq.get(4567)
|
||||
assert result == 1
|
||||
result = mq.get(1234)
|
||||
assert result == 2
|
||||
assert not mq.empty()
|
||||
result = mq.get(4567)
|
||||
assert result == 2
|
||||
assert mq.empty()
|
||||
|
||||
140
py/test/rsession/testing/test_webjs.py
Normal file
140
py/test/rsession/testing/test_webjs.py
Normal file
@@ -0,0 +1,140 @@
|
||||
import py
|
||||
|
||||
try:
|
||||
import pypy
|
||||
from pypy.translator.js.modules import dom
|
||||
from pypy.translator.js.tester import schedule_callbacks
|
||||
from py.__.test.rsession.rsession import session_options
|
||||
dom.Window # check whether dom was properly imported or is just a
|
||||
# leftover in sys.modules
|
||||
except (ImportError, AttributeError):
|
||||
py.test.skip('PyPy not found')
|
||||
|
||||
from py.__.test.rsession import webjs
|
||||
from py.__.test.rsession.web import exported_methods
|
||||
here = py.magic.autopath().dirpath()
|
||||
|
||||
def setup_module(mod):
|
||||
# load HTML into window object
|
||||
html = here.join('../webdata/index.html').read()
|
||||
mod.html = html
|
||||
from pypy.translator.js.modules import dom
|
||||
mod.dom = dom
|
||||
dom.window = dom.Window(html)
|
||||
dom.document = dom.window.document
|
||||
config = py.test.config._reparse([])
|
||||
from py.__.test.rsession.rsession import session_options
|
||||
session_options.bind_config(config)
|
||||
session_options.import_pypy = True
|
||||
from py.__.test.rsession import webjs
|
||||
from py.__.test.rsession.web import exported_methods
|
||||
mod.webjs = webjs
|
||||
mod.exported_methods = exported_methods
|
||||
|
||||
def setup_function(f):
|
||||
dom.window = dom.Window(html)
|
||||
dom.document = dom.window.document
|
||||
|
||||
def test_html_loaded():
|
||||
body = dom.window.document.getElementsByTagName('body')[0]
|
||||
assert len(body.childNodes) > 0
|
||||
assert str(body.childNodes[1].nodeName) == 'A'
|
||||
|
||||
def test_set_msgbox():
|
||||
msgbox = dom.window.document.getElementById('messagebox')
|
||||
assert len(msgbox.childNodes) == 0
|
||||
webjs.set_msgbox('foo', 'bar')
|
||||
assert len(msgbox.childNodes) == 1
|
||||
assert msgbox.childNodes[0].nodeName == 'PRE'
|
||||
assert msgbox.childNodes[0].childNodes[0].nodeValue == 'foo\nbar'
|
||||
|
||||
def test_show_info():
|
||||
info = dom.window.document.getElementById('info')
|
||||
info.style.visibility = 'hidden'
|
||||
info.innerHTML = ''
|
||||
webjs.show_info('foobar')
|
||||
content = info.innerHTML
|
||||
assert content == 'foobar'
|
||||
bgcolor = info.style.backgroundColor
|
||||
assert bgcolor == 'beige'
|
||||
|
||||
def test_hide_info():
|
||||
info = dom.window.document.getElementById('info')
|
||||
info.style.visibility = 'visible'
|
||||
webjs.hide_info()
|
||||
assert info.style.visibility == 'hidden'
|
||||
|
||||
def test_process():
|
||||
main_t = dom.window.document.getElementById('main_table')
|
||||
assert len(main_t.getElementsByTagName('tr')) == 0
|
||||
assert not webjs.process({})
|
||||
|
||||
msg = {'type': 'ItemStart',
|
||||
'itemtype': 'Module',
|
||||
'itemname': 'foo.py',
|
||||
'fullitemname': 'modules/foo.py',
|
||||
'length': 10,
|
||||
}
|
||||
assert webjs.process(msg)
|
||||
trs = main_t.getElementsByTagName('tr')
|
||||
assert len(trs) == 1
|
||||
tr = trs[0]
|
||||
assert len(tr.childNodes) == 2
|
||||
assert tr.childNodes[0].nodeName == 'TD'
|
||||
assert tr.childNodes[0].innerHTML == 'foo.py[0/10]'
|
||||
assert tr.childNodes[1].nodeName == 'TD'
|
||||
assert tr.childNodes[1].childNodes[0].nodeName == 'TABLE'
|
||||
assert len(tr.childNodes[1].getElementsByTagName('tr')) == 0
|
||||
|
||||
def test_process_two():
|
||||
main_t = dom.window.document.getElementById('main_table')
|
||||
msg = {'type': 'ItemStart',
|
||||
'itemtype': 'Module',
|
||||
'itemname': 'foo.py',
|
||||
'fullitemname': 'modules/foo.py',
|
||||
'length': 10,
|
||||
}
|
||||
webjs.process(msg)
|
||||
msg = {'type': 'ReceivedItemOutcome',
|
||||
'fullmodulename': 'modules/foo.py',
|
||||
'passed' : 'True',
|
||||
'fullitemname' : 'modules/foo.py/test_item',
|
||||
'hostkey': None,
|
||||
}
|
||||
webjs.process(msg)
|
||||
trs = main_t.getElementsByTagName('tr')
|
||||
tds = trs[0].getElementsByTagName('td')
|
||||
# two cells in the row, one in the table inside one of the cells
|
||||
assert len(tds) == 3
|
||||
html = tds[0].innerHTML
|
||||
assert html == 'foo.py[1/10]'
|
||||
assert tds[2].innerHTML == '.'
|
||||
|
||||
def test_signal():
|
||||
main_t = dom.window.document.getElementById('main_table')
|
||||
msg = {'type': 'ItemStart',
|
||||
'itemtype': 'Module',
|
||||
'itemname': 'foo.py',
|
||||
'fullitemname': 'modules/foo.py',
|
||||
'length': 10,
|
||||
}
|
||||
webjs.process(msg)
|
||||
msg = {'type': 'ReceivedItemOutcome',
|
||||
'fullmodulename': 'modules/foo.py',
|
||||
'passed' : 'False',
|
||||
'fullitemname' : 'modules/foo.py/test_item',
|
||||
'hostkey': None,
|
||||
'signal': '10',
|
||||
'skipped': 'False',
|
||||
}
|
||||
exported_methods.fail_reasons['modules/foo.py/test_item'] = 'Received signal 10'
|
||||
exported_methods.stdout['modules/foo.py/test_item'] = ''
|
||||
exported_methods.stderr['modules/foo.py/test_item'] = ''
|
||||
webjs.process(msg)
|
||||
schedule_callbacks(exported_methods)
|
||||
# ouch
|
||||
assert dom.document.getElementById('modules/foo.py').childNodes[0].\
|
||||
childNodes[0].childNodes[0].childNodes[0].nodeValue == 'F'
|
||||
|
||||
# XXX: Write down test for full run
|
||||
|
||||
430
py/test/rsession/web.py
Normal file
430
py/test/rsession/web.py
Normal file
@@ -0,0 +1,430 @@
|
||||
|
||||
""" web server for py.test
|
||||
"""
|
||||
|
||||
from BaseHTTPServer import HTTPServer, BaseHTTPRequestHandler
|
||||
|
||||
import thread, threading
|
||||
import re
|
||||
import time
|
||||
import random
|
||||
import Queue
|
||||
import os
|
||||
import sys
|
||||
import socket
|
||||
|
||||
import py
|
||||
from py.__.test.rsession.rsession import RSession, session_options
|
||||
from py.__.test.rsession import report
|
||||
from py.__.test import collect
|
||||
from py.__.test.rsession.webdata import json
|
||||
|
||||
DATADIR = py.path.local(__file__).dirpath("webdata")
|
||||
FUNCTION_LIST = ["main", "show_skip", "show_traceback", "show_info", "hide_info",
|
||||
"show_host", "hide_host", "hide_messagebox"]
|
||||
|
||||
try:
|
||||
try:
|
||||
if not session_options.import_pypy:
|
||||
raise ImportError
|
||||
except AttributeError:
|
||||
pass
|
||||
from pypy.rpython.ootypesystem.bltregistry import MethodDesc, BasicExternal,\
|
||||
described
|
||||
from pypy.translator.js.main import rpython2javascript
|
||||
from pypy.translator.js import commproxy
|
||||
from pypy.rpython.extfunc import _callable
|
||||
|
||||
commproxy.USE_MOCHIKIT = False
|
||||
IMPORTED_PYPY = True
|
||||
except (ImportError, NameError):
|
||||
class BasicExternal(object):
|
||||
pass
|
||||
|
||||
def described(*args, **kwargs):
|
||||
def decorator(func):
|
||||
return func
|
||||
return decorator
|
||||
|
||||
def _callable(*args, **kwargs):
|
||||
pass
|
||||
|
||||
IMPORTED_PYPY = False
|
||||
|
||||
def add_item(event):
|
||||
""" A little helper
|
||||
"""
|
||||
item = event.item
|
||||
itemtype = item.__class__.__name__
|
||||
itemname = item.name
|
||||
fullitemname = "/".join(item.listnames())
|
||||
d = {'fullitemname': fullitemname, 'itemtype': itemtype,
|
||||
'itemname': itemname}
|
||||
#if itemtype == 'Module':
|
||||
try:
|
||||
d['length'] = str(len(list(event.item.tryiter())))
|
||||
except:
|
||||
d['length'] = "?"
|
||||
return d
|
||||
|
||||
class MultiQueue(object):
|
||||
""" a tailor-made queue (internally using Queue) for py.test.rsession.web
|
||||
|
||||
API-wise the main difference is that the get() method gets a sessid
|
||||
argument, which is used to determine what data to feed to the client
|
||||
|
||||
when a data queue for a sessid doesn't yet exist, it is created, and
|
||||
filled with data that has already been fed to the other clients
|
||||
"""
|
||||
def __init__(self):
|
||||
self._cache = []
|
||||
self._session_queues = {}
|
||||
self._lock = py.std.thread.allocate_lock()
|
||||
|
||||
def put(self, item):
|
||||
self._lock.acquire()
|
||||
try:
|
||||
self._cache.append(item)
|
||||
for key, q in self._session_queues.items():
|
||||
q.put(item)
|
||||
finally:
|
||||
self._lock.release()
|
||||
|
||||
def _del(self, sessid):
|
||||
self._lock.acquire()
|
||||
try:
|
||||
del self._session_queues[sessid]
|
||||
finally:
|
||||
self._lock.release()
|
||||
|
||||
def get(self, sessid):
|
||||
self._lock.acquire()
|
||||
try:
|
||||
if not sessid in self._session_queues:
|
||||
self._create_session_queue(sessid)
|
||||
finally:
|
||||
self._lock.release()
|
||||
return self._session_queues[sessid].get(sessid)
|
||||
|
||||
def empty(self):
|
||||
self._lock.acquire()
|
||||
try:
|
||||
if not self._session_queues:
|
||||
return not len(self._cache)
|
||||
for q in self._session_queues.values():
|
||||
if not q.empty():
|
||||
return False
|
||||
finally:
|
||||
self._lock.release()
|
||||
return True
|
||||
|
||||
def empty_queue(self, sessid):
|
||||
return self._session_queues[sessid].empty()
|
||||
|
||||
def _create_session_queue(self, sessid):
|
||||
self._session_queues[sessid] = q = Queue.Queue()
|
||||
for item in self._cache:
|
||||
q.put(item)
|
||||
|
||||
class ExportedMethods(BasicExternal):
|
||||
_render_xmlhttp = True
|
||||
def __init__(self):
|
||||
self.pending_events = MultiQueue()
|
||||
self.start_event = threading.Event()
|
||||
self.end_event = threading.Event()
|
||||
self.skip_reasons = {}
|
||||
self.fail_reasons = {}
|
||||
self.stdout = {}
|
||||
self.stderr = {}
|
||||
self.all = 0
|
||||
|
||||
def findmodule(self, item):
|
||||
# find the most outwards parent which is module
|
||||
current = item
|
||||
while current:
|
||||
if isinstance(current, collect.Module):
|
||||
break
|
||||
current = current.parent
|
||||
|
||||
if current is not None:
|
||||
return str(current.name), str("/".join(current.listnames()))
|
||||
else:
|
||||
return str(item.parent.name), str("/".join(item.parent.listnames()))
|
||||
|
||||
def show_hosts(self):
|
||||
self.start_event.wait()
|
||||
to_send = {}
|
||||
for host in self.hosts:
|
||||
to_send[host.hostid] = host.hostname
|
||||
return to_send
|
||||
show_hosts = described(retval={str:str}, args=[('callback',
|
||||
_callable([{str:str}]))])(show_hosts)
|
||||
|
||||
def show_skip(self, item_name="aa"):
|
||||
return {'item_name': item_name,
|
||||
'reason': self.skip_reasons[item_name]}
|
||||
show_skip = described(retval={str:str}, args=[('item_name',str),('callback',
|
||||
_callable([{str:str}]))])(show_skip)
|
||||
|
||||
def show_fail(self, item_name="aa"):
|
||||
return {'item_name':item_name,
|
||||
'traceback':str(self.fail_reasons[item_name]),
|
||||
'stdout':self.stdout[item_name],
|
||||
'stderr':self.stderr[item_name]}
|
||||
show_fail = described(retval={str:str}, args=[('item_name',str),('callback',
|
||||
_callable([{str:str}]))])(show_fail)
|
||||
|
||||
_sessids = None
|
||||
_sesslock = py.std.thread.allocate_lock()
|
||||
def show_sessid(self):
|
||||
if not self._sessids:
|
||||
self._sessids = []
|
||||
self._sesslock.acquire()
|
||||
try:
|
||||
while 1:
|
||||
chars = list(py.std.string.lowercase)
|
||||
py.std.random.shuffle(chars)
|
||||
sessid = ''.join(chars[:8])
|
||||
if sessid not in self._sessids:
|
||||
self._sessids.append(sessid)
|
||||
break
|
||||
finally:
|
||||
self._sesslock.release()
|
||||
return sessid
|
||||
show_sessid = described(retval=str, args=[('callback',
|
||||
_callable([str]))])(show_sessid)
|
||||
|
||||
def failed(self, **kwargs):
|
||||
if not 'sessid' in kwargs:
|
||||
return
|
||||
sessid = kwargs['sessid']
|
||||
to_del = -1
|
||||
for num, i in enumerate(self._sessids):
|
||||
if i == sessid:
|
||||
to_del = num
|
||||
if to_del != -1:
|
||||
del self._sessids[to_del]
|
||||
self.pending_events._del(kwargs['sessid'])
|
||||
|
||||
def show_all_statuses(self, sessid=-1):
|
||||
retlist = [self.show_status_change(sessid)]
|
||||
while not self.pending_events.empty_queue(sessid):
|
||||
retlist.append(self.show_status_change(sessid))
|
||||
retval = retlist
|
||||
return retval
|
||||
show_all_statuses = described(retval=[{str:str}],args=
|
||||
[('sessid',str), ('callback',_callable([[{str:str}]]))])(show_all_statuses)
|
||||
|
||||
def show_status_change(self, sessid):
|
||||
event = self.pending_events.get(sessid)
|
||||
if event is None:
|
||||
self.end_event.set()
|
||||
return {}
|
||||
# some dispatcher here
|
||||
if isinstance(event, report.ReceivedItemOutcome):
|
||||
args = {}
|
||||
outcome = event.outcome
|
||||
for key, val in outcome.__dict__.iteritems():
|
||||
args[key] = str(val)
|
||||
args.update(add_item(event))
|
||||
mod_name, mod_fullname = self.findmodule(event.item)
|
||||
args['modulename'] = str(mod_name)
|
||||
args['fullmodulename'] = str(mod_fullname)
|
||||
fullitemname = args['fullitemname']
|
||||
if outcome.skipped:
|
||||
self.skip_reasons[fullitemname] = outcome.skipped
|
||||
elif outcome.excinfo:
|
||||
self.fail_reasons[fullitemname] = self.repr_failure_tblong(
|
||||
event.item, outcome.excinfo, outcome.excinfo.traceback)
|
||||
self.stdout[fullitemname] = outcome.stdout
|
||||
self.stderr[fullitemname] = outcome.stderr
|
||||
elif outcome.signal:
|
||||
self.fail_reasons[fullitemname] = "Received signal %d" % outcome.signal
|
||||
self.stdout[fullitemname] = outcome.stdout
|
||||
self.stderr[fullitemname] = outcome.stderr
|
||||
if event.channel:
|
||||
args['hostkey'] = event.channel.gateway.host.hostid
|
||||
else:
|
||||
args['hostkey'] = ''
|
||||
elif isinstance(event, report.ItemStart):
|
||||
args = add_item(event)
|
||||
elif isinstance(event, report.TestFinished):
|
||||
args = {}
|
||||
args['run'] = str(self.all)
|
||||
args['fails'] = str(len(self.fail_reasons))
|
||||
args['skips'] = str(len(self.skip_reasons))
|
||||
elif isinstance(event, report.SendItem):
|
||||
args = add_item(event)
|
||||
args['hostkey'] = event.channel.gateway.host.hostid
|
||||
elif isinstance(event, report.HostReady):
|
||||
self.ready_hosts[event.host] = True
|
||||
args = {'hostname' : event.host.hostname, 'hostkey' : event.host.hostid}
|
||||
elif isinstance(event, report.FailedTryiter):
|
||||
args = add_item(event)
|
||||
elif isinstance(event, report.SkippedTryiter):
|
||||
args = add_item(event)
|
||||
args['reason'] = str(event.excinfo.value)
|
||||
else:
|
||||
args = {}
|
||||
args['event'] = str(event)
|
||||
args['type'] = event.__class__.__name__
|
||||
return args
|
||||
|
||||
def repr_failure_tblong(self, item, excinfo, traceback):
|
||||
lines = []
|
||||
for index, entry in py.builtin.enumerate(traceback):
|
||||
lines.append('----------')
|
||||
lines.append("%s: %s" % (entry.path, entry.lineno))
|
||||
lines += self.repr_source(entry.relline, entry.source)
|
||||
lines.append("%s: %s" % (excinfo.typename, excinfo.value))
|
||||
return "\n".join(lines)
|
||||
|
||||
def repr_source(self, relline, source):
|
||||
lines = []
|
||||
for num, line in enumerate(source.split("\n")):
|
||||
if num == relline:
|
||||
lines.append(">>>>" + line)
|
||||
else:
|
||||
lines.append(" " + line)
|
||||
return lines
|
||||
|
||||
def report_ReceivedItemOutcome(self, event):
|
||||
self.all += 1
|
||||
self.pending_events.put(event)
|
||||
|
||||
def report_ItemStart(self, event):
|
||||
if isinstance(event.item, py.test.collect.Module):
|
||||
self.pending_events.put(event)
|
||||
|
||||
def report_unknown(self, event):
|
||||
# XXX: right now, we just pass it for showing
|
||||
self.pending_events.put(event)
|
||||
|
||||
def report_TestStarted(self, event):
|
||||
# XXX: It overrides out self.hosts
|
||||
self.hosts = {}
|
||||
self.ready_hosts = {}
|
||||
for host in event.hosts:
|
||||
self.hosts[host] = host
|
||||
self.ready_hosts[host] = False
|
||||
self.start_event.set()
|
||||
self.pending_events.put(event)
|
||||
|
||||
def report(self, what):
|
||||
repfun = getattr(self, "report_" + what.__class__.__name__,
|
||||
self.report_unknown)
|
||||
try:
|
||||
repfun(what)
|
||||
except (KeyboardInterrupt, SystemExit):
|
||||
raise
|
||||
except:
|
||||
print "Internal reporting problem"
|
||||
excinfo = py.code.ExceptionInfo()
|
||||
for i in excinfo.traceback:
|
||||
print str(i)[2:-1]
|
||||
print excinfo
|
||||
|
||||
## try:
|
||||
## self.wait_flag.acquire()
|
||||
## self.pending_events.insert(0, event)
|
||||
## self.wait_flag.notify()
|
||||
## finally:
|
||||
## self.wait_flag.release()
|
||||
|
||||
exported_methods = ExportedMethods()
|
||||
|
||||
class TestHandler(BaseHTTPRequestHandler):
|
||||
exported_methods = exported_methods
|
||||
|
||||
def do_GET(self):
|
||||
path = self.path
|
||||
if path.endswith("/"):
|
||||
path = path[:-1]
|
||||
if path.startswith("/"):
|
||||
path = path[1:]
|
||||
m = re.match('^(.*)\?(.*)$', path)
|
||||
if m:
|
||||
path = m.group(1)
|
||||
getargs = m.group(2)
|
||||
else:
|
||||
getargs = ""
|
||||
name_path = path.replace(".", "_")
|
||||
method_to_call = getattr(self, "run_" + name_path, None)
|
||||
if method_to_call is None:
|
||||
exec_meth = getattr(self.exported_methods, name_path, None)
|
||||
if exec_meth is None:
|
||||
self.send_error(404, "File %s not found" % path)
|
||||
else:
|
||||
try:
|
||||
self.serve_data('text/json',
|
||||
json.write(exec_meth(**self.parse_args(getargs))))
|
||||
except socket.error:
|
||||
# client happily disconnected
|
||||
exported_methods.failed(**self.parse_args(getargs))
|
||||
else:
|
||||
method_to_call()
|
||||
|
||||
def parse_args(self, getargs):
|
||||
# parse get argument list
|
||||
if getargs == "":
|
||||
return {}
|
||||
|
||||
unquote = py.std.urllib.unquote
|
||||
args = {}
|
||||
arg_pairs = getargs.split("&")
|
||||
for arg in arg_pairs:
|
||||
key, value = arg.split("=")
|
||||
args[unquote(key)] = unquote(value)
|
||||
return args
|
||||
|
||||
def log_message(self, format, *args):
|
||||
# XXX just discard it
|
||||
pass
|
||||
|
||||
do_POST = do_GET
|
||||
|
||||
def run_(self):
|
||||
self.run_index()
|
||||
|
||||
def run_index(self):
|
||||
data = py.path.local(DATADIR).join("index.html").open().read()
|
||||
self.serve_data("text/html", data)
|
||||
|
||||
def run_jssource(self):
|
||||
js_name = py.path.local(__file__).dirpath("webdata").join("source.js")
|
||||
web_name = py.path.local(__file__).dirpath().join("webjs.py")
|
||||
if IMPORTED_PYPY and web_name.mtime() > js_name.mtime():
|
||||
from py.__.test.rsession import webjs
|
||||
|
||||
javascript_source = rpython2javascript(webjs,
|
||||
FUNCTION_LIST, use_pdb=False)
|
||||
open(str(js_name), "w").write(javascript_source)
|
||||
self.serve_data("text/javascript", javascript_source)
|
||||
else:
|
||||
js_source = open(str(js_name), "r").read()
|
||||
self.serve_data("text/javascript", js_source)
|
||||
|
||||
def serve_data(self, content_type, data):
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", content_type)
|
||||
self.send_header("Content-length", len(data))
|
||||
self.end_headers()
|
||||
self.wfile.write(data)
|
||||
|
||||
def start_server(server_address = ('', 8000), handler=TestHandler, start_new=True):
|
||||
httpd = HTTPServer(server_address, handler)
|
||||
|
||||
if start_new:
|
||||
thread.start_new_thread(httpd.serve_forever, ())
|
||||
print "Server started, listening on %s" % (server_address,)
|
||||
return httpd
|
||||
else:
|
||||
print "Server started, listening on %s" % (server_address,)
|
||||
httpd.serve_forever()
|
||||
|
||||
def kill_server():
|
||||
exported_methods.pending_events.put(None)
|
||||
while not exported_methods.pending_events.empty():
|
||||
time.sleep(.1)
|
||||
exported_methods.end_event.wait()
|
||||
|
||||
0
py/test/rsession/webdata/__init__.py
Normal file
0
py/test/rsession/webdata/__init__.py
Normal file
114
py/test/rsession/webdata/index.html
Normal file
114
py/test/rsession/webdata/index.html
Normal file
@@ -0,0 +1,114 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>Py.test</title>
|
||||
<script type="text/javascript" src="/jssource"></script>
|
||||
<style type="text/css">
|
||||
|
||||
body, td, div {
|
||||
font-family: Andale mono;
|
||||
background-color: #ffd;
|
||||
color: #003;
|
||||
}
|
||||
|
||||
#info {
|
||||
visibility: hidden;
|
||||
position: fixed;
|
||||
left: 0px;
|
||||
top: 0px;
|
||||
text-align: center;
|
||||
width: 100%;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
#navbar {
|
||||
position: fixed;
|
||||
right: 1em;
|
||||
top: 1em;
|
||||
background-color: #ffd;
|
||||
border: 1px solid #003;
|
||||
z-index: 1;
|
||||
}
|
||||
|
||||
#navbar tr, #navbar td {
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
#navbar a {
|
||||
color: #003;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#navbar .title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#jobs {
|
||||
position: absolute;
|
||||
visibility: hidden;
|
||||
right: 2px;
|
||||
}
|
||||
|
||||
#jobs, #jobs td {
|
||||
background-color: #ffd;
|
||||
border: 1px solid #003;
|
||||
}
|
||||
|
||||
#jobs tr, #jobs td {
|
||||
border: 0px;
|
||||
}
|
||||
|
||||
#hosts {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
</style>
|
||||
</head>
|
||||
<body onload="main()">
|
||||
<a name="beginning">
|
||||
<h3 id="Tests">Tests</h3>
|
||||
</a>
|
||||
<div id="info">
|
||||
 
|
||||
</div>
|
||||
<table id="navbar">
|
||||
<tbody>
|
||||
<tr>
|
||||
<td class="title">Hosts status</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<table id="hosts">
|
||||
<tbody id="hostsbody">
|
||||
</tbody>
|
||||
</table>
|
||||
<table id="jobs">
|
||||
</table>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="title">Navigation</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="#beginning">Top</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="#message">Traceback info</a></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><a href="#aftermessage">End of Traceback</a></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table id="main_table">
|
||||
</table>
|
||||
<fieldset id="messagebox_fieldset">
|
||||
<legend><b>Data [<a href="javascript:hide_messagebox()">hide</a>]:</b></legend>
|
||||
<a name="message">
|
||||
<div id="messagebox"></div>
|
||||
</a>
|
||||
</fieldset>
|
||||
<a name="aftermessage">
|
||||
<div id="testmain"></div>
|
||||
</a>
|
||||
</body>
|
||||
</html>
|
||||
310
py/test/rsession/webdata/json.py
Normal file
310
py/test/rsession/webdata/json.py
Normal file
@@ -0,0 +1,310 @@
|
||||
import string
|
||||
import types
|
||||
|
||||
## json.py implements a JSON (http://json.org) reader and writer.
|
||||
## Copyright (C) 2005 Patrick D. Logan
|
||||
## Contact mailto:patrickdlogan@stardecisions.com
|
||||
##
|
||||
## This library is free software; you can redistribute it and/or
|
||||
## modify it under the terms of the GNU Lesser General Public
|
||||
## License as published by the Free Software Foundation; either
|
||||
## version 2.1 of the License, or (at your option) any later version.
|
||||
##
|
||||
## This library is distributed in the hope that it will be useful,
|
||||
## but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
||||
## Lesser General Public License for more details.
|
||||
##
|
||||
## You should have received a copy of the GNU Lesser General Public
|
||||
## License along with this library; if not, write to the Free Software
|
||||
## Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
|
||||
|
||||
class _StringGenerator(object):
|
||||
def __init__(self, string):
|
||||
self.string = string
|
||||
self.index = -1
|
||||
def peek(self):
|
||||
i = self.index + 1
|
||||
if i < len(self.string):
|
||||
return self.string[i]
|
||||
else:
|
||||
return None
|
||||
def next(self):
|
||||
self.index += 1
|
||||
if self.index < len(self.string):
|
||||
return self.string[self.index]
|
||||
else:
|
||||
raise StopIteration
|
||||
def all(self):
|
||||
return self.string
|
||||
|
||||
class WriteException(Exception):
|
||||
pass
|
||||
|
||||
class ReadException(Exception):
|
||||
pass
|
||||
|
||||
class JsonReader(object):
|
||||
hex_digits = {'A': 10,'B': 11,'C': 12,'D': 13,'E': 14,'F':15}
|
||||
escapes = {'t':'\t','n':'\n','f':'\f','r':'\r','b':'\b'}
|
||||
|
||||
def read(self, s):
|
||||
self._generator = _StringGenerator(s)
|
||||
result = self._read()
|
||||
return result
|
||||
|
||||
def _read(self):
|
||||
self._eatWhitespace()
|
||||
peek = self._peek()
|
||||
if peek is None:
|
||||
raise ReadException, "Nothing to read: '%s'" % self._generator.all()
|
||||
if peek == '{':
|
||||
return self._readObject()
|
||||
elif peek == '[':
|
||||
return self._readArray()
|
||||
elif peek == '"':
|
||||
return self._readString()
|
||||
elif peek == '-' or peek.isdigit():
|
||||
return self._readNumber()
|
||||
elif peek == 't':
|
||||
return self._readTrue()
|
||||
elif peek == 'f':
|
||||
return self._readFalse()
|
||||
elif peek == 'n':
|
||||
return self._readNull()
|
||||
elif peek == '/':
|
||||
self._readComment()
|
||||
return self._read()
|
||||
else:
|
||||
raise ReadException, "Input is not valid JSON: '%s'" % self._generator.all()
|
||||
|
||||
def _readTrue(self):
|
||||
self._assertNext('t', "true")
|
||||
self._assertNext('r', "true")
|
||||
self._assertNext('u', "true")
|
||||
self._assertNext('e', "true")
|
||||
return True
|
||||
|
||||
def _readFalse(self):
|
||||
self._assertNext('f', "false")
|
||||
self._assertNext('a', "false")
|
||||
self._assertNext('l', "false")
|
||||
self._assertNext('s', "false")
|
||||
self._assertNext('e', "false")
|
||||
return False
|
||||
|
||||
def _readNull(self):
|
||||
self._assertNext('n', "null")
|
||||
self._assertNext('u', "null")
|
||||
self._assertNext('l', "null")
|
||||
self._assertNext('l', "null")
|
||||
return None
|
||||
|
||||
def _assertNext(self, ch, target):
|
||||
if self._next() != ch:
|
||||
raise ReadException, "Trying to read %s: '%s'" % (target, self._generator.all())
|
||||
|
||||
def _readNumber(self):
|
||||
isfloat = False
|
||||
result = self._next()
|
||||
peek = self._peek()
|
||||
while peek is not None and (peek.isdigit() or peek == "."):
|
||||
isfloat = isfloat or peek == "."
|
||||
result = result + self._next()
|
||||
peek = self._peek()
|
||||
try:
|
||||
if isfloat:
|
||||
return float(result)
|
||||
else:
|
||||
return int(result)
|
||||
except ValueError:
|
||||
raise ReadException, "Not a valid JSON number: '%s'" % result
|
||||
|
||||
def _readString(self):
|
||||
result = ""
|
||||
assert self._next() == '"'
|
||||
try:
|
||||
while self._peek() != '"':
|
||||
ch = self._next()
|
||||
if ch == "\\":
|
||||
ch = self._next()
|
||||
if ch in 'brnft':
|
||||
ch = self.escapes[ch]
|
||||
elif ch == "u":
|
||||
ch4096 = self._next()
|
||||
ch256 = self._next()
|
||||
ch16 = self._next()
|
||||
ch1 = self._next()
|
||||
n = 4096 * self._hexDigitToInt(ch4096)
|
||||
n += 256 * self._hexDigitToInt(ch256)
|
||||
n += 16 * self._hexDigitToInt(ch16)
|
||||
n += self._hexDigitToInt(ch1)
|
||||
ch = unichr(n)
|
||||
elif ch not in '"/\\':
|
||||
raise ReadException, "Not a valid escaped JSON character: '%s' in %s" % (ch, self._generator.all())
|
||||
result = result + ch
|
||||
except StopIteration:
|
||||
raise ReadException, "Not a valid JSON string: '%s'" % self._generator.all()
|
||||
assert self._next() == '"'
|
||||
return result
|
||||
|
||||
def _hexDigitToInt(self, ch):
|
||||
try:
|
||||
result = self.hex_digits[ch.upper()]
|
||||
except KeyError:
|
||||
try:
|
||||
result = int(ch)
|
||||
except ValueError:
|
||||
raise ReadException, "The character %s is not a hex digit." % ch
|
||||
return result
|
||||
|
||||
def _readComment(self):
|
||||
assert self._next() == "/"
|
||||
second = self._next()
|
||||
if second == "/":
|
||||
self._readDoubleSolidusComment()
|
||||
elif second == '*':
|
||||
self._readCStyleComment()
|
||||
else:
|
||||
raise ReadException, "Not a valid JSON comment: %s" % self._generator.all()
|
||||
|
||||
def _readCStyleComment(self):
|
||||
try:
|
||||
done = False
|
||||
while not done:
|
||||
ch = self._next()
|
||||
done = (ch == "*" and self._peek() == "/")
|
||||
if not done and ch == "/" and self._peek() == "*":
|
||||
raise ReadException, "Not a valid JSON comment: %s, '/*' cannot be embedded in the comment." % self._generator.all()
|
||||
self._next()
|
||||
except StopIteration:
|
||||
raise ReadException, "Not a valid JSON comment: %s, expected */" % self._generator.all()
|
||||
|
||||
def _readDoubleSolidusComment(self):
|
||||
try:
|
||||
ch = self._next()
|
||||
while ch != "\r" and ch != "\n":
|
||||
ch = self._next()
|
||||
except StopIteration:
|
||||
pass
|
||||
|
||||
def _readArray(self):
|
||||
result = []
|
||||
assert self._next() == '['
|
||||
done = self._peek() == ']'
|
||||
while not done:
|
||||
item = self._read()
|
||||
result.append(item)
|
||||
self._eatWhitespace()
|
||||
done = self._peek() == ']'
|
||||
if not done:
|
||||
ch = self._next()
|
||||
if ch != ",":
|
||||
raise ReadException, "Not a valid JSON array: '%s' due to: '%s'" % (self._generator.all(), ch)
|
||||
assert ']' == self._next()
|
||||
return result
|
||||
|
||||
def _readObject(self):
|
||||
result = {}
|
||||
assert self._next() == '{'
|
||||
done = self._peek() == '}'
|
||||
while not done:
|
||||
key = self._read()
|
||||
if type(key) is not types.StringType:
|
||||
raise ReadException, "Not a valid JSON object key (should be a string): %s" % key
|
||||
self._eatWhitespace()
|
||||
ch = self._next()
|
||||
if ch != ":":
|
||||
raise ReadException, "Not a valid JSON object: '%s' due to: '%s'" % (self._generator.all(), ch)
|
||||
self._eatWhitespace()
|
||||
val = self._read()
|
||||
result[key] = val
|
||||
self._eatWhitespace()
|
||||
done = self._peek() == '}'
|
||||
if not done:
|
||||
ch = self._next()
|
||||
if ch != ",":
|
||||
raise ReadException, "Not a valid JSON array: '%s' due to: '%s'" % (self._generator.all(), ch)
|
||||
assert self._next() == "}"
|
||||
return result
|
||||
|
||||
def _eatWhitespace(self):
|
||||
p = self._peek()
|
||||
while p is not None and p in string.whitespace or p == '/':
|
||||
if p == '/':
|
||||
self._readComment()
|
||||
else:
|
||||
self._next()
|
||||
p = self._peek()
|
||||
|
||||
def _peek(self):
|
||||
return self._generator.peek()
|
||||
|
||||
def _next(self):
|
||||
return self._generator.next()
|
||||
|
||||
class JsonWriter(object):
|
||||
|
||||
def _append(self, s):
|
||||
self._results.append(s)
|
||||
|
||||
def write(self, obj, escaped_forward_slash=False):
|
||||
self._escaped_forward_slash = escaped_forward_slash
|
||||
self._results = []
|
||||
self._write(obj)
|
||||
return "".join(self._results)
|
||||
|
||||
def _write(self, obj):
|
||||
ty = type(obj)
|
||||
if ty is types.DictType:
|
||||
n = len(obj)
|
||||
self._append("{")
|
||||
for k, v in obj.items():
|
||||
self._write(k)
|
||||
self._append(":")
|
||||
self._write(v)
|
||||
n = n - 1
|
||||
if n > 0:
|
||||
self._append(",")
|
||||
self._append("}")
|
||||
elif ty is types.ListType or ty is types.TupleType:
|
||||
n = len(obj)
|
||||
self._append("[")
|
||||
for item in obj:
|
||||
self._write(item)
|
||||
n = n - 1
|
||||
if n > 0:
|
||||
self._append(",")
|
||||
self._append("]")
|
||||
elif ty is types.StringType or ty is types.UnicodeType:
|
||||
self._append('"')
|
||||
obj = obj.replace('\\', r'\\')
|
||||
if self._escaped_forward_slash:
|
||||
obj = obj.replace('/', r'\/')
|
||||
obj = obj.replace('"', r'\"')
|
||||
obj = obj.replace('\b', r'\b')
|
||||
obj = obj.replace('\f', r'\f')
|
||||
obj = obj.replace('\n', r'\n')
|
||||
obj = obj.replace('\r', r'\r')
|
||||
obj = obj.replace('\t', r'\t')
|
||||
self._append(obj)
|
||||
self._append('"')
|
||||
elif ty is types.IntType or ty is types.LongType:
|
||||
self._append(str(obj))
|
||||
elif ty is types.FloatType:
|
||||
self._append("%f" % obj)
|
||||
elif obj is True:
|
||||
self._append("true")
|
||||
elif obj is False:
|
||||
self._append("false")
|
||||
elif obj is None:
|
||||
self._append("null")
|
||||
else:
|
||||
raise WriteException, "Cannot write in JSON: %s" % repr(obj)
|
||||
|
||||
def write(obj, escaped_forward_slash=False):
|
||||
return JsonWriter().write(obj, escaped_forward_slash)
|
||||
|
||||
def read(s):
|
||||
return JsonReader().read(s)
|
||||
3721
py/test/rsession/webdata/source.js
Normal file
3721
py/test/rsession/webdata/source.js
Normal file
File diff suppressed because it is too large
Load Diff
304
py/test/rsession/webjs.py
Normal file
304
py/test/rsession/webjs.py
Normal file
@@ -0,0 +1,304 @@
|
||||
|
||||
""" javascript source for py.test distributed
|
||||
"""
|
||||
|
||||
import py
|
||||
from py.__.test.rsession.web import exported_methods
|
||||
try:
|
||||
from pypy.translator.js.modules import dom
|
||||
from pypy.translator.js.helper import __show_traceback
|
||||
from pypy.translator.transformer.debug import traceback_handler
|
||||
except ImportError:
|
||||
py.test.skip("PyPy not found")
|
||||
|
||||
def create_elem(s):
|
||||
return dom.document.createElement(s)
|
||||
|
||||
def get_elem(el):
|
||||
return dom.document.getElementById(el)
|
||||
|
||||
def create_text_elem(txt):
|
||||
return dom.document.createTextNode(txt)
|
||||
|
||||
tracebacks = {}
|
||||
skips = {}
|
||||
counters = {}
|
||||
max_items = {}
|
||||
short_item_names = {}
|
||||
|
||||
MAX_COUNTER = 50 # Maximal size of one-line table
|
||||
|
||||
class Globals(object):
|
||||
def __init__(self):
|
||||
self.pending = []
|
||||
self.host = ""
|
||||
self.data_empty = True
|
||||
|
||||
glob = Globals()
|
||||
|
||||
def comeback(msglist):
|
||||
if len(msglist) == 0:
|
||||
return
|
||||
for item in glob.pending[:]:
|
||||
if not process(item):
|
||||
return
|
||||
glob.pending = []
|
||||
for msg in msglist:
|
||||
if not process(msg):
|
||||
return
|
||||
exported_methods.show_all_statuses(glob.sessid, comeback)
|
||||
|
||||
def show_info(data="aa"):
|
||||
info = dom.document.getElementById("info")
|
||||
info.style.visibility = "visible"
|
||||
while len(info.childNodes):
|
||||
info.removeChild(info.childNodes[0])
|
||||
txt = create_text_elem(data)
|
||||
info.appendChild(txt)
|
||||
info.style.backgroundColor = "beige"
|
||||
# XXX: Need guido
|
||||
|
||||
def hide_info():
|
||||
info = dom.document.getElementById("info")
|
||||
info.style.visibility = "hidden"
|
||||
|
||||
SCROLL_LINES = 50
|
||||
|
||||
def scroll_down_if_needed(mbox):
|
||||
if dom.window.scrollMaxY - dom.window.scrollY < SCROLL_LINES:
|
||||
mbox.parentNode.scrollIntoView()
|
||||
|
||||
def hide_messagebox():
|
||||
mbox = dom.document.getElementById("messagebox")
|
||||
while mbox.childNodes:
|
||||
mbox.removeChild(mbox.childNodes[0])
|
||||
|
||||
def make_module_box(msg):
|
||||
tr = create_elem("tr")
|
||||
td = create_elem("td")
|
||||
tr.appendChild(td)
|
||||
td.appendChild(create_text_elem("%s[0/%s]" % (msg['itemname'],
|
||||
msg['length'])))
|
||||
max_items[msg['fullitemname']] = int(msg['length'])
|
||||
short_item_names[msg['fullitemname']] = msg['itemname']
|
||||
td.id = '_txt_' + msg['fullitemname']
|
||||
#tr.setAttribute("id", msg['fullitemname'])
|
||||
td.setAttribute("onmouseover",
|
||||
"show_info('%s')" % (msg['fullitemname'],))
|
||||
td.setAttribute("onmouseout", "hide_info()")
|
||||
td2 = create_elem('td')
|
||||
tr.appendChild(td2)
|
||||
table = create_elem("table")
|
||||
td2.appendChild(table)
|
||||
tbody = create_elem('tbody')
|
||||
tbody.id = msg['fullitemname']
|
||||
table.appendChild(tbody)
|
||||
counters[msg['fullitemname']] = 0
|
||||
return tr
|
||||
|
||||
def add_received_item_outcome(msg, module_part):
|
||||
if msg['hostkey']:
|
||||
host_elem = dom.document.getElementById(msg['hostkey'])
|
||||
glob.host_pending[msg['hostkey']].pop()
|
||||
count = len(glob.host_pending[msg['hostkey']])
|
||||
host_elem.childNodes[0].nodeValue = '%s[%s]' % (
|
||||
glob.host_dict[msg['hostkey']], count)
|
||||
|
||||
td = create_elem("td")
|
||||
td.setAttribute("onmouseover", "show_info('%s')" % (
|
||||
msg['fullitemname'],))
|
||||
td.setAttribute("onmouseout", "hide_info()")
|
||||
item_name = msg['fullitemname']
|
||||
# TODO: dispatch output
|
||||
if msg["passed"] == 'True':
|
||||
txt = create_text_elem(".")
|
||||
td.appendChild(txt)
|
||||
elif msg["skipped"] != 'None' and msg["skipped"] != "False":
|
||||
exported_methods.show_skip(item_name, skip_come_back)
|
||||
link = create_elem("a")
|
||||
link.setAttribute("href", "javascript:show_skip('%s')" % (
|
||||
msg['fullitemname'],))
|
||||
txt = create_text_elem('s')
|
||||
link.appendChild(txt)
|
||||
td.appendChild(link)
|
||||
else:
|
||||
link = create_elem("a")
|
||||
link.setAttribute("href", "javascript:show_traceback('%s')" % (
|
||||
msg['fullitemname'],))
|
||||
txt = create_text_elem('F')
|
||||
link.appendChild(txt)
|
||||
td.appendChild(link)
|
||||
exported_methods.show_fail(item_name, fail_come_back)
|
||||
|
||||
if counters[msg['fullmodulename']] % MAX_COUNTER == 0:
|
||||
tr = create_elem("tr")
|
||||
module_part.appendChild(tr)
|
||||
|
||||
name = msg['fullmodulename']
|
||||
counters[name] += 1
|
||||
counter_part = get_elem('_txt_' + name)
|
||||
newcontent = "%s[%d/%d]" % (short_item_names[name], counters[name],
|
||||
max_items[name])
|
||||
counter_part.childNodes[0].nodeValue = newcontent
|
||||
module_part.childNodes[-1].appendChild(td)
|
||||
|
||||
def process(msg):
|
||||
if len(msg) == 0:
|
||||
return False
|
||||
elem = dom.document.getElementById("testmain")
|
||||
#elem.innerHTML += '%s<br/>' % msg['event']
|
||||
main_t = dom.document.getElementById("main_table")
|
||||
if msg['type'] == 'ItemStart':
|
||||
# we start a new directory or what
|
||||
#if msg['itemtype'] == 'Module':
|
||||
tr = make_module_box(msg)
|
||||
main_t.appendChild(tr)
|
||||
elif msg['type'] == 'SendItem':
|
||||
host_elem = dom.document.getElementById(msg['hostkey'])
|
||||
glob.host_pending[msg['hostkey']].insert(0, msg['fullitemname'])
|
||||
count = len(glob.host_pending[msg['hostkey']])
|
||||
host_elem.childNodes[0].nodeValue = '%s[%s]' % (
|
||||
glob.host_dict[msg['hostkey']], count)
|
||||
|
||||
elif msg['type'] == 'HostReady':
|
||||
host_elem = dom.document.getElementById(msg['hostkey'])
|
||||
host_elem.style.background = \
|
||||
"#00ff00"
|
||||
host_elem.childNodes[0].nodeValue = '%s[0]' % (
|
||||
glob.host_dict[msg['hostkey']],)
|
||||
elif msg['type'] == 'ReceivedItemOutcome':
|
||||
module_part = get_elem(msg['fullmodulename'])
|
||||
if not module_part:
|
||||
glob.pending.append(msg)
|
||||
return True
|
||||
|
||||
add_received_item_outcome(msg, module_part)
|
||||
elif msg['type'] == 'TestFinished':
|
||||
text = "FINISHED %s run, %s failures, %s skipped" % (msg['run'], msg['fails'], msg['skips'])
|
||||
dom.document.title = "Py.test %s" % text
|
||||
dom.document.getElementById("Tests").childNodes[0].nodeValue = \
|
||||
"Tests [%s]" % text
|
||||
elif msg['type'] == 'FailedTryiter':
|
||||
module_part = get_elem(msg['fullitemname'])
|
||||
if not module_part:
|
||||
glob.pending.append(msg)
|
||||
return True
|
||||
tr = create_elem("tr")
|
||||
td = create_elem("td")
|
||||
txt = create_text_elem("- FAILED TO LOAD MODULE")
|
||||
td.appendChild(txt)
|
||||
tr.appendChild(td)
|
||||
module_part.appendChild(tr)
|
||||
elif msg['type'] == 'SkippedTryiter':
|
||||
module_part = get_elem(msg['fullitemname'])
|
||||
if not module_part:
|
||||
glob.pending.append(msg)
|
||||
return True
|
||||
tr = create_elem("tr")
|
||||
td = create_elem("td")
|
||||
txt = create_text_elem("- skipped (%s)" % (msg['reason'],))
|
||||
td.appendChild(txt)
|
||||
tr.appendChild(td)
|
||||
module_part.appendChild(tr)
|
||||
elif msg['type'] == 'RsyncFinished':
|
||||
glob.rsync_done = True
|
||||
if glob.data_empty:
|
||||
mbox = dom.document.getElementById('messagebox')
|
||||
scroll_down_if_needed(mbox)
|
||||
return True
|
||||
|
||||
def show_skip(item_name="aa"):
|
||||
set_msgbox(item_name, skips[item_name])
|
||||
|
||||
def set_msgbox(item_name, data):
|
||||
msgbox = get_elem("messagebox")
|
||||
while len(msgbox.childNodes):
|
||||
msgbox.removeChild(msgbox.childNodes[0])
|
||||
pre = create_elem("pre")
|
||||
txt = create_text_elem(item_name + "\n" + data)
|
||||
pre.appendChild(txt)
|
||||
msgbox.appendChild(pre)
|
||||
dom.document.location = "#message"
|
||||
glob.data_empty = False
|
||||
|
||||
def show_traceback(item_name="aa"):
|
||||
data = ("====== Traceback: =========\n%s\n======== Stdout: ========\n%s\n"
|
||||
"========== Stderr: ==========\n%s\n" % tracebacks[item_name])
|
||||
set_msgbox(item_name, data)
|
||||
|
||||
def fail_come_back(msg):
|
||||
tracebacks[msg['item_name']] = (msg['traceback'], msg['stdout'],
|
||||
msg['stderr'])
|
||||
|
||||
def skip_come_back(msg):
|
||||
skips[msg['item_name']] = msg['reason']
|
||||
|
||||
def reshow_host():
|
||||
if glob.host == "":
|
||||
return
|
||||
show_host(glob.host)
|
||||
|
||||
def show_host(host_name="aa"):
|
||||
elem = dom.document.getElementById("jobs")
|
||||
if elem.childNodes:
|
||||
elem.removeChild(elem.childNodes[0])
|
||||
tbody = create_elem("tbody")
|
||||
for item in glob.host_pending[host_name]:
|
||||
tr = create_elem("tr")
|
||||
td = create_elem("td")
|
||||
td.appendChild(create_text_elem(item))
|
||||
tr.appendChild(td)
|
||||
tbody.appendChild(tr)
|
||||
elem.appendChild(tbody)
|
||||
elem.style.visibility = "visible"
|
||||
glob.host = host_name
|
||||
dom.setTimeout(reshow_host, 100)
|
||||
|
||||
def hide_host():
|
||||
elem = dom.document.getElementById("jobs")
|
||||
while len(elem.childNodes):
|
||||
elem.removeChild(elem.childNodes[0])
|
||||
elem.style.visibility = "hidden"
|
||||
glob.host = ""
|
||||
|
||||
def update_rsync():
|
||||
elem = dom.document.getElementById("Tests")
|
||||
if glob.rsync_done is True:
|
||||
elem.childNodes[0].nodeValue = "Tests"
|
||||
return
|
||||
text = "Rsyncing" + '.' * glob.rsync_dots
|
||||
glob.rsync_dots += 1
|
||||
if glob.rsync_dots > 5:
|
||||
glob.rsync_dots = 0
|
||||
elem.childNodes[0].nodeValue = "Tests [%s]" % text
|
||||
dom.setTimeout(update_rsync, 1000)
|
||||
|
||||
def host_init(host_dict):
|
||||
tbody = dom.document.getElementById("hostsbody")
|
||||
for host in host_dict.keys():
|
||||
tr = create_elem('tr')
|
||||
tbody.appendChild(tr)
|
||||
td = create_elem("td")
|
||||
td.style.background = "#ff0000"
|
||||
txt = create_text_elem(host_dict[host])
|
||||
td.appendChild(txt)
|
||||
td.id = host
|
||||
tr.appendChild(td)
|
||||
td.setAttribute("onmouseover", "show_host('%s')" % host)
|
||||
td.setAttribute("onmouseout", "hide_host()")
|
||||
glob.rsync_dots = 0
|
||||
glob.rsync_done = False
|
||||
dom.setTimeout(update_rsync, 1000)
|
||||
glob.host_dict = host_dict
|
||||
glob.host_pending = {}
|
||||
for key in host_dict.keys():
|
||||
glob.host_pending[key] = []
|
||||
|
||||
def sessid_comeback(id):
|
||||
glob.sessid = id
|
||||
exported_methods.show_all_statuses(id, comeback)
|
||||
|
||||
def main():
|
||||
exported_methods.show_hosts(host_init)
|
||||
exported_methods.show_sessid(sessid_comeback)
|
||||
|
||||
Reference in New Issue
Block a user