2007-01-24 22:24:01 +08:00
|
|
|
|
|
|
|
""" Remote session base class
|
|
|
|
"""
|
|
|
|
|
|
|
|
import os
|
|
|
|
import py
|
|
|
|
import sys
|
|
|
|
import re
|
|
|
|
import time
|
|
|
|
|
|
|
|
from py.__.test.rsession import report
|
2007-02-05 07:12:12 +08:00
|
|
|
from py.__.test.rsession.master import MasterNode, dispatch_loop, itemgen
|
2007-02-04 22:05:01 +08:00
|
|
|
from py.__.test.rsession.hostmanage import HostInfo, HostManager
|
2007-01-24 22:24:01 +08:00
|
|
|
from py.__.test.rsession.local import local_loop, plain_runner, apigen_runner,\
|
2007-01-25 00:46:46 +08:00
|
|
|
box_runner
|
2007-01-24 22:24:01 +08:00
|
|
|
from py.__.test.rsession.reporter import LocalReporter, RemoteReporter
|
2007-01-28 21:25:03 +08:00
|
|
|
from py.__.test.session import Session
|
2007-02-03 20:14:46 +08:00
|
|
|
from py.__.test.outcome import Skipped, Failed
|
2007-01-24 22:24:01 +08:00
|
|
|
|
2007-01-28 21:25:03 +08:00
|
|
|
class AbstractSession(Session):
|
2007-01-24 22:24:01 +08:00
|
|
|
"""
|
|
|
|
An abstract session executes collectors/items through a runner.
|
|
|
|
|
|
|
|
"""
|
2007-01-28 21:25:03 +08:00
|
|
|
def fixoptions(self):
|
|
|
|
option = self.config.option
|
|
|
|
if option.runbrowser and not option.startserver:
|
|
|
|
#print "--runbrowser implies --startserver"
|
|
|
|
option.startserver = True
|
|
|
|
super(AbstractSession, self).fixoptions()
|
2007-01-24 22:24:01 +08:00
|
|
|
|
|
|
|
def init_reporter(self, reporter, sshhosts, reporter_class, arg=""):
|
2007-01-25 00:46:46 +08:00
|
|
|
""" This initialises so called `reporter` class, which will
|
|
|
|
handle all event presenting to user. Does not get called
|
|
|
|
if main received custom reporter
|
|
|
|
"""
|
2007-01-24 22:24:01 +08:00
|
|
|
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
|
2007-01-25 00:46:46 +08:00
|
|
|
if self.config.option.runbrowser:
|
|
|
|
from socket import INADDR_ANY
|
|
|
|
port = INADDR_ANY # pick a random port when starting browser
|
|
|
|
else:
|
|
|
|
port = 8000 # stick to a fixed port otherwise
|
2007-01-24 22:24:01 +08:00
|
|
|
|
|
|
|
reporter = exported_methods.report
|
2007-01-25 00:46:46 +08:00
|
|
|
httpd = start_server(server_address = ('', port))
|
|
|
|
port = httpd.server_port
|
2007-01-24 22:24:01 +08:00
|
|
|
if self.config.option.runbrowser:
|
2007-01-25 00:46:46 +08:00
|
|
|
import webbrowser, thread
|
|
|
|
# webbrowser.open() may block until the browser finishes or not
|
|
|
|
url = "http://localhost:%d" % (port,)
|
|
|
|
thread.start_new_thread(webbrowser.open, (url,))
|
2007-01-24 22:24:01 +08:00
|
|
|
elif reporter is None:
|
|
|
|
if restflag:
|
|
|
|
from py.__.test.rsession.rest import RestReporter
|
|
|
|
reporter_class = RestReporter
|
|
|
|
if arg:
|
2007-01-26 19:49:59 +08:00
|
|
|
reporter_instance = reporter_class(self.config, sshhosts)
|
2007-01-24 22:24:01 +08:00
|
|
|
else:
|
|
|
|
reporter_instance = reporter_class(self.config, sshhosts)
|
|
|
|
reporter = reporter_instance.report
|
|
|
|
else:
|
|
|
|
startserverflag = False
|
|
|
|
|
|
|
|
return reporter, startserverflag
|
|
|
|
|
|
|
|
def reporterror(reporter, data):
|
2007-02-05 07:21:35 +08:00
|
|
|
excinfo, item = data
|
|
|
|
if excinfo is None:
|
|
|
|
reporter(report.ItemStart(item))
|
|
|
|
elif excinfo.type is Skipped:
|
|
|
|
reporter(report.SkippedTryiter(excinfo, item))
|
|
|
|
else:
|
|
|
|
reporter(report.FailedTryiter(excinfo, item))
|
2007-01-24 22:24:01 +08:00
|
|
|
reporterror = staticmethod(reporterror)
|
|
|
|
|
|
|
|
def kill_server(self, startserverflag):
|
2007-01-25 00:46:46 +08:00
|
|
|
""" Kill web server
|
|
|
|
"""
|
2007-01-24 22:24:01 +08:00
|
|
|
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
|
|
|
|
|
|
|
|
class RSession(AbstractSession):
|
|
|
|
""" Remote version of session
|
|
|
|
"""
|
2007-01-25 05:05:33 +08:00
|
|
|
def fixoptions(self):
|
2007-01-28 21:25:03 +08:00
|
|
|
super(RSession, self).fixoptions()
|
2007-01-25 05:05:33 +08:00
|
|
|
config = self.config
|
|
|
|
try:
|
2007-01-29 05:54:12 +08:00
|
|
|
config.getvalue('dist_hosts')
|
2007-01-25 05:05:33 +08:00
|
|
|
except KeyError:
|
2007-01-29 05:54:12 +08:00
|
|
|
print "For running ad-hoc distributed tests you need to specify"
|
|
|
|
print "dist_hosts in a local conftest.py file, for example:"
|
2007-01-25 05:05:33 +08:00
|
|
|
print "for example:"
|
2007-01-29 05:54:12 +08:00
|
|
|
print
|
|
|
|
print " dist_hosts = ['localhost'] * 4 # for 3 processors"
|
|
|
|
print " dist_hosts = ['you@remote.com', '...'] # for testing on ssh accounts"
|
|
|
|
print " # with your remote ssh accounts"
|
|
|
|
print
|
|
|
|
print "see also: http://codespeak.net/py/current/doc/test.html#automated-distributed-testing"
|
2007-01-25 05:05:33 +08:00
|
|
|
raise SystemExit
|
|
|
|
|
2007-01-24 22:24:01 +08:00
|
|
|
def main(self, reporter=None):
|
|
|
|
""" main loop for running tests. """
|
2007-01-25 00:46:46 +08:00
|
|
|
args = self.config.args
|
2007-01-24 22:24:01 +08:00
|
|
|
|
2007-02-04 22:05:01 +08:00
|
|
|
sshhosts = self._getconfighosts()
|
2007-01-24 22:24:01 +08:00
|
|
|
reporter, startserverflag = self.init_reporter(reporter,
|
|
|
|
sshhosts, RemoteReporter)
|
|
|
|
reporter, checkfun = self.wrap_reporter(reporter)
|
|
|
|
|
|
|
|
reporter(report.TestStarted(sshhosts))
|
|
|
|
|
2007-02-04 22:05:01 +08:00
|
|
|
hostmanager = HostManager(sshhosts, self.config)
|
2007-01-24 22:24:01 +08:00
|
|
|
try:
|
2007-02-05 07:12:12 +08:00
|
|
|
nodes = hostmanager.init_hosts(reporter)
|
2007-01-25 00:46:46 +08:00
|
|
|
reporter(report.RsyncFinished())
|
|
|
|
try:
|
2007-02-05 07:12:12 +08:00
|
|
|
self.dispatch_tests(nodes, reporter, checkfun)
|
2007-01-25 00:46:46 +08:00
|
|
|
except (KeyboardInterrupt, SystemExit):
|
|
|
|
print >>sys.stderr, "C-c pressed waiting for gateways to teardown..."
|
|
|
|
channels = [node.channel for node in nodes]
|
|
|
|
hostmanager.kill_channels(channels)
|
|
|
|
hostmanager.teardown_gateways(reporter, channels)
|
|
|
|
print >>sys.stderr, "... Done"
|
|
|
|
raise
|
|
|
|
|
|
|
|
channels = [node.channel for node in nodes]
|
|
|
|
hostmanager.teardown_hosts(reporter, channels, nodes,
|
2007-01-24 22:24:01 +08:00
|
|
|
exitfirst=self.config.option.exitfirst)
|
2007-01-25 00:46:46 +08:00
|
|
|
reporter(report.Nodes(nodes))
|
|
|
|
retval = reporter(report.TestFinished())
|
|
|
|
self.kill_server(startserverflag)
|
|
|
|
return retval
|
|
|
|
except (KeyboardInterrupt, SystemExit):
|
|
|
|
reporter(report.InterruptedExecution())
|
|
|
|
self.kill_server(startserverflag)
|
|
|
|
raise
|
|
|
|
except:
|
|
|
|
reporter(report.CrashedExecution())
|
|
|
|
self.kill_server(startserverflag)
|
|
|
|
raise
|
2007-01-24 22:24:01 +08:00
|
|
|
|
2007-02-04 22:05:01 +08:00
|
|
|
def _getconfighosts(self):
|
|
|
|
return [HostInfo(spec) for spec in
|
|
|
|
self.config.getvalue("dist_hosts")]
|
2007-01-24 22:24:01 +08:00
|
|
|
|
2007-02-05 07:12:12 +08:00
|
|
|
def dispatch_tests(self, nodes, reporter, checkfun):
|
2007-01-26 19:49:59 +08:00
|
|
|
colitems = self.config.getcolitems()
|
2007-01-24 22:24:01 +08:00
|
|
|
keyword = self.config.option.keyword
|
|
|
|
itemgenerator = itemgen(colitems, reporter, keyword, self.reporterror)
|
|
|
|
|
|
|
|
all_tests = dispatch_loop(nodes, itemgenerator, checkfun)
|
|
|
|
|
|
|
|
class LSession(AbstractSession):
|
|
|
|
""" Local version of session
|
|
|
|
"""
|
|
|
|
def main(self, reporter=None, runner=None):
|
|
|
|
# check out if used options makes any sense
|
2007-01-25 00:46:46 +08:00
|
|
|
args = self.config.args
|
2007-01-24 22:24:01 +08:00
|
|
|
|
|
|
|
sshhosts = [HostInfo('localhost')] # this is just an info to reporter
|
|
|
|
|
|
|
|
if not self.config.option.nomagic:
|
|
|
|
py.magic.invoke(assertion=1)
|
|
|
|
|
|
|
|
reporter, startserverflag = self.init_reporter(reporter,
|
|
|
|
sshhosts, LocalReporter, args[0])
|
|
|
|
reporter, checkfun = self.wrap_reporter(reporter)
|
|
|
|
|
|
|
|
reporter(report.TestStarted(sshhosts))
|
2007-01-26 19:49:59 +08:00
|
|
|
colitems = self.config.getcolitems()
|
2007-01-24 22:24:01 +08:00
|
|
|
reporter(report.RsyncFinished())
|
|
|
|
|
|
|
|
if runner is None:
|
2007-01-27 21:01:58 +08:00
|
|
|
runner = self.init_runner()
|
2007-01-24 22:24:01 +08:00
|
|
|
|
|
|
|
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)
|
|
|
|
|
2007-01-27 21:01:58 +08:00
|
|
|
self.write_docs()
|
2007-01-24 22:24:01 +08:00
|
|
|
return retval
|
|
|
|
|
2007-01-27 21:01:58 +08:00
|
|
|
def write_docs(self):
|
2007-01-24 22:24:01 +08:00
|
|
|
if self.config.option.apigen:
|
|
|
|
from py.__.apigen.tracer.docstorage import DocStorageAccessor
|
|
|
|
apigen = py.path.local(self.config.option.apigen).pyimport()
|
2007-01-25 23:23:18 +08:00
|
|
|
if not hasattr(apigen, 'build'):
|
2007-01-29 04:25:11 +08:00
|
|
|
raise NotImplementedError("%s does not contain 'build' "
|
|
|
|
"function" %(apigen,))
|
2007-01-24 22:24:01 +08:00
|
|
|
print >>sys.stderr, 'building documentation'
|
2007-02-01 23:20:39 +08:00
|
|
|
capture = py.io.StdCaptureFD()
|
2007-01-24 22:24:01 +08:00
|
|
|
try:
|
2007-02-04 22:05:01 +08:00
|
|
|
pkgdir = py.path.local(self.config.args[0]).pypkgpath()
|
2007-01-29 20:53:52 +08:00
|
|
|
apigen.build(pkgdir,
|
2007-02-01 21:56:31 +08:00
|
|
|
DocStorageAccessor(self.docstorage),
|
|
|
|
capture)
|
2007-01-24 22:24:01 +08:00
|
|
|
finally:
|
|
|
|
capture.reset()
|
|
|
|
|
2007-01-27 21:01:58 +08:00
|
|
|
def init_runner(self):
|
2007-01-24 22:24:01 +08:00
|
|
|
if self.config.option.apigen:
|
|
|
|
from py.__.apigen.tracer.tracer import Tracer, DocStorage
|
2007-02-04 22:05:01 +08:00
|
|
|
pkgdir = py.path.local(self.config.args[0]).pypkgpath()
|
2007-01-30 18:34:08 +08:00
|
|
|
apigen = py.path.local(self.config.option.apigen).pyimport()
|
|
|
|
if not hasattr(apigen, 'get_documentable_items'):
|
2007-01-24 22:24:01 +08:00
|
|
|
raise NotImplementedError("Provided script does not seem "
|
|
|
|
"to contain get_documentable_items")
|
2007-01-30 18:34:08 +08:00
|
|
|
pkgname, items = apigen.get_documentable_items(pkgdir)
|
|
|
|
self.docstorage = DocStorage().from_dict(items,
|
|
|
|
module_name=pkgname)
|
2007-01-24 22:24:01 +08:00
|
|
|
self.tracer = Tracer(self.docstorage)
|
|
|
|
return apigen_runner
|
|
|
|
else:
|
2007-01-25 00:46:46 +08:00
|
|
|
if (self.config.getvalue('dist_boxing') or self.config.option.boxing)\
|
|
|
|
and not self.config.option.nocapture:
|
|
|
|
return box_runner
|
|
|
|
return plain_runner
|
|
|
|
|