3 # adt-run <options>... --- <virt-server> [<virt-server-arg>...]
5 # invoke in toplevel of package (not necessarily built)
6 # with package installed
10 # 4 at least one test failed
11 # 8 no tests in this package
12 # 12 erroneous package
14 # 20 other unexpected failures including bad usage
26 from optparse import OptionParser
31 signal.signal(signal.SIGINT, signal.SIG_DFL) # undo stupid Python SIGINT thing
34 def __init__(q,ec,m): q.ec = ec; q.m = m
36 def bomb(m): raise Quit(20, "unexpected error: %s" % m)
37 def badpkg(m): raise Quit(12, "erroneous package: %s" % m)
40 def __init__(u, lno, m):
41 if lno >= 0: u.m = '%s (control line %d)' % (m, lno)
44 print '%-20s SKIP %s' % (tname, u.m)
48 if not opts.debug: return
49 print >>sys.stderr, 'atd-run: debug:', m
52 return reduce((lambda a,b: a + b), l, [])
55 def __init__(p, tb, path, what, dir=False):
62 bomb("path %s specified as being in testbed but"
63 " not absolute: `%s'" % (what, p.p))
67 if p.dir: p.dirsfx = '/'
71 def append(p, suffix, what, dir=False):
72 return Path(p.tb, p.path() + suffix, what=what, dir=dir)
74 if p.tb: pfx = '/VIRT'
75 elif p.p[:1] == '/': pfx = '/HOST'
79 if not p.tb: return p.p
80 if p.local is not None: return p.local
82 p.local = tmpdir + '/tb.' + p.what
83 testbed.command('copyup', (p.path(), p.local + p.dirsfx))
88 usage = "%prog <options> -- <virt-server>..."
89 parser = OptionParser(usage=usage)
90 pa = parser.add_option
91 pe = parser.add_option
93 def cb_vserv(op,optstr,value,parser):
94 parser.values.vserver = list(parser.rargs)
97 def cb_path(op,optstr,value,parser, long,tb,dir):
98 name = long.replace('-','_')
99 setattr(parser.values, name, Path(tb, value, long, dir))
101 def pa_path(long, dir, help):
102 def papa_tb(long, ca, pahelp):
103 pa('', long, action='callback', callback=cb_path,
104 nargs=1, type='string', callback_args=ca,
105 help=(help % pahelp), metavar='PATH')
106 papa_tb('--'+long, (long, False, dir), 'host')
107 papa_tb('--'+long+'-tb',(long, True, dir), 'testbed')
109 pa_path('build-tree', True, 'use build tree from PATH on %s')
110 pa_path('control', False, 'read control file PATH on %s')
112 pa('-d', '--debug', action='store_true', dest='debug');
113 pa('','--user', type='string',
114 help='run tests as USER (needs root on testbed)')
116 class SpecialOption(optparse.Option): pass
117 vs_op = SpecialOption('','--VSERVER-DUMMY')
118 vs_op.action = 'callback'
122 vs_op.callback = cb_vserv
123 vs_op.callback_args = ( )
124 vs_op.callback_kwargs = { }
125 vs_op.help = 'introduces virtualisation server and args'
126 vs_op._short_opts = []
127 #vs_op._long_opts = ['--DUMMY']
128 vs_op._long_opts = ['---']
132 (opts,args) = parser.parse_args()
133 if not hasattr(opts,'vserver'):
134 parser.error('you must specifiy --- <virt-server>...')
136 if opts.build_tree is None:
137 opts.build_tree = Path(False, '.', 'build-tree', dir=True)
138 if opts.control is None:
139 opts.control = opts.build_tree.append(
140 'debian/tests/control', 'control')
149 tb.sp = subprocess.Popen(opts.vserver,
150 stdin=p, stdout=p, stderr=None)
154 if tb.sp is None: return
155 ec = tb.sp.returncode
162 tb.bomb('testbed gave exit status %d after quit' % ec)
164 if tb.scratch is not None: return
165 p = tb.commandr1('open')
166 tb.scratch = Path(True, p, 'tb-scratch', dir=True)
168 if tb.scratch is None: return
172 if tb.sp is not None:
176 if ec: print >>sys.stderr, ('adt-run: testbed failing,'
177 ' exit status %d' % ec)
179 raise Quit(16, 'testbed failed: %s' % m)
180 def send(tb, string):
183 print >>tb.sp.stdin, string
187 tb.bomb('cannot send to testbed: %s' %
188 formatexception_only(sys.last_type, sys.last_value))
189 def expect(tb, keyword, nresults=-1):
190 l = tb.sp.stdout.readline()
191 if not l: tb.bomb('unexpected eof from the testbed')
192 if not l.endswith('\n'): tb.bomb('unterminated line from the testbed')
196 if not ll: tb.bomb('unexpected whitespace-only line from the testbed')
198 if tb.lastsend is None:
199 tb.bomb("got banner `%s', expected `%s...'" %
202 tb.bomb("sent `%s', got `%s', expected `%s...'" %
203 (tb.lastsend, l, keyword))
205 if nresults >= 0 and len(ll) != nresults:
206 tb.bomb("sent `%s', got `%s' (%d result parameters),"
207 " expected %d result parameters" %
208 (string, l, len(ll), nresults))
210 def commandr(tb, cmd, nresults, args=()):
211 al = [cmd] + map(urllib.quote, args)
212 tb.send(string.join(al))
214 rl = map(urllib.unquote, ll)
216 def command(tb, cmd, args=()):
217 tb.commandr(cmd, 0, args)
218 def commandr1(tb, cmd, args=()):
219 rl = tb.commandr(cmd, 1, args)
223 def __init__(f, fname, stz, base, tnames, vl):
232 r = map((lambda w: (lno, w)), r)
234 return flatten(map(distribute, f.vl))
235 def atmostone(f, default):
242 raise Unsupported(f.vl[1][0],
243 'only one %s field allowed' % fn)
246 class FieldIgnore(FieldBase):
250 def __init__(r,rname,base): pass
252 class Restriction_rw_build_tree(Restriction): pass
254 class Field_Restrictions(FieldBase):
256 for wle in f.words():
258 rname = rname.replace('-','_')
259 try: rclass = globals()['Restriction_'+rname]
260 except KeyError: raise Unsupported(lno,
261 'unknown restriction %s' % rname)
262 r = rclass(rname, f.base)
263 f.base['restrictions'].append(r)
265 class Field_Tests(FieldIgnore): pass
267 class Field_Tests_directory(FieldBase):
269 base['testsdir'] = oneonly(f)
272 def __init__(t, tname, base):
274 for k in base: setattr(t,k,base[k])
278 control = file(opts.control.onhost(), 'r')
280 def badctrl(m): testbed.badpkg('tests/control line %d: %s' % (lno, m))
281 stz = None # stz[field_name][index] = (lno, value)
287 if stz is None: return
293 initre = regexp.compile('([A-Z][-0-9a-z]*)\s*\:\s*(.*)$')
295 l = control.readline()
298 if not l.endswith('\n'): badctrl('unterminated line')
299 if regexp.compile('\s*\#').match(l): continue
300 if not regexp.compile('\S').match(l): end_stanza(stz); continue
301 initmat = initre.match(l)
303 (fname, l) = initmat.groups()
304 fname = string.capwords(fname)
306 stz = { ' lno': lno }
307 if not stz.has_key(fname): stz[fname] = [ ]
308 hcurrent = stz[fname]
309 elif regexp.compile('\s').match(l):
310 if not hcurrent: badctrl('unexpected continuation')
312 badctrl('syntax error')
313 hcurrent.append((lno, l))
316 def testbadctrl(stz, lno, m):
317 report_badctrl(lno, m)
322 try: tnames = stz['Tests']
325 raise Unsupported(stz[' lno'],
327 tnames = map((lambda lt: lt[1]), tnames)
328 tnames = string.join(tnames).split()
331 'testsdir': 'debian/tests'
333 for fname in stz.keys():
334 if fname.startswith(' '): continue
336 try: fclass = globals()['Field_'+
337 fname.replace('-','_')]
338 except KeyError: raise Unsupported(vl[0][0],
339 'unknown metadata field %s' % fname)
340 f = fclass(stz, fname, base, tnames, vl)
342 except Unsupported, u:
343 for tname in tnames: u.report(tname)
347 t = Test(tname, base)
351 def print_exception(ei, msgprefix=''):
352 if msgprefix: print >>sys.stderr, msgprefix
355 print >>sys.stderr, 'adt-run:', q.m
358 print >>sys.stderr, "adt-run: unexpected, exceptional, error:"
359 traceback.print_exc()
365 if tmpdir is not None:
366 rm_ec = subprocess.call(['rm','-rf','--',tmpdir])
367 if testbed is not None:
369 if rm_ec: bomb('rm -rf -- %s failed, code %d' % (tmpdir, ec))
371 print_exception(sys.exc_info(),
372 '\nadt-run: error cleaning up:\n')
380 tmpdir = tempfile.mkdtemp()
385 ec = print_exception(sys.exc_info(), '')