[svn r37264] create the new development trunk

--HG--
branch : trunk
This commit is contained in:
hpk
2007-01-24 15:24:01 +01:00
commit 5992a8ef21
435 changed files with 58640 additions and 0 deletions

View File

@@ -0,0 +1 @@
#

106
py/test/rsession/box.py Normal file
View 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

View 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'),
)

View 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

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

View 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

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

View 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
View 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)

View 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
View 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()"

View 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
View 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)

View File

@@ -0,0 +1 @@
#

View File

@@ -0,0 +1,12 @@
def f1():
f2()
def f2():
pass
def g1():
g2()
def g2():
raise ValueError()

View 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

View 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

View 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']

View 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

View 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"

View 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()")

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

View 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__

View 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

View 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

View 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'

View 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}

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

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

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

View File

View 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">
&#xa0;
</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>

View 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)

File diff suppressed because it is too large Load Diff

304
py/test/rsession/webjs.py Normal file
View 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)