From 1de7ff5465a0bcf65b042504d04257a096ae3703 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Thu, 8 Mar 2007 02:30:52 +0000 Subject: [PATCH] * New adt-testreport-onepackage script, with some example config files onepackage-config and ubuntu-config. * Fixed quite a few bugs in adt-run. * Made adt_distro xenlvm config var settable. --- debian/changelog | 5 +- runner/adt-run | 87 +++++++++++++----- runner/adt-run.1 | 6 ++ runner/adt-testreport-onepackage | 147 ++++++++++++++++++++++--------- runner/onepackage-config | 5 ++ runner/ubuntu-config | 2 + xen/README | 2 +- 7 files changed, 186 insertions(+), 68 deletions(-) create mode 100644 runner/onepackage-config create mode 100644 runner/ubuntu-config diff --git a/debian/changelog b/debian/changelog index fe3ed3f..f8b0444 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,6 +1,9 @@ autopkgtest (0.7.2) unstable; urgency=low - * WIP adt-testreport-onepackage + * New adt-testreport-onepackage script, with some example config + files onepackage-config and ubuntu-config. + * Fixed quite a few bugs in adt-run. + * Made adt_distro xenlvm config var settable. -- diff --git a/runner/adt-run b/runner/adt-run index 9822d16..8873757 100755 --- a/runner/adt-run +++ b/runner/adt-run @@ -46,6 +46,7 @@ tmpdir = None # pathstring on host testbed = None # Testbed errorcode = 0 # exit status that we are going to use binaries = None # Binaries (.debs we have registered) +build_essential = ["build-essential"] #---------- output handling # @@ -73,7 +74,12 @@ binaries = None # Binaries (.debs we have registered) # is done by forking off a copy of ourselves to do plumbing, # which copy we wait for at the appropriate point. +class DummyOpts: + def __init__(do): do.debuglevel = 0 + +opts = DummyOpts() trace_stream = None +summary_stream = None def pstderr(m): print >>sys.stderr, m @@ -111,7 +117,7 @@ class Errplumb: else: ep.stream = trace_stream ep._sp = None else: - ep._sp = subprocess.Popen(['tee','/dev/stderr'], + ep._sp = subprocess.Popen(['tee','-a','/dev/stderr'], stdin=subprocess.PIPE, stdout=trace_stream, close_fds=True) ep.stream = ep._sp.stdin @@ -134,9 +140,14 @@ def subprocess_cooked(cmdl, critical=False, dbg=None, **kwargs): ep.wait() return (rc, output) +def psummary(m): + if summary_stream is not None: print >>summary_stream, m + def preport(m): print m + sys.stdout.flush() if trace_stream is not None: print >>trace_stream, m + psummary(m) def report(tname, result): preport('%-20s %s' % (tname, result)) @@ -220,7 +231,8 @@ class AutoFile: else: return p.path[tbp]+p.dir+'!' out = p.what if p.spec is not None: - if p.spec_tbp: out += '#' + if not hasattr(p,'spec_tb'): out += '~' + elif p.spec_tbp: out += '#' else: out += '=' out += p.spec out += ':' @@ -231,7 +243,8 @@ class AutoFile: def _wrong(p, how): xtra = '' - if p.spec is not None: xtra = ' spec[%s]=%s' % (p.spec, p.spec_tb) + if p.spec is not None: + xtra = ' spec[%s]=%s' % (p.spec, getattr(p,'spec_tb',None)) raise ("internal error: %s (%s)" % (how, str(p))) def _ensure_path(p, tbp): @@ -390,6 +403,7 @@ class Action: def parse_args(): global opts + global n_non_actions # argh, stupid python scoping rules usage = "%prog --- ..." parser = OptionParser(usage=usage) pa = parser.add_option @@ -410,6 +424,7 @@ def parse_args(): # actions (ie, test sets to run, sources to build, binaries to use): def cb_action(op,optstr,value,parser, long,kindpath,is_act): + global n_non_actions parser.largs.append((value,kindpath)) n_non_actions += not(is_act) @@ -501,7 +516,7 @@ def parse_args(): def cb_path(op,optstr,value,parser, constructor,long,dir): name = long.replace('-','_') - af = constructor(arghandling['tb'], value, long, dir) + af = constructor(long, value,arghandling['tb']) setattr(parser.values, name, af) def pa_path(long, constructor, help, dir=False): @@ -519,6 +534,9 @@ def parse_args(): pa('','--log-file', type='string', dest='logfile', help='write the log LOGFILE, emptying it beforehand,' ' instead of using OUTPUT-DIR/log or TMPDIR/log') + pa('','--summary-file', type='string', dest='summary', + help='write a summary report to SUMMARY,' + ' emptying it beforehand') pa('','--user', type='string', dest='user', help='run tests as USER (needs root on testbed)') @@ -594,7 +612,7 @@ def parse_args(): opts.actions.append(Action(kind, af, arghandling, what)) def setup_trace(): - global trace_stream, tmpdir + global trace_stream, tmpdir, summary_stream if opts.tmpdir is not None: rmtree('tmpdir(specified)',opts.tmpdir) @@ -611,11 +629,13 @@ def setup_trace(): opts.logfile = opts.tmpdir + '/log' if opts.logfile is not None: trace_stream = open(opts.logfile, 'w', 0) + if opts.summary is not None: + summary_stream = open(opts.summary, 'w', 0) debug('options: '+`opts`, 1) def finalise_options(): - global opts, tb + global opts, tb, build_essential if opts.user is None and 'root-on-testbed' not in testbed.caps: opts.user = '' @@ -646,6 +666,7 @@ def finalise_options(): if (opts.user or 'root-on-testbed' not in testbed.caps): opts.gainroot = 'fakeroot' + build_essential += ['fakeroot'] if opts.gnupghome.startswith('~/'): try: home = os.environ['HOME'] @@ -722,6 +743,7 @@ class Testbed: (tb.modified, tb.deps_processed, deps_new), 1) if 'revert' in tb.caps and (tb.modified or [d for d in tb.deps_processed if d not in deps_new]): + for af in tb._ephemeral: af.read(False) tb._debug('reset **') tb.command('revert') tb.blamed = [] @@ -735,7 +757,7 @@ class Testbed: tb.prepare1(deps_new) tb.prepare2(deps_new) def register_ephemeral(tb, af): - if not getattr(af,'spec_tbp',False): tb._ephemeral.append(af) + tb._ephemeral.append(af) def _install_deps(tb, deps_new): tb._debug(' installing dependencies '+`deps_new`, 1) tb.deps_processed = deps_new @@ -950,6 +972,7 @@ def run_tests(stanzas, tree): global errorcode, testbed if stanzas == (): report('*', 'SKIP no tests in this package') + errorcode |= 8 for stanza in stanzas: tests = stanza[' tests'] if not tests: @@ -958,7 +981,7 @@ def run_tests(stanzas, tree): for t in tests: t.prepare() t.run(tree) - if 'breaks-testbed' in t.restrictions: + if 'breaks-testbed' in t.restriction_names: testbed.needs_reset() testbed.needs_reset() @@ -1000,7 +1023,7 @@ class Test: t._debug('[----------------------------------------') def stdouterr(oe): idstr = t.what + '-' + oe - if opts.output_dir is not None and opts.output_dir.tb: + if opts.output_dir is not None and opts.output_dir.spec_tbp: use_dir = opts.output_dir else: use_dir = testbed.scratch @@ -1014,18 +1037,26 @@ class Test: tf = af.read(True) tmpdir = None + tree.read(True) + + rc = testbed.execute('testchmod-'+t.what, ['chmod','+x','--',tf]) + if rc: bomb('failed to chmod +x %s' % tf) - if 'needs-root' not in t.restrictions: - tf = opts.user_wrap(tf) - tmpdir = '%s/%s-tmpdir' % (testbed.scratch.read(True), t.what) + if 'needs-root' not in t.restriction_names and opts.user is not None: + tfl = ['su',opts.user,'-c',tf] + tmpdir = '%s%s-tmpdir' % (testbed.scratch.read(True), t.what) script = 'rm -rf -- "$1"; mkdir -- "$1"' if opts.user: script += '; chown %s "$1"' % opts.user + if 'rw-build-tree' in t.restriction_names: + script += '; chown -R %s "$2"' % opts.user rc = testbed.execute('mktmpdir-'+t.what, - ['sh','-xec',script,'x',tmpdir]) + ['sh','-xec',script,'x',tmpdir,tree.read(True)]) if rc: bomb("could not create test tmpdir `%s', exit code %d" % (tmpdir, rc)) + else: + tfl = [tf] - rc = testbed.execute('test-'+t.what, [tf], + rc = testbed.execute('test-'+t.what, tfl, so=so.write(True), se=se.write(True), cwd=tree.read(True), tmpdir=tmpdir) @@ -1037,7 +1068,7 @@ class Test: if stab.st_size != 0: l = open(se_read).readline() l = l.rstrip('\n \t\r') - if len(l) > 40: l = l[:40] + '...' + if len(l) > 35: l = l[:35] + '...' t.reportfail('status: %d, stderr: %s' % (rc, l)) t._debug(' - - - - - - - - - - stderr - - - - - - - - - -') debug_file(se_read) @@ -1150,9 +1181,11 @@ def print_exception(ei, msgprefix=''): (et, q, tb) = ei if et is Quit: pstderr('adt-run: ' + q.m) + psummary('quitting: '+q.m) return q.ec else: pstderr("adt-run: unexpected, exceptional, error:") + psummary('quitting: unexpected error, consult transcript') traceback.print_exc(None, sys.stderr) if trace_stream is not None: traceback.print_exc(None, trace_stream) @@ -1268,7 +1301,9 @@ END 'print res', 'print d.missingDeps', 'print d.requiredChanges', - 'assert(res)', + 'if not res: raise "gdebi failed (%s, %s, %s): %s" % '+ + ' (`res`, `d.missingDeps`, `d.requiredChanges`, '+ + 'd._failureString)', 'cache.commit()', '' ] @@ -1398,7 +1433,7 @@ def source_rules_command(act,script,what,which,work,cwd, results = open(so.read()).read().rstrip('\n') if len(results): results = results.split("\n") else: results = [] - if rc: badpkg("%s failed with exit code %d" % (which,rc)) + if rc: badpkg("rules %s failed with exit code %d" % (which,rc)) if results_lines is not None and len(results) != results_lines: badpkg("got %d lines of results from %s where %d expected" % (len(results), which, results_lines)) @@ -1460,7 +1495,7 @@ def build_source(act, control_override): script = binaries.apt_pkg_gdebi_script('', [[ 'from GDebi.DebPackage import DebPackage', 'd = DebPackage(cache)', - 'res = d.satisfyDependsStr("dpkg-source")', + 'res = d.satisfyDependsStr("dpkg-dev")', ]]) cmdl = ['python','-c',script] whatp = what+'-dpkgsource' @@ -1546,11 +1581,11 @@ def build_source(act, control_override): for stanza in stanzas: for t in stanza[' tests']: if 'no-build-needed' not in t.feature_names: - build_needed('test %s' % t.name) + build_needed('test %s' % t.tname) for d in t.depends: if '@' in d: build_needed('test %s ' - 'dependency %s' % (t.name,d)) + 'dependency %s' % (t.tname,d)) debug_b('build not needed') built = False @@ -1568,7 +1603,9 @@ def build_source(act, control_override): ],[ 'from GDebi.DebPackage import DebPackage', 'd = DebPackage(cache)', - 'res = d.satisfyDependsStr("build-essential")', + 'res = d.satisfyDependsStr("'+ + ','.join(build_essential)+ + '")', ]]) cmdl = ['python','-c',script] @@ -1595,11 +1632,12 @@ def build_source(act, control_override): work.read(True)+os.path.basename(result_pwd), True) if act.ah['dsc_tests']: - act.tests_tree.read() testbed.register_ephemeral(act.work) testbed.register_ephemeral(act.tests_tree) - if not built: return + if not built: + act.blamed = [] + return act.blamed = copy.copy(testbed.blamed) @@ -1652,7 +1690,8 @@ def process_actions(): binaries = Binaries() for act in opts.actions: - testbed.register_ephemeral(act.af) + if not act.af.spec_tbp: + testbed.register_ephemeral(act.af) binaries.reset() control_override = None diff --git a/runner/adt-run.1 b/runner/adt-run.1 index fb6a05c..1fbd59e 100644 --- a/runner/adt-run.1 +++ b/runner/adt-run.1 @@ -153,6 +153,12 @@ Specifies that the trace log should be written to \fIlogfile\fR instead of to \fBlog\fR in \fIoutput-dir\fR or \fItmpdir\fR. \fIlog-file\fR is not affected by \fB--paths-testbed\fR. .TP +.BI --summary= summary +Specifies that a summary of the outcome should be written to +\fIsummary\fR. The events in the summary are written to the log +in any case. +\fIsummary\fR is not affected by \fB--paths-testbed\fR. +.TP .BR --debug | -d Include additional debugging information in the trace log. Each additional \fB-d\fR increases the debugging level; the current maximum diff --git a/runner/adt-testreport-onepackage b/runner/adt-testreport-onepackage index efb869e..2fc283d 100755 --- a/runner/adt-testreport-onepackage +++ b/runner/adt-testreport-onepackage @@ -14,9 +14,13 @@ salutation="Ian" from="$salutation Jackson " rsync=rsync disable=true +interactive=true for config in "$@"; do - . "$config" + case "$config" in + *=*) eval "$config" ;; + *) . "$config" ;; + esac done if $disable; then @@ -24,14 +28,20 @@ if $disable; then exit 1 fi -destdirtail=autopkgtest-output/$distro -destrsynchead=ijackson@chiark:public-html/ -desthttphead=http://www.chiark.greenend.org.uk/~ijackson/ +exec 3>&1 +printf >&3 "starting " rm -rf "$tmp" mkdir "$tmp" -exec >"$tmp"/log 5>&1 +>"$tmp"/_log + +if $interactive; then + echo '(log diverted to stdout)' >>"$tmp"/_log +else + exec >>"$tmp"/_log +fi +exec 4>&1 progress () { echo "++++++ $1 ++++++" @@ -39,18 +49,19 @@ progress () { gurl () { progress "fetching $1" - curl -sS "$1" >"$2" + curl -fsS "$1" >"$2" } -gurl "$mirror/dists/$distro/$suite/source/Sources.gz" "$tmp"/sources.gz -zcat "$tmp"/sources.gz >"$tmp"/sources-in +gurl "$mirror/dists/$distro/$suite/source/Sources.gz" "$tmp"/_sources.gz +zcat "$tmp"/_sources.gz >"$tmp"/_sources-in now=`date +%s` >>"$var"/last-info progress selecting -pkg="`perl -e ' +if [ "x$pkg" = x ]; then + pkg="`perl -e ' use IO::Handle; $pre= "[-+.0-9a-z]+"; @@ -61,9 +72,10 @@ pkg="`perl -e ' f1(); while () { - die unless m/^($pre) ($vre) (\d+)$/; + die unless m/^($pre) ($vre) (\d+)( .*)?$/; $lastver{$1}= $2; $lasttime{$1}= $3; + $extras{$1}= $4." "; } f2(); f1(); @@ -77,8 +89,9 @@ pkg="`perl -e ' die if length $version; $version= $1; $score= '$now' - $lasttime{$package}; - $score= 1e8 if $score>1e8; + $score= 1e7 if $score>1e7; $score *= 5 if $lastver{$package} ne $version; + $score *= 10 unless $extras{$package} =~ m/ nt /; next if $score < $best_score or ($score==$best_score and \ $package gt $best_package); @@ -91,7 +104,7 @@ pkg="`perl -e ' } f2(); die unless length $best_package; - open L, ">&5" or die $!; + open L, ">&4" or die $!; printf L "selected %s (age %s, score %d)\n", $best_package, exists($lastime{$best_package}) @@ -99,18 +112,21 @@ pkg="`perl -e ' : "", $best_score; print "$best_package\n" or die $!; -' "$var"/last-info "$tmp"/sources-in`" + ' "$var"/last-info "$tmp"/_sources-in`" +else + printf >&4 "package forced: %s\n" "$pkg" +fi sed -n "/^Package: $pkg\$/,/^\$/p" \ - <"$tmp"/sources-in >"$tmp"/this-stanza + <"$tmp"/_sources-in >"$tmp"/_this-stanza echo -cat "$tmp"/this-stanza +cat "$tmp"/_this-stanza getfield () { eval 'p'$1'="` sed -n '\''s/^'$1': //p'\'' \ - <"$tmp"/this-stanza + <"$tmp"/_this-stanza `"' } @@ -121,9 +137,11 @@ leafnames="` sed -n '/^Files:/,/^([^ ].*)?$/{ /^ /{ s/^ [0-9a-z][0-9a-z]* *[0-9][0-9]* //; p }}' \ - <"$tmp"/this-stanza + <"$tmp"/_this-stanza `" +printf >&3 "selected \"%s\" " $pkg + tp="$tmp/$pkg" mkdir "$tp" "$tp/src" "$tp/tmp" "$tp/out" @@ -133,7 +151,7 @@ for leafname in $leafnames; do */*|.*) echo >&2 "bad leafname: $leafname"; exit 1;; *.dsc) dsc="$df";; esac - gurl "$mirror/pool/$suite/$pDirectory/$leafname" "$df" + gurl "$mirror/$pDirectory/$leafname" "$df" done if [ "x$maintainer_email_override" = x ]; then @@ -143,6 +161,8 @@ else maintainer_email=maintainer_email_override fi +printf >&3 "adt-run " + progress "starting test" xrc () { @@ -153,66 +173,91 @@ xrc () { set -e } +echo 'fatal: adt-run did not start properly' >"$tmp"/_summary + xrc adt-run --tmp-dir "$tp"/tmp \ --output-dir "$tp"/out \ --log-file "$tp"/log \ + --summary "$tmp"/_summary \ + $adtrun_extra_opts \ --source "$dsc" \ --- \ adt-virt-xenlvm \ - distro="$distro" \ - 2>&1 + $adtvirt_extra_opts \ + -- \ + --distro="$distro" \ + 2>&1 3>&- 4>&- + +printf >&3 "%s " $rc + +ourx=0 +upload=true +extras='' case "$rc" in 0) summary='all OK'; email='' ;; 2) summary='OK (some skipped)'; email='' ;; -8) summary='package declares no tests'; email='' ;; +8) summary='package declares no tests'; email='' + upload=false; extras='nt' ;; 4|6) summary='test(s) failed!'; email="$maintainer_email" ;; 12) summary='erroneous package!'; email="$maintainer_email" ;; 16) summary='testbed failed!'; email="administrator_email" ;; -*) summary='unexpected failure!'; email="administrator_email" ;; +*) summary='unexpected failure!'; email="administrator_email"; ourx=20;; esac progress "RESULTS $summary" -progress "contacting $email" -for odir in tmp out; do - if test -d "$tp"/$odir; then - GZIP=-2 tar -f "$tp"/$odir.tar.gz -C "$tp" -zc $odir - rm -r "$tp"/$odir - fi -done - -$rsync -rltH --safe-links --delete "$tp" "$destrsynchead/$destdirtail/" +if $upload; then + progress "bundling" + ln "$tmp"/_summary "$tp"/summary + + for odir in tmp out; do + if test -d "$tp"/$odir; then + GZIP=-2 tar -f "$tp"/$odir.tar.gz -C "$tp" -zc $odir + rm -r "$tp"/$odir + fi + done + + progress "uploading" + printf >&3 "uploading" + $rsync -rltH --safe-links --delete "$tp" "$destrsynchead/$destdirtail/" + printf >&3 " " +fi if [ "x$email" != x ]; then + progress "contacting $email" eval "email_addr=\$$email" - cat >"$tmp"/email <&3 "email \"%s\" " "$email_addr" + cat >"$tmp"/_email <>"$tmp"/_email + cat >>"$tmp"/_email <>"$tmp"/email <>"$tmp"/_email <>"$tmp"/email <>"$tmp"/_email <>"$tmp"/email <>"$tmp"/_email <>"$tmp/email" <>"$tmp/_email" <>"$tmp"/email 2>&1 "$tmp"/log ||: - sendmail -odq -oem -t -oi <"$tmp"/email fi printf >>"$var"/log "package=%s rc=%s emailed='%s'\n" \ "$pkg" $rc "$email_addr" -sed -e "/^$pkg /d" <"$var"/last-info >"$var"/last-info.new -printf "%s %s %s\n" "$pkg" "$pVersion" "$now" >>"$var"/last-info.new -mv "$var"/last-info.new "$var"/last-info +if [ "x$ourx" = x0 ]; then + sed -e "/^$pkg /d" <"$var"/last-info >"$var"/last-info.new + printf "%s %s %s %s\n" "$pkg" "$pVersion" "$now" "$extras" \ + >>"$var"/last-info.new + mv "$var"/last-info.new "$var"/last-info + progress "tested." +else + progress "fault ($ourx)." +fi + +if [ "x$email" = x ]; then + if $interactive; then + cat "$tmp"/_log >&2 + fi +else + cat >>"$tmp"/_email 2>&1 "$tmp"/_log ||: + if $interactive; then + cat "$tmp"/_email >&2 + else + sendmail -odq -oem -t -oi <"$tmp"/_email + fi +fi -progress "done." +printf >&3 "done %s.\n" $ourx +exit $ourx diff --git a/runner/onepackage-config b/runner/onepackage-config new file mode 100644 index 0000000..e864343 --- /dev/null +++ b/runner/onepackage-config @@ -0,0 +1,5 @@ +cd /root/adt-play +disable=false +desthttphead=http://www.chiark.greenend.org.uk/~ijackson/ +destrsynchead=ijackson@login.chiark.greenend.org.uk:public-html/ +destdirtail=autopkgtest-output/$distro diff --git a/runner/ubuntu-config b/runner/ubuntu-config new file mode 100644 index 0000000..b4443f1 --- /dev/null +++ b/runner/ubuntu-config @@ -0,0 +1,2 @@ +adtrun_extra_opts=--no-built-binaries +interactive=false diff --git a/xen/README b/xen/README index 93022b2..ed13712 100644 --- a/xen/README +++ b/xen/README @@ -134,7 +134,7 @@ adt_modules /lib/modules/`uname -r` adt_lvm_vg the system's LVM volume group if there is only one LVM volume group to create base filesystem image in. -adt_distro +adt_distro host system's distribution Distribution name (eg, `edgy', `feisty', `sarge', `etch'). adt_pbuilder_args -- 2.30.2