[Zope-CVS] CVS: Packages/autotester - README.txt:1.1 autotest.py:1.1 autotest.xml:1.1 zconfig.conf:1.1 zope.conf:1.1
Fred L. Drake, Jr.
fred@zope.com
Thu, 3 Jul 2003 17:51:04 -0400
Update of /cvs-repository/Packages/autotester
In directory cvs.zope.org:/tmp/cvs-serv25630
Added Files:
README.txt autotest.py autotest.xml zconfig.conf zope.conf
Log Message:
Simple autotester micro-app.
=== Added File Packages/autotester/README.txt ===
=====================
Automated Test Runner
=====================
This is a very simple automated test runner suitable for use with
cron. The tester uses a ZConfig-based configuration file to determine
what code needs to be checked out, how to build it, how to run the
tests, and where to send email reporting the results.
Requirements
------------
This script requires Python, the logging package (available as part of
the standard library starting with Python 2.3), and ZConfig (see
http://www.zope.org/Members/fdrake/zconfig/ for more information).
Documentation
-------------
There will be some.
What's Here
-----------
autotest.py
The test runner.
autotest.xml
Configuration schema for ZConfig.
zconfig.conf
Sample configuration file that runs the tests for ZConfig.
zope.conf
More elaborate example that runs the Zope 2 and Zope 3 unit tests,
and the Zope 3 functional tests.
Credits
-------
This code was initially written by Jeremy Hylton, and has been further
munged by Fred Drake.
=== Added File Packages/autotester/autotest.py ===
"""Simple script to run checkout, build, and run tests."""
msg_body = """\
Email automatically generated by a daily test runner.
"""
import getopt
import logging
import os
import re
import shutil
import smtplib
import sys
import tempfile
from distutils.util import get_platform
import ZConfig
TESTING = False
class TestEmailHandler(logging.Handler):
"""Collect logging events to send in email.
Email is sent when the email() method is called.
"""
def __init__(self, config, begin=None):
logging.Handler.__init__(self)
self.smtp_server = config.smtp_server
self.sender = config.sender
self.from_ = config.from_
self.recipients = config.to
self.subject = config.subject
self.buffer = []
if begin:
self.buffer.append(begin)
def emit(self, rec):
self.buffer.append(rec.getMessage())
def get_status(self):
# Assume the tests were run by unittest which prints a
# summary like
# "OK"
# or
# "FAILED (errors=7)"
# as the last line of output.
return self.buffer[-1]
def email(self):
status = self.get_status()
subject = "%s: %s" % (status, self.subject)
body = "\n".join(self.buffer)
self.buffer = []
recipients = ",\n ".join(self.recipients)
msg = ("From: %s\r\n"
"To: %s\r\n"
"Subject: %s\r\n"
"\r\n"
"%s" % (self.from_, recipients, subject, body))
if self.sender:
msg = ("Sender: %s\r\n" % self.sender) + msg
server, port = self.smtp_server
port = port or 0
if not TESTING:
s = smtplib.SMTP(server, port)
s.sendmail(self.sender or self.from_, self.recipients, msg)
s.quit()
else:
print '-'*78
print msg
print '-'*78
class Test:
def __init__(self, config):
self.config = config
self.python = config.python
self.configLog()
def configLog(self):
self.logger = logging.getLogger("autotest")
self.logger.setLevel(logging.INFO)
if TESTING:
fh = logging.StreamHandler(sys.stdout)
elif self.config.log:
fh = logging.FileHandler(self.config.log)
self.logger.addHandler(fh)
def execute(self, cmd, logger=None):
"""Run a command, logging stdout and stderr."""
if logger is None:
logger = self.logger
logger.info(cmd)
fin, fout = os.popen4(cmd)
fin.close()
for line in fout:
line = line.rstrip()
# XXX hack to deal with debug build of Python
if re.match("\[\d+ refs\]", line):
continue
logger.info(line)
err = fout.close()
if err is not None:
raise ValueError, err
def checkout(self, checkout):
self.chdir(self.rootdir)
options = []
if checkout.tag:
options.append("-r %s" % checkout.tag)
if checkout.dest:
options.append("-d %s" % checkout.dest)
options = " ".join(options)
if options:
cmd = "cvs -d%%(repository)s co %s %%(module)s" % options
else:
cmd = "cvs -d%(repository)s co %(module)s"
self.execute(cmd % checkout.__dict__)
def build(self, checkout):
dir = checkout.dest or checkout.module
self.chdir(os.path.join(self.rootdir, dir))
self.execute("%s -u setup.py %s" % (self.python, checkout.setup))
def get_platspec(self):
"""Return self.python and find out what version it is."""
cmd = self.python + ' -c "import sys; print sys.version[0:3]"'
self.logger.info(cmd)
f = os.popen(cmd)
s = f.read().strip()
f.close()
return "%s-%s" % (get_platform(), s)
def test(self, test):
if test.log:
logger = logging.getLogger("autotest.test")
fh = logging.FileHandler(test.log)
logger.addHandler(fh)
else:
logger = None
self.chdir(os.path.join(self.rootdir, test.dir))
path = []
for dir in test.pythonpath:
path.append(os.path.join(self.rootdir, dir))
if test.pythonpath_build:
platspec = self.get_platspec()
for dir in test.pythonpath_build:
build_dir = os.path.join(dir, "build", "lib.%s" % platspec)
path.append(os.path.join(self.rootdir, build_dir))
path = "PYTHONPATH=" + os.pathsep.join(path)
self.execute("%s %s -u %s" % (path, self.python, test.script),
logger)
def chdir(self, dir):
self.logger.info("cd %s" % dir)
os.chdir(dir)
def go(self):
prev_dir = os.getcwd()
self.rootdir = tempfile.mkdtemp(prefix="autotest")
self.logger.info("running in %s" % self.rootdir)
self.chdir(self.rootdir)
for checkout in self.config.checkouts:
self.checkout(checkout)
if checkout.post:
self.execute(checkout.post)
self.build(checkout)
for test in self.config.tests:
email_config = merge_email(self.config.email, test.email)
eh = TestEmailHandler(email_config, msg_body)
self.logger.addHandler(eh)
self.test(test)
eh.email()
self.logger.removeHandler(eh)
self.chdir(prev_dir)
self.logger.info("deleting %s" % self.rootdir)
shutil.rmtree(self.rootdir)
def merge_email(source, dest):
if dest is None:
dest = source
else:
# Update dest with information from source:
if dest.smtp_server is None:
dest.smtp_server = source.smtp_server
if dest.sender is None:
dest.sender = source.sender
if dest.from_ is None:
dest.from_ = source.from_
if not dest.to:
dest.to = source.to
if dest.subject is None:
dest.subject = source.subject
# Check that we have required values:
if not dest.smtp_server:
dest.smtp_server = "localhost", 0
if not dest.from_:
raise ZConfig.ConfigurationError("email from address is required")
if not dest.to:
raise ZConfig.ConfigurationError("email to address is required")
if not dest.subject:
dest.subject = "Test Result"
return dest
def main(args=None):
global TESTING
if args is None:
args = sys.argv[1:]
opts, args = getopt.getopt(args, "t", ["testing"])
for opt, val in opts:
if opt in ("-t", "--testing"):
TESTING = True
if len(args) > 1:
print >>sys.stderr, "Too many command line arguments!"
sys.exit(2)
if args:
configpath = args[0]
default_logfile = os.path.splitext(configpath)[0] + ".log"
else:
configpath = "autotest.conf"
default_logfile = "autotest.log"
schemapath = os.path.join(os.path.dirname(os.path.realpath(sys.argv[0])),
"autotest.xml")
schema = ZConfig.loadSchema(schemapath)
config, _ = ZConfig.loadConfig(schema, configpath)
for u in config.units:
if not u.log:
u.log = default_logfile
u.email = merge_email(config.email, u.email)
Test(u).go()
if __name__ == "__main__":
main()
=== Added File Packages/autotester/autotest.xml ===
<schema>
<sectiontype name="email">
<key name="smtp-server" required="no" default="localhost"
datatype="inet-address" />
<key name="from" required="no" attribute="from_" />
<key name="sender" required="no" />
<multikey name="to" required="no" />
<key name="subject" required="no" default="" />
</sectiontype>
<sectiontype name="checkout">
<key name="repository" required="yes" />
<key name="module" required="yes" />
<key name="tag" required="no" default="" />
<key name="dest" required="no" default="" />
<key name="setup" required="no" default="build" />
<key name="post" required="no" default="" />
</sectiontype>
<sectiontype name="test">
<key name="dir" required="yes" />
<key name="script" required="yes" />
<key name="log" required="no" />
<multikey name="pythonpath" required="no" />
<multikey name="pythonpath-build" required="no" />
<section name="*" type="email" attribute="email" />
</sectiontype>
<sectiontype name="unit">
<key name="python" default="/usr/local/bin/python" required="no" />
<key name="log" datatype="existing-dirpath" />
<section name="*" attribute="email" type="email" />
<multisection name="*" attribute="checkouts" type="checkout"
required="yes" />
<multisection name="*" attribute="tests" type="test"
required="yes" />
</sectiontype>
<section name="*" type="email" attribute="email" />
<multisection name="*" type="unit" attribute="units" />
</schema>
=== Added File Packages/autotester/zconfig.conf ===
# This is an example configuration file.
# It checks out the ZConfig implementation and runs it's unit tests.
<email>
# change these to something realistic:
from me@example.com
to you@example.com
</email>
<unit>
python /usr/local/bin/python2.1
<email>
subject ZConfig unit test results
</email>
<checkout>
repository :pserver:anonymous@cvs.zope.org:/cvs-repository
module StandaloneZConfig
dest ZConfig-turnk
</checkout>
<test>
dir ZConfig-turnk/ZConfig/tests
pythonpath-build ../..
script runtests.py
</test>
</unit>
=== Added File Packages/autotester/zope.conf ===
<email>
from test-runner@example.com
</email>
<unit Zope2>
python /usr/local/bin/python2.2
<email>
subject Zope 2 tests on Linux
to zope-coders@zope.org
</email>
<checkout>
repository :ext:cvs.zope.org:/cvs-repository
module Zope
setup build_ext -i
</checkout>
<test>
dir Zope
pythonpath lib/python
script test.py
</test>
</unit>
<unit Zope3>
python /usr/local/bin/python2.2
<email>
to zope3-dev@zope.org
</email>
<checkout>
repository :ext:cvs.zope.org:/cvs-repository
module Zope3
setup build_ext -i
</checkout>
<test>
<email>
subject Zope 3 unit tests on Linux
</email>
dir Zope3
pythonpath src
script test.py
</test>
<test>
<email>
subject Zope 3 functional tests on Linux
</email>
dir Zope3
pythonpath src
script test.py -f
</test>
</unit>