#!/usr/bin/python2.4
-# usage:
-# adt-run <options>... --- <virt-server> [<virt-server-arg>...]
#
-# invoke in toplevel of package (not necessarily built)
-# with package installed
-
-# exit status:
-# 0 all tests passed
-# 4 at least one test failed
-# 8 no tests in this package
-# 12 erroneous package
-# 16 testbed failure
-# 20 other unexpected failures including bad usage
+# adt-run is part of autodebtest
+# autodebtest is a tool for testing Debian binary packages
+#
+# autodebtest is Copyright (C) 2006 Canonical Ltd.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program 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 General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+#
+# See the file CREDITS for a full list of credits information (often
+# installed as /usr/share/doc/autodebtest/CREDITS).
import signal
import optparse
import urllib
import string
import re as regexp
+import os
+import errno
from optparse import OptionParser
tmpdir = None
testbed = None
+errorcode = 0
signal.signal(signal.SIGINT, signal.SIG_DFL) # undo stupid Python SIGINT thing
def bomb(m): raise Quit(20, "unexpected error: %s" % m)
def badpkg(m): raise Quit(12, "erroneous package: %s" % m)
+def report(tname, result): print '%-20s %s' % (tname, result)
class Unsupported:
def __init__(u, lno, m):
if lno >= 0: u.m = '%s (control line %d)' % (m, lno)
else: u.m = m
def report(u, tname):
- print '%-20s SKIP %s' % (tname, u.m)
+ global errorcode
+ errorcode != 2
+ report(tname, 'SKIP %s' % u.m)
def debug(m):
global opts
return reduce((lambda a,b: a + b), l, [])
class Path:
- def __init__(p, tb, path, what, dir=False):
+ def __init__(p, tb, path, what, dir=False, tbscratch=None):
p.tb = tb
p.p = path
p.what = what
p.dir = dir
+ p.tbscratch = tbscratch
if p.tb:
if p.p[:1] != '/':
bomb("path %s specified as being in testbed but"
" not absolute: `%s'" % (what, p.p))
p.local = None
+ p.down = p.p
else:
p.local = p.p
+ p.down = None
if p.dir: p.dirsfx = '/'
else: p.dirsfx = ''
def path(p):
return p.p + p.dirsfx
def append(p, suffix, what, dir=False):
- return Path(p.tb, p.path() + suffix, what=what, dir=dir)
+ return Path(p.tb, p.path() + suffix, what=what, dir=dir,
+ tbscratch=p.tbscratch)
def __str__(p):
if p.tb: pfx = '/VIRT'
elif p.p[:1] == '/': pfx = '/HOST'
else: pfx = './'
return pfx + p.p
- def onhost(p):
- if not p.tb: return p.p
- if p.local is not None: return p.local
+ def onhost(p, lpath = None):
+ if p.local is not None:
+ if lpath is not None: assert(p.local == lpath)
+ return p.local
testbed.open()
- p.local = tmpdir + '/tb.' + p.what
+ p.local = lpath
+ if p.local is None: p.local = tmpdir + '/tb-' + p.what
testbed.command('copyup', (p.path(), p.local + p.dirsfx))
return p.local
+ def ontb(p):
+ testbed.open()
+ if p.tbscratch is not None:
+ if p.tbscratch != testbed.scratch:
+ p.down = None
+ if p.down is not None: return p.down
+ if p.tb:
+ bomb("testbed scratch path " + str(p) + " survived testbed")
+ p.down = testbed.scratch.p + '/host-' + p.what
+ p.tbscratch = testbed.scratch
+ testbed.command('copydown', (p.path(), p.down + p.dirsfx))
+ return p.down
def parse_args():
global opts
pa_path('build-tree', True, 'use build tree from PATH on %s')
pa_path('control', False, 'read control file PATH on %s')
+ pa_path('output-dir', True, 'write stderr/out files in PATH on %s')
pa('-d', '--debug', action='store_true', dest='debug');
- pa('','--user', type='string',
- help='run tests as USER (needs root on testbed)')
+ # pa('','--user', type='string',
+ # help='run tests as USER (needs root on testbed)')
+ # nyi
class SpecialOption(optparse.Option): pass
vs_op = SpecialOption('','--VSERVER-DUMMY')
if tb.scratch is not None: return
p = tb.commandr1('open')
tb.scratch = Path(True, p, 'tb-scratch', dir=True)
+ tb.scratch.tbscratch = tb.scratch
def close(tb):
if tb.scratch is None: return
tb.scratch = None
+ if tb.sp is None: return
tb.command('close')
def bomb(tb, m):
if tb.sp is not None:
tb.sp = None
raise Quit(16, 'testbed failed: %s' % m)
def send(tb, string):
+ tb.sp.stdin
try:
debug('>> '+string)
print >>tb.sp.stdin, string
tb.sp.stdin.flush()
tb.lastsend = string
except:
- tb.bomb('cannot send to testbed: %s' %
- formatexception_only(sys.last_type, sys.last_value))
+ (type, value, dummy) = sys.exc_info()
+ tb.bomb('cannot send to testbed: %s' % traceback.
+ format_exception_only(type, value))
def expect(tb, keyword, nresults=-1):
l = tb.sp.stdout.readline()
if not l: tb.bomb('unexpected eof from the testbed')
class FieldBase:
def __init__(f, fname, stz, base, tnames, vl):
+ assert(vl)
f.stz = stz
f.base = base
f.tnames = tnames
r = map((lambda w: (lno, w)), r)
return r
return flatten(map(distribute, f.vl))
- def atmostone(f, default):
- if not vl:
- f.v = default
- f.lno = -1
- elif len(vl) == 1:
+ def atmostone(f):
+ if len(vl) == 1:
(f.lno, f.v) = vl[0]
else:
raise Unsupported(f.vl[1][0],
class Field_Tests_directory(FieldBase):
def parse(f):
- base['testsdir'] = oneonly(f)
+ td = atmostone(f)
+ if td.startswith('/'): raise Unspported(f.lno,
+ 'Tests-Directory may not be absolute')
+ base['testsdir'] = td
+
+def run_tests():
+ testbed.close()
+ for t in tests:
+ t.run()
+ if not tests:
+ global errorcode
+ report('*', 'SKIP no tests in this package')
+ errorcode |= 8
class Test:
def __init__(t, tname, base):
- t.tname = tname
+ if '/' in tname: raise Unsupported(base[' lno'],
+ 'test name may not contain / character')
for k in base: setattr(t,k,base[k])
+ t.tname = tname
+ if len(base['testsdir']): tpath = base['testsdir'] + '/' + tname
+ else: tpath = tname
+ t.p = opts.build_tree.append(tpath, 'test-'+tname)
+ def report(t, m):
+ report(t.tname, m)
+ def reportfail(t, m):
+ global errorcode
+ errorcode |= 4
+ report(t.tname, 'FAIL ' + m)
+ def run(t):
+ testbed.open()
+ def stdouterr(oe):
+ idstr = oe + '-' + t.tname
+ if opts.output_dir is not None and opts.output_dir.tb:
+ return opts.output_dir.append(idstr)
+ else:
+ return testbed.scratch.append(idstr, idstr)
+ def stdouterrh(p, oe):
+ idstr = oe + '-' + t.tname
+ if opts.output_dir is None or opts.output_dir.tb:
+ return p.onhost()
+ else:
+ return p.onhost(opts.output_dir.onhost() + '/' + idstr)
+ so = stdouterr('stdout')
+ se = stdouterr('stderr')
+ rc = testbed.commandr1('execute',(t.p.ontb(),
+ '/dev/null', so.ontb(), se.ontb()))
+ soh = stdouterrh(so, 'stdout')
+ soe = stdouterrh(se, 'stderr')
+ testbed.close()
+ rc = int(rc)
+ stab = os.stat(soh)
+ if stab.st_size != 0:
+ l = file(seh).readline()
+ l = l.rstrip('\n \t\r')
+ if len(l) > 40: l = l[:40] + '...'
+ t.reportfail('stderr: %s' % l)
+ elif rc != 0:
+ t.reportfail('non-zero exit status %d' % rc)
+ else:
+ t.report('PASS')
def read_control():
global tests
- control = file(opts.control.onhost(), 'r')
+ try:
+ control = file(opts.control.onhost(), 'r')
+ except IOError, oe:
+ if oe[0] != errno.ENOENT: raise
+ tests = []
+ return
lno = 0
def badctrl(m): testbed.badpkg('tests/control line %d: %s' % (lno, m))
stz = None # stz[field_name][index] = (lno, value)
'unknown metadata field %s' % fname)
f = fclass(stz, fname, base, tnames, vl)
f.parse()
+ tests = []
+ for tname in tnames:
+ t = Test(tname, base)
+ tests.append(t)
except Unsupported, u:
for tname in tnames: u.report(tname)
continue
- tests = []
- for tname in tnames:
- t = Test(tname, base)
- tests.append(t)
- testbed.close()
def print_exception(ei, msgprefix=''):
if msgprefix: print >>sys.stderr, msgprefix
except:
print_exception(sys.exc_info(),
'\nadt-run: error cleaning up:\n')
- sys.exit(20)
+ os._exit(20)
def main():
global testbed
global tmpdir
try:
parse_args()
+ except SystemExit, se:
+ os._exit(20)
+ try:
tmpdir = tempfile.mkdtemp()
testbed = Testbed()
testbed.start()
+ testbed.open()
+ testbed.close()
read_control()
+ run_tests()
except:
ec = print_exception(sys.exc_info(), '')
cleanup()
- sys.exit(ec)
+ os._exit(ec)
cleanup()
+ os._exit(errorcode)
main()