chiark / gitweb /
proper pathname handling; documentation; now ready for test (ha ha)
authorIan Jackson <ian@anarres>
Wed, 17 Jan 2007 17:41:34 +0000 (17:41 +0000)
committerIan Jackson <ian@anarres>
Wed, 17 Jan 2007 17:41:34 +0000 (17:41 +0000)
runner/adt-run
runner/adt-run.1

index 02db43ac08a3ca88ac01ca5419e61baae2d9c6c4..ad816b2f83b8c40a2e9dac7cd7057f6a5bbb4259 100755 (executable)
@@ -51,8 +51,12 @@ binaries = None              # Binaries (.debs we have registered)
 class Quit:
        def __init__(q,ec,m): q.ec = ec; q.m = m
 
-def bomb(m): raise Quit(20, "unexpected error: %s" % m)
-def badpkg(m):
+def bomb(m, se=''):
+       print >>sys.stderr, se
+       raise Quit(20, "unexpected error: %s" % m)
+
+def badpkg(m, se=''):
+       print >>sys.stderr, se
        print 'blame: ', ' '.join(testbed.blamed)
        raise Quit(12, "erroneous package: %s" % m)
 
@@ -81,184 +85,129 @@ class AutoFile:
        # p.what
        # p.path[tb]    None or path    not None => path known
        # p.file[tb]    None or path    not None => file exists
-       # p.spec        string
-       # p.spec_tb     True or False, None iff p.spec is None
-       # p.tb_scratch
+       # p.spec        string or not set
+       # p.spec_tbp    True or False, or not set
        # p.dir         '' or '/'
+
  def __init__(p, what):
        p.what = what
        p.path = [None,None]
        p.file = [None,None]
        p.dir = ''
+
+ def __str__(p):
+       out = p.what
+       def ptbp(tbp):
+               if p.path[tbp] is None: out += '-'
+               elif p.file[tbp] is None: out += '?'+p.path[tbp]
+               else: out += '!'+p.path[tbp]
+               out += p.dir
+       ptbp(False)
+       out += '|'
+       ptbp(False)
+       if p.has_key('spec'):
+               if p.spec_tb: out += '<'
+               else: out += '>'
+               out += p.spec
+       return out
+
+ def _wrong(p, how):
+       xtra = ''
+       if p.has_key('spec'): xtra = ' spec[%s]=%s' % (p.spec, p.spec_tb)
+       error "internal error: %s (%s)" % (how, p.__str__())
+
+ def _ensure_path(p, tbp):
+       if p.path[tbp] is None:
+               if not tbp:
+                       p.path[tbp] = tmpdir+'/'+what
+               else:
+                       p.path[tbp] = testbed.scratch.path[True]+'/'+what
+
+ def write(p, tbp=False):
+       p._ensure_path(tbp)
+
+       if p.dir and not p.file[tbp]:
+               if not tbp:
+                       try: os.mkdir(p.path[tbp])
+                       except IOError, oe: if oe.errno != errno.EEXIST: raise
+               else:
+                       cmdl = ['sh','-ec',
+                               'test -d "$1" || mkdir "$1"',
+                               'x', p.path[tbp]]
+                       tf_what = urllib.quote(p.what).replace('/',' ')
+                       (rc,se) = testbed.execute('mkdir-'+tf_what, cmdl)
+                       if rc: bomb('failed to create directory %s' %
+                               p.path[tbp], se)
+
+       p.file[tbp] = p.path[tbp]
+       return p.path[tbp]
+
+ def read(p, tbp=False):
+       if p.file[not tbp] is None: p._wrong("requesting read but nonexistent")
+       p._ensure_path(tbp)
+
+       if p.file[tbp] is None:
+               cud = ['copyup','copydown'][tbp]
+               src = p.file[not tbp] + p.dir
+               dst = p.file[tbp] + p.dir
+               testbed.command(cud, (src, dst))
+
+       return p.file[tbp]
+
  def subpath(p, what, leaf, constructor):
-       if not p.dir: error "creating subpath of non-directory"
-       return constructor(what, p.spec+'/'+leaf, p.spec_tb)
- def invalidate(p, tb=False):
-       p.file[tb] = None
+       if not p.dir: p._wrong("creating subpath of non-directory")
+       return constructor(what, p.spec+p.dir+leaf, p.spec_tbp)
+ def sibling(p, what, leaf, constructor):
+       dir = os.path.dirname(p.spec)
+       if dir: dir += '/'
+       return constructor(what, dir+leaf, p.spec_tbp)
+
+ def invalidate(p, tbp=False):
+       p.file[tbp] = None
+
+ def _check(p):
+       for tbp in [False,True]:
+        for pf in [t.path, t.file]:
+               if pf[tbp] is None: continue
+               if not pf[tbp]: bomb('empty path specified for '+p.what)
+               if p.dir and pf[tbp].endswith('/'):
+                       pf[tbp] = pf[tbp].rstrip('/')
+                       if not pf[tbp]: pf[tbp] = '/'
+               if not p.dir and pf[tbp].endswith('/'):
+                       bomb("directory `%s' specified for "
+                            "non-directory %s" % (pf[tbp], p.what))
 
 class InputFile(Path):
- def __init__(p, what, spec, spec_tb=False):
+ def __init__(p, what, spec, spec_tbp=False):
        AutoFile.__init__(p, what)
        p.spec = spec
-       p.spec_tb = spec_tb
-       p.path[spec_tb] = p.file[spec_tb] = spec
+       p.spec_tbp = spec_tbp
+       p.path[spec_tbp] = p.file[spec_tbp] = spec
+       p._check()
 
 class InputDir(Path):
- def __init__(p, what, spec, spec_tb=False):
-       InputFile.__init__(p,what,spec,spec_tb)
+ def __init__(p, what, spec, spec_tbp=False):
+       InputFile.__init__(p,what,spec,spec_tbp)
        p.dir = '/'
+       p._check()
 
 class OutputFile(Path):
- def __init__(p, what, spec, spec_tb=False):
+ def __init__(p, what, spec, spec_tbp=False):
        AutoFile.__init__(p, what)
        p.spec = spec
-       p.spec_tb = spec_tb
-       p.path[spec_tb] = spec
+       p.spec_tbp = spec_tbp
+       p.path[spec_tbp] = spec
+       p._check()
 
 class OutputDir(Path):
- def __init__(p, what, spec, spec_tb=False):
-       OutputFile.__init__(p,what,spec,spec_tb)
+ def __init__(p, what, spec, spec_tbp=False):
+       OutputFile.__init__(p,what,spec,spec_tbp)
        p.dir = '/'
+       p._check()
 
 class TemporaryFile(Path):
  def __init__(p, what):
-       p.path = 
-       OutputFile.__init__(p,what, testbed.scratch
-
- def ensure_path(p, tb=False):
-       if tb and not p.spec_tb:
-               if not testbed.scratch:
-                       error "called ensure_path for `%s' when testbed closed"
-                               % what
-               if not p.tb_scratch or p.tb_scratch is not testbed.scratch:
-                       
-
-       if p.path[tb] is not None: return
-       if tb: p.path[tb] = p.tb_tmpdir
-       else: p.path[tb] = tmpdir
-       p.path[tb] += '/'+p.what
-
- def ensure_file(p, tb=False):
-       if p.file[tb] is not None: return
-       p.ensure_path(tb)
-       testbed.open()
-       
-
- def write(p, tb=False):
-       p.ensure_path(tb)
-       return p.path[tb]
- def read(p, tb=False):
-       p.ensure_file(tb)
-
-
-
-
-class InputPath:
-class OutputPath:
-
- def __init__(p, path, spec_tb, what, dir=False):
-       if p.tb:
-               if p.p[:1] != '/':
-                       bomb("path %s specified as being in testbed but"
-                               " not absolute: `%s'" % (what, p.p))
-       p.path[spec_tb] = p.file[spec_tb] = path
-       p.what = what
-
-
-       if spec_tb:
-               p.
-tb_path = path
-               p.tb_onpath = path
-               p.tb_onhost = None
-       
-
-4 def __init__(p, tb, path, what, dir=False, tbscratch=None, xfmap=None
-               lpath=None):
-       p.tb = tb
-       p.p = path
-       p.what = what
-       p.dir = dir
-       p.tbscratch = tbscratch
-       p.lpath = None
-       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,
-                       tbscratch=p.tbscratch)
- def __str__(p):
-       if p.tb: pfx = '/VIRT'
-       elif p.p[:1] == '/': pfx = '/HOST'
-       else: pfx = './'
-       return pfx + p.p
-
- def xfmapcopy(p, cud, dstdir):
-       if p.xfmap is None: return
-       srcdir = os.path.dirname(p.path()+'/')
-       dstdir = p.xfmapdstdir+'/'
-       for f in p.xfmap(file(p.local)):
-               if '/' in f: bomb("control file %s mentions other filename"
-                               "containing slash" % p.what)
-               testbed.command(cud, (srcdir+f, dstdir+f))
-
- def onhost(p, lpath = None):
-       if lpath is not None:
-               if p.lpath is not None: assert(p.lpath == lpath)
-               p.lpath = lpath
-       if p.local is not None:
-               if p.lpath is not None: assert(p.local == p.lpath)
-               return p.local
-
-       if p.xfmap is None:
-               p.local = p.lpath
-               if p.local is None: p.local = tmpdir + '/tb-' + p.what
-       else:
-               assert(p.lpath is None)
-               assert(not p.dir)
-               p.xfmapdstdir = tmpdir + '/tbd-' + p.what
-               os.mkdir(p.xfmapdstdir)
-               p.local = p.xfmapdstdir + '/' + os.path.basename(p.down)
-
-       testbed.command('copyup', (p.path(), p.local + p.dirsfx))
-       p.xfmapcopy('copyup')
-
-       return p.local
-
- def maybe_onhost(p):
-       if p.lpath is None: return None
-       return p.onhost()
-
- def ontb(p):
-
-       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")
-
-       if p.xfmap is None:
-               p.down = testbed.scratch.p + '/host-' + p.what          
-       else:
-               assert(not p.dir)
-               p.xfmapdstdir = testbed.scratch.p + '/hostd-' + p.what
-               testbed.command('mkdir '+p.xfmapdstdir)
-               p.down = p.xfmapdstdir + '/' + os.path.basename(p.local)
-
-       p.tbscratch = testbed.scratch
-       testbed.command('copydown', (p.path(), p.down + p.dirsfx))
-       p.xfmapcopy('copydown')
-       return p.down
+       AutoFile.__init__(p, what)
 
 #---------- parsing and representation of the arguments
 
@@ -391,14 +340,15 @@ def parse_args():
 
        pa_path('output-dir', OutputDir, dir=True,
                help='write stderr/out files in PATH')
-       pa_path('tmp-dir', OutputDir, dir=True,
-               help='write temporary files to PATH, emptying PATH'
+
+       pa('','--tmp-dir',              type='string', dest='tmpdir',
+               help='write temporary files to TMPDIR, emptying it'
                     ' beforehand and leaving it behind at the end')
 
        pa('','--user',                 type='string', dest='user',
                help='run tests as USER (needs root on testbed)')
-       pa('','--fakeroot',             type='string', dest='fakeroot',
-               help='prefix debian/rules build with FAKEROOT')
+       pa('','--gain-root',            type='string', dest='gainroot',
+               help='prefix debian/rules binary with GAINROOT')
        pa('-d', '--debug', action='store_true', dest='debug');
        pa('','--gnupg-home',           type='string', dest='gnupghome',
                default='~/.autopkgtest/gpg',
@@ -459,7 +409,7 @@ def parse_args():
                what = '%s%s' % (kind,ix); ix++
 
                af = constructor(what+'-'+kind, pathstr, arghandling['tb'])
-               opts.actions.append(Action(kind, af, arghandling, ix))
+               opts.actions.append(Action(kind, af, arghandling, what))
 
 def finalise_options():
        global opts, testbed
@@ -492,11 +442,11 @@ def finalise_options():
        else:
                opts.user_wrap = lambda x: x
 
-       if opts.fakeroot is None:
-               opts.fakeroot = ''
+       if opts.gainroot is None:
+               opts.gainroot = ''
                if opts.user or
                   'root-on-testbed' not in testbed.caps:
-                       opts.fakeroot = 'fakeroot'
+                       opts.gainroot = 'fakeroot'
 
        if opts.gnupghome.startswith('~/'):
                try: home = os.environ['HOME']
@@ -522,7 +472,7 @@ class Testbed:
        tb.sp = subprocess.Popen(opts.vserver,
                stdin=p, stdout=p, stderr=None)
        tb.expect('ok')
-       tb.caps = tb.command('capabilities')
+       tb.caps = tb.commandr('capabilities')
  def stop(tb):
        tb.close()
        if tb.sp is None: return
@@ -536,8 +486,8 @@ class Testbed:
                tb.bomb('testbed gave exit status %d after quit' % ec)
  def open(tb):
        if tb.scratch is not None: return
-       p = tb.commandr1('open')
-       tb.scratch = OutputDir('tb-scratch', p, True)
+       pl = tb.commandr('open')
+       tb.scratch = OutputDir('tb-scratch', pl[0], True)
  def close(tb):
        if tb.scratch is None: return
        tb.scratch = None
@@ -598,7 +548,7 @@ class Testbed:
        if type(cmd) is str: cmd = [cmd]
        al = cmd + map(urllib.quote, args)
        tb.send(string.join(al))
-       ll = tb.expect('ok')
+       ll = tb.expect('ok', nresults)
        rl = map(urllib.unquote, ll)
        return rl
  def command(tb, cmd, args=()):
@@ -606,6 +556,20 @@ class Testbed:
  def commandr1(tb, cmd, args=()):
        rl = tb.commandr(cmd, 1, args)
        return rl[0]
+ def execute(tb, what, cmdargs,
+               si='/dev/null', so='/dev/null', se=None, cwd=None)
+       if cwd is None: cwd = tb.tbscratch.write(True)
+       se_use = se
+       if se_use is None:
+               se_use = TemporaryFile('xerr-'+what).write(True)
+       rc = tb.commandr1(['execute',
+               ','.join(map(urllib.quote, cmdargs))],
+               si, so, se_use, cwd)
+       try: rc = int(rc)
+       except ValueError: bomb("execute for %s gave invalid response `%s'"
+                                       % (what,rc))
+       if se is not None: return rc
+       return (rc, file(se.read()).read())
 
 #---------- representation of test control files: Field*, Test, etc.
 
@@ -678,47 +642,42 @@ def run_tests(stanzas):
                testbed.needs_reset()
 
 class Test:
- def __init__(t, tname, base):
+ def __init__(t, tname, base, act_what):
        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
+       t.what = act_what+'-'+tname
        if len(base['testsdir']): tpath = base['testsdir'] + '/' + tname
        else: tpath = tname
        t.af = opts.tests_tree.subpath('test-'+tname, tpath, InputFile)
  def report(t, m):
-       report(t.tname, m)
+       report(t.what, m)
  def reportfail(t, m):
        global errorcode
        errorcode |= 4
-       report(t.tname, 'FAIL ' + m)
+       report(t.what, 'FAIL ' + m)
  def run(t):
        def stdouterr(oe):
-               idstr = oe + '-' + t.tname
+               idstr = oe + '-' + t.what
                if opts.output_dir is not None and opts.output_dir.tb:
                        use_dir = opts.output_dir
                else:
                        use_dir = testbed.scratch
                return use_dir.subpath(idstr, idstr, OutputFile)
-       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.af.ontb(),
-               '/dev/null', so.ontb(), se.ontb(), opts.tests_tree.ontb()))
-       soh = stdouterrh(so, 'stdout')
-       seh = stdouterrh(se, 'stderr')
-       rc = int(rc)
-       stab = os.stat(seh)
+       rc = testbed.execute('test-'+t.what,
+               [opts.user_wrap(t.af.read(True))],
+               so=so, se=se, cwd=opts.tests_tree.write(True))
+                       
+       stab = os.stat(se.read())
        if stab.st_size != 0:
-               l = file(seh).readline()
+               l = file(se.read()).readline()
                l = l.rstrip('\n \t\r')
                if len(l) > 40: l = l[:40] + '...'
-               t.reportfail('stderr: %s' % l)
+               t.reportfail('status: %d, stderr: %s' % (rc, l))
        elif rc != 0:
                t.reportfail('non-zero exit status %d' % rc)
        else:
@@ -733,7 +692,6 @@ def read_control(act, tree, control_override):
        else:
                control_af = tree.subpath(act.what+'-testcontrol',
                        'debian/tests/control', InputFile)
-               testbed.blame('arg:'+tree.spec)
 
        try:
                control = file(control_af.read(), 'r')
@@ -804,7 +762,7 @@ def read_control(act, tree, control_override):
                                f = fclass(stz, fname, base, tnames, vl)
                                f.parse()
                        for tname in tnames:
-                               t = Test(tname, base)
+                               t = Test(tname, base, act.what)
                                stz[' tests'].append(t)
                except Unsupported, u:
                        for tname in tnames: u.report(tname)
@@ -934,26 +892,20 @@ END
        b.dir.invalidate(True)
        apt_source = b.dir.read(True)
 
-       se = TemporaryFile('%s-aptkey-stderr' % act.what)
        script = '
   apt-key add archive-key.pgp
   echo "deb file:///'+apt_source+'/ /" >/etc/apt/sources.list.d/autopkgtest
        '
-       rc = testbed.commandr1(['execute',
-                       ','.join(map(urllib.quote, ['sh','-ec','script']))],
-                       '/dev/null', '/dev/null', se.write(True), tbp)
+       (rc,se) = testbed.execute('aptkey-'+what, ['sh','-ec','script'], b.dir)
        if rc: bomb('apt setup failed with exit code %d' % rc, se)
 
        testbed.blamed += b.blamed
 
        for pkg in b.install:
                testbed.blame(pkg)
-               se = TemporaryFile('%s-install-%s-stderr' % (act.what,pkg))
-               rc = testbed.commandr1('execute','apt-get,-qy,install,'+pkg,
-                               '/dev/null','/dev/null',se.ontb(),
-                               testbed.scratch.read(True))
-               if rc:
-                       badpkg("installation of %s failed, exit code %d"
+               (rc,se) = testbed.execute('install-%s'+act.what,
+                       ['apt-get','-qy','install',pkg])
+               if rc: badpkg("installation of %s failed, exit code %d"
                                % (pkg, rc), se)
 
 #---------- processing of sources (building)
@@ -962,12 +914,11 @@ def source_rules_command(act,script,which,work,results_lines=0):
        script = "exec 3>&1 >&2\n" + '\n'.join(script)
        so = TemporaryFile('%s-%s-results' % (what,which))
        se = TemporaryFile('%s-%s-log' & (what,which))
-       rc = testbed.commandr1(['execute',
-               ','.join(map(urllib.quote, ['sh','-xec',script]))],
-               '/dev/null', so.write(True), se.write(True), work.write(True))
+       rc = testbed.execute('%s-%s' % (what,which),
+                       ['sh','-xec',script],
+                       so=so, se=se, cwd= work.write(True))
        results = file(so.read()).read().split("\n")
-       if rc:
-               badpkg_se("%s failed with exit code %d" % (which,rc), se)
+       if rc: badpkg_se("%s failed with exit code %d" % (which,rc), se)
        if results_lines is not None and len(results) != results_lines:
                badpkg_se("got %d lines of results from %s where %d expected"
                        % (len(results), which, results_lines), se)
@@ -981,7 +932,7 @@ def build_source(act):
 
        what = act.ah['what']
        dsc = act.af
-       basename = dsc.spec; if basename is None: basename = 'source.dsc'
+       basename = dsc.spec
        dsc_what = what+'/'+basename
 
        dsc_file = open(dsc.read())
@@ -996,11 +947,11 @@ def build_source(act):
                                act.blame = 'dsc:'+l[7:].strip()
                                testbed.blame(act.blame)
                elif not in_files: pass
-               if not dsc.spec_tb: continue
+
                m = re.match(l)
                if not m: badpkg(".dsc contains unparseable line"
                                " in Files: `%s'" % (`dsc`,l))
-               subfile = dsc.enclosingdir().subpath(
+               subfile = dsc.sibling(
                                dsc_what+'/'+m.groups(0), m.groups(0),
                                InputFile)
                subfile.read(True)
@@ -1035,7 +986,7 @@ def build_source(act):
        if act.ah['dsc_filter'] != '_':
                script = [
                        'cd '+work.write(True)+'/*/.',
-                       opts.user_wrap(opts.fakeroot+' debian/rules binary'),
+                       opts.user_wrap(opts.gainroot+' debian/rules binary'),
                        'cd ..',
                        'echo *.deb >&3',
                        ]
@@ -1090,11 +1041,11 @@ def process_actions():
                        for (pkg,bin) in act.binaries:
                                binaries.register(act,pkg,bin,'tests',
                                        act.blamed)
-                       if not act.ah['dsc_tests']: continue
-                       stanzas = read_control(act, act.tests_tree,
-                                       control_override)
-                       testbed.blamed += act.blamed
-                       run_tests(act, stanzas)
+                       if act.ah['dsc_tests']:
+                               stanzas = read_control(act, act.tests_tree,
+                                               control_override)
+                               testbed.blamed += act.blamed
+                               run_tests(act, stanzas)
                        control_override = None
                if act.kind == 'tree':
                        testbed.blame('arg:'+act.af.spec)
index 3050df86473125a1cc370bb9685b80917b3ff761..3140dc4740336fba1495976e0df46622b024c4b6 100644 (file)
@@ -25,131 +25,80 @@ adt\-run should be invoked (unless options to the contrary are
 supplied) in the top level directory of the built source tree, on the
 host.  The package should be installed on the testbed.
 
-.SH OPTIONS
-.TP
-.BR --tests-tree [ -tb ] " " \fIdirectory\fR
-Specifies that the built source tree can be found in
-.IR directory .
-(Default: adt-run's current directory, on the host.)
-.TP
-.BR --source [ -tb ] " " \fIdsc\fR
-Builds \fIdsc\fR, and then uses the tests in that tree, as well as
-testing the packages from this build and using them to satisfy
-dependencies.  Equivalent to \fB--source-binaries-tests\fR.  Order is
-significant: each \fB--source\fR* option only affects the packages
-used pursuant to \fIsubsequent\fR \fB--source\fR or \fB--binary\fR options.
-.TP
-.BR --source [[ -forbuilds | -installforbuilds ][ -fortests | installfortests ]| -binaries | -install ][ -tests ][ -tb "] \fIdsc\fR"
-Builds \fIdsc\fR (installing dependencies as appropriate), and then
-uses the resulting binary packages to satisfy dependencies (not
-\fBinstall\fR, or just \fBbinaries\fR), installs the resulting
-binaries unconditionally (subject to
-\fB--package-filter-source-install\fR) (\fBinstall\fR*), or uses the
-resulting build tree's test suite (\fBtests\fR).  The scope of the use
-of the generated binary packages can be limited to building other
-source packages (\fBforbuilds\fR, only available if the virtualisation used
-supports filesystem rollback) or running the tests
-(\fBforbinaries\fR).  The subcomponents of the \fB--source-\fR* option
-must be specified in the order shown.
-.TP
-.BR --binary [ -tb ] " " \fIfilename\fR
-Specifies that \fIfilename\fR (which should be a \fB.deb\fR) should
-be used to satisfy dependencies, both during building and testing,
-(which will include using it as the specific package under test).
-Equivalent to \fB--binary-forbuilds-fortests\fR.
-.TP
-.BR --binary [ -installforbuilds | -forbuilds ][ -installfortests | -fortests ] -tb "] \fIpkg\fR"
-Uses the specified binary package to satisfy dependencies, or
-unconditionally installs it (\fBinstall\fR), during building
-(\fBforbuilds\fR) or during testing (\fBfortests\fR), as specified.
-Order is significant: each \fB--source\fR* option only affects the
-packages installed pursuant to \fIsubsequent\fR \fB--source\fR or \fB--binary\fR options.
-.TP
-.BR --user " " \fIusername\fR
-Specifies that commands on the testbed should generally be run as
-\fIusername\fR; this option does not make much sense unless the
-virtualisation environment provides root on the testbed.  Commands are
-run via \fBsu -c\fR; affected commands are tests (other than those
-which declare that they need root), source package builds and source
-package binary generation (via fakeroot).  If the testbed advertises
-the \fBroot-on-testbed\fR and \fBsuggest-normal-user=\fR capabilities,
-the suggested normal is used as the default; otherwise the default is
-to run commands as the testbed's default user (this can be explicitly
-requested by specifying the empty string for \fIusername\fR).
-.TP
-.BR --fakeroot " " \fIfakeroot-command\fR
-Specifies that \fBdebian/rules binary\fR should be run via
-\fIfakeroot-command\fR.  The default is \fBfakeroot\fR if the testbed
-doesn't offer us root or if we are running \fBdebian/rules build\fR as
-a normal user according to a specified or default value of
-\fB--user\fR, and no wrapper otherwise.
-
-The default is to run without any special
-measures if the test
-
+.SH PROCESSING INSTRUCTIONS
+.TP
+.BR --tests-tree " " \fIdirectory\fR
+Specifies that tests from the built source tree
+.IR directory
+should be run.
+.TP
+.BR --source " " \fIdsc\fR
+Builds \fIdsc\fR.  The resulting binaries will (by default) be used to
+satisfy dependencies.  The tests from that built tree will also be run
+(by default).  The ordering is significant: each \fB--source\fR option
+should precede options whose dependencies are to be satisfied by the
+binaries it produces.
+.TP
+.BR --binary " " \fIdeb\fR
+Specifies that \fIdeb\fR should be used.  By default it will be used
+to satisfy dependencies, both during building and testing, but not
+necessarily installed.  The ordering is significant, as for
+\fB--source\fR.
+.TP
+.I filename
+Bare filename arguments are processed as if
+.BR --tests-tree ", " --source " or " --binary
+was specified; the nature of the argument is guessed from the form of
+the filename.  (So in the case of \fB--tests-tree\fR, either the
+option must be specified, or the filename must end in a slash.)
+.SH PROCESSING OPTIONS
+These affect modify processing instructions.  Unless stated
+otherwise, they affect all subsequent options.
+.TP
+.BR --paths-testbed | --paths-host
+Specifies that subsequent pathnames in command-line arguments refer to
+files on the testbed, or on the host, respectively.  The default is
+\fB--paths-host\fR.
+.TP
+.BR --sources-tests | --sources-no-tests
+Specifies that the tests in subsequent \fB--source\fR arguments should
+(or should not) be run.
+.TP
+.BR --built-binaries-filter= \fIpattern\fB,\fIpattern\fB,\fR...
+Specifies that only binaries whose package names match one of the
+specified patterns should be used; others will be ignored.  This
+option applies to subsequent \fB--source\fR arguments.
+.TP
+.BR --no-built-binaries
+Specifies that all built binaries should be ignored completely;
+equivalent to
+.BR --built-binaries-filter=_
+(since no package name ever contains \fB_\fR).
+.TP
+.B --binaries=ignore | --binaries=auto | --binaries=install
+Specifies that binary package (in subsequently specified
+\fB--binary\fR arguments, or resulting from subsequently specified
+\fB--source\fR arguments and not filtered out) should be ignored, used
+only to satisfy dependencies, or installed unconditionally,
+respectively.  Equivalent to specifying both
+.BR --binaries-forbuilds " and " --binaries-fortests .
 .TP
-.BR --install-binary [ -tb ] " " \fIfilename\fR
-Specifies that \fIfilename\fR (which should be a \fB.deb\fR) should be
-unconditionally installed on the testbed before any tests are run (but
-after any necessary building).  This can be used to test a specific
-binary package.
-.TP
-.BR --from-source [ -tb ] " " \fIsource\fR
-Specifies that \fIsource\fR (which should be a \fB.dsc\fR) should be
-built on the testbed and then the resulting \fB.deb\fR's
-used to satisfy dependencies where necessary.
-.TP
-.BR --install-from-source [ -tb ] " " \fIsource\fR
-Specifies that \fIsource\fR (which should be a \fB.dsc\fR) should be
-built on the testbed and then (some subset of) the \fB.deb\fR's which
-result should be installed.
-;.TP
-;.BR --without-depends | --with-depends-only | --with-depends | --with-recommends
-;Specifies dependency handling: These options control whether
-;dependencies necessary for building and installing the packages as
-;requested will be installed (and possibly deinstalled, if there are
-;conflicts).
-;.IP
-;Each option controls installation of the dependencies for
-;all subsequent
-;.BR --build-source ", " --install-package " and " --install-from-source
-;options, until the next dependency handling option; the last
-;dependency handling option controls whether dependencies specified in
-;the actual test control file are installed.
-;.IP
-;The four handling modes are to honour, respectively: no dependencies, only
-;\fBDepends\fR, everything except \fBRecommends\fR and \fBSuggests\fR,
-;and everything except \fBSuggests\fR.
-;The default is \fB--without-depends\fR (it is as if this was
-;specified at the start of the command line).
-;.TP
-;.BR --package-filter-dependency " [!]\fIpattern\fR[,[!]\fIpattern\fR...]"
-;Limits the packages installed (or removed) due to dependencies to
-;those matching the specified filter.  The filter is a comma-separated
-;list of glob patterns (which must match the whole package name); each
-;optionally preceded by \fB!\fR (which indicates that matching packages
-;should not be installed).  The first pattern found determines; if no
-;pattern matches, then the package is taken to match the filter iff the
-;last pattern had a \fB!\fR.
-.TP
-.BR --package-filter-from-source " [!]\fIpattern\fR[,[!]\fIpattern\fR...]"
-Limits the packages installed directly due to
-.B --install-from-source
-directives; the patterns are interpreted as for
-.BR --package-filter-dependency .
-.TP
-.BR --control [ -tb ] " " \fIcontrol\fR
-Specifies that
-.I control
-should be used as the test control file instead of
-.B debian/tests/control
-in the build tree.  Note that it is not an error for this file not to
-exist; that just means that there are no tests.
-.TP
-.BR --output-dir [ -tb ] " " \fIoutput-dir\fR
+.BI --binaries-forbuilds= ...
+Like \fB--binaries=\fR but only changes the handling during package
+building: packages will be ignored, used for dependencies, or
+unconditionally installed, when a source package is built.
+.TP
+.BI --binaries-fortests= ...
+Like \fB--binaries=\fR but only changes the handling during testing:
+packages will be ignored, used for dependencies (including as the
+package under test), or unconditionally installed, when tests are run
+(as a result of either \fB--source\fR or \fB--build-tree\fR).
+.SH OTHER OPTIONS
+.TP
+.BI --output-dir " " \fIoutput-dir\fR
 Specifies that stderr and stdout from the tests should be placed in
 .IR output-dir .
-The files are named
+The files were previously named
 .BI stderr- test
 and
 .BI stdout- test
@@ -162,6 +111,42 @@ and
 .BR --install-from-source ),
 .BI log-install- j
 .RI "(for the installation logs from the " j "th installation or removal)".
+Names have probably changed since this manual was written.
+.TP
+.BI --user= user
+Run builds and tests as \fIuser\fR on the testbed.  This needs root on
+the testbed; if root on the testbed is not available then builds and
+tests run as whatever user is provided.
+.TP
+.BI --gain-root= gain-root
+Prefixes
+.B debian/rules binary
+with
+.RB gain-root .  The default is not to use anything, except that if
+\fB--user\fR is supplied or root on the testbed is not available the
+default is \fBfakeroot\fR.
+.TP
+.BI --tmp-dir= tmpdir
+Specifies that \fItmpdir\fR should be used instead of a fresh
+temporary directory on the host.  \fItmpdir\fR will be created if
+necessary, and emptied of all of its contents before \fBadt-run\fR
+starts, and it will not be cleaned out afterwards.  \fItmpdir\fR is
+not affected by \fB--paths-testbed\fR.
+.B NOTE
+again that all of the contents of \fItmpdir\fR will be \fBdeleted\fR.
+.TP
+.BI --gnupg-home= dir
+Uses \fIdir\fR as the \fBGNUPGHOME\fR for local apt archive signing.
+The specified directory should not contain keyrings containing other
+unrelated keys, since \fBadt-run\fR does not specify to \fBgpg\fR
+which keys to use.  The default is
+.BR $HOME/.autopkgtest .
+\fB--paths-testbed\fR has no effect on this option.
+.TP
+.B --gnupg-home=fresh
+Use a fresh temporary directory and generate fresh keys each run.
+This can be very slow and depends on the availability of sufficient
+quantities of high-quality entropy.
 .TP
 .BR -d " | " --debug
 Enables debugging output.  Probably not hugely interesting.
@@ -172,18 +157,10 @@ to invoke.  All the remaining arguments and options after
 .B ---
 are passed to the virtualisation server program.
 
-.SS NOTES
-Some options which come in variants with and without
-.BR -tb .
-These specify paths on the testbed and the host, respectively.  The
-data will be copied by
-.B adt-run
-to where it is needed.
-
 .SH OUTPUT FORMAT
 During a normal test run, one line is printed for each test.  This
-consists of the name of the test, some horizontal whitespace, and
-either
+consists of a short string identifying the test, some horizontal
+whitespace, and either
 .B PASS
 or
 .BR FAIL " reason"
@@ -192,6 +169,10 @@ or
 where the pass/fail indication is separated by any reason by some
 horizontal whitespace.
 
+The string to identify the test consists of a short alphanumeric
+string invented by \fBadt-run\fR to distinguish different command-line
+arguments, followed by a hyphen and the test name.
+
 Sometimes a
 .B SKIP
 will be reported when the name of the test is not known or not
@@ -203,6 +184,21 @@ In this case
 .B *
 will appear where the name of the test should be.
 
+Also, the output:
+.BR blame:  ...
+may be produced, where the right hand side is
+.BI arg: argument
+(representing a pathname found in a command line argument),
+.BI dsc: package
+(a source package name),
+.BI deb: package
+(a binary package name)
+or possibly other strings to be determined.  In case of an erroneous
+package (see \fBEXIT STATUS\fR) this indicates which arguments and/or
+packages might have contributed to the problem; the ones which were
+processed most recently and which are therefore most likely to be the
+cause of a problem are listed last.
+
 .SH EXIT STATUS
 0      all tests passed
 .br