krb5 commit: Add marks to longer Python test scripts

Greg Hudson <[email protected]>
Newsgroups gmane.comp.encryption.kerberos.cvs
Message-ID <[email protected]>
https://github.com/krb5/krb5/commit/0879b7d506c0a1bb77c85d7499a55d91743ad9a8
commit 0879b7d506c0a1bb77c85d7499a55d91743ad9a8
Author: Greg Hudson <[email protected]>
Date:   Wed Apr 18 19:21:56 2018 -0400

    Add marks to longer Python test scripts

 src/appl/gss-sample/t_gss_sample.py |    4 ++++
 src/lib/krb5/ccache/t_cccol.py      |    6 ++++++
 src/lib/krb5/krb/t_vfy_increds.py   |    9 +++++++++
 src/tests/t_authdata.py             |   24 ++++++++++++++++++++++++
 src/tests/t_ccache.py               |    9 +++++++++
 src/tests/t_crossrealm.py           |    7 +++++++
 src/tests/t_dump.py                 |    4 ++++
 src/tests/t_etype_info.py           |    2 ++
 src/tests/t_general.py              |    7 +++++++
 src/tests/t_hostrealm.py            |   12 ++++++++++++
 src/tests/t_iprop.py                |   17 +++++++++++++++++
 src/tests/t_kadmin_acl.py           |   18 ++++++++++++++++++
 src/tests/t_kdb.py                  |   19 +++++++++++++++++++
 src/tests/t_keytab.py               |   10 ++++++++++
 src/tests/t_localauth.py            |   12 ++++++++++++
 src/tests/t_mkey.py                 |   16 ++++++++++++++++
 src/tests/t_otp.py                  |    5 +++++
 src/tests/t_pkinit.py               |   25 +++++++++++++++++++++++++
 src/tests/t_policy.py               |   10 ++++++++--
 src/tests/t_preauth.py              |   14 ++++++++++++++
 src/tests/t_proxy.py                |   15 +++++++++++++++
 src/tests/t_pwqual.py               |    4 ++++
 src/tests/t_rdreq.py                |   13 +++++++++++++
 src/tests/t_referral.py             |    5 +++++
 src/tests/t_renew.py                |    9 +++++++++
 src/tests/t_skew.py                 |    4 ++++
 src/tests/t_sn2princ.py             |    5 +++++
 src/tests/t_spake.py                |    8 +++++++-
 src/tests/t_y2038.py                |    5 +++++
 29 files changed, 295 insertions(+), 3 deletions(-)

diff --git a/src/appl/gss-sample/t_gss_sample.py b/src/appl/gss-sample/t_gss_sample.py
index 0299e45..c053b27 100755
--- a/src/appl/gss-sample/t_gss_sample.py
+++ b/src/appl/gss-sample/t_gss_sample.py
@@ -95,22 +95,26 @@ def kt_test(realm, options, server_options=[]):
 for realm in multipass_realms():
     ccache_save(realm)
 
+    mark('TGS')
     tgs_test(realm, ['-krb5'])
     tgs_test(realm, ['-spnego'])
     tgs_test(realm, ['-iakerb'], ['-iakerb'])
     # test default (i.e., krb5) mechanism with GSS_C_DCE_STYLE
     tgs_test(realm, ['-dce'])
 
+    mark('pw')
     pw_test(realm, ['-krb5'])
     pw_test(realm, ['-spnego'])
     pw_test(realm, ['-iakerb'], ['-iakerb'])
     pw_test(realm, ['-dce'])
 
+    mark('wrong pw')
     wrong_pw_test(realm, ['-krb5'])
     wrong_pw_test(realm, ['-spnego'])
     wrong_pw_test(realm, ['-iakerb'], ['-iakerb'], True)
     wrong_pw_test(realm, ['-dce'])
 
+    mark('client keytab')
     realm.extract_keytab(realm.user_princ, realm.client_keytab)
     kt_test(realm, ['-krb5'])
     kt_test(realm, ['-spnego'])
diff --git a/src/lib/krb5/ccache/t_cccol.py b/src/lib/krb5/ccache/t_cccol.py
index f7f1785..0d78b5c 100755
--- a/src/lib/krb5/ccache/t_cccol.py
+++ b/src/lib/krb5/ccache/t_cccol.py
@@ -81,20 +81,24 @@ def cursor_test(testname, args, expected):
              'Expected output:\n\n' + '\n'.join(expected) + '\n\n' +
              'Actual output:\n\n' + '\n'.join(outlines))
 
+mark('FILE cursor')
 fccname = 'FILE:%s' % realm.ccache
 cursor_test('file-default', [], [fccname])
 cursor_test('file-default2', [realm.ccache], [fccname])
 cursor_test('file-default3', [fccname], [fccname])
 
+mark('DIR cursor')
 cursor_test('dir', [dccname], [duser, dalice, dbob])
 cursor_test('dir-subsidiary', [duser], [duser])
 cursor_test('dir-nofile', [dnoent], [])
 
 if test_keyring:
+    mark('KEYRING cursor')
     cursor_test('keyring', [krccname], [kruser, kralice, krbob])
     cursor_test('keyring-subsidiary', [kruser], [kruser])
     cursor_test('keyring-noent', [krnoent], [])
 
+mark('MEMORY cursor')
 mfoo = 'MEMORY:foo'
 mbar = 'MEMORY:bar'
 cursor_test('filemem', [fccname, mfoo, mbar], [fccname, mfoo, mbar])
@@ -103,6 +107,7 @@ if test_keyring:
     cursor_test('keyringmem', [krccname, mfoo], [kruser, kralice, krbob, mfoo])
 
 # Test krb5_cccol_have_content.
+mark('krb5_cccol_have_content')
 realm.run(['./t_cccursor', dccname, 'CONTENT'])
 realm.run(['./t_cccursor', fccname, 'CONTENT'])
 realm.run(['./t_cccursor', realm.ccache, 'CONTENT'])
@@ -112,6 +117,7 @@ if test_keyring:
     cleanup_keyring('@s', col_ringname)
 
 # Make sure FILE doesn't yield a nonexistent default cache.
+mark('FILE nonexistent')
 realm.run([kdestroy])
 cursor_test('noexist', [], [])
 realm.run(['./t_cccursor', fccname, 'CONTENT'], expected_code=1)
diff --git a/src/lib/krb5/krb/t_vfy_increds.py b/src/lib/krb5/krb/t_vfy_increds.py
index c820cc6..1e8e262 100755
--- a/src/lib/krb5/krb/t_vfy_increds.py
+++ b/src/lib/krb5/krb/t_vfy_increds.py
@@ -27,17 +27,20 @@ from k5test import *
 realm = K5Realm()
 
 # Verify the default test realm credentials with the default keytab.
+mark('default keytab')
 realm.run(['./t_vfy_increds'])
 realm.run(['./t_vfy_increds', '-n'])
 
 # Verify after updating the keytab (so the keytab contains an outdated
 # version 1 key followed by an up-to-date version 2 key).
+mark('updated keytab')
 realm.run([kadminl, 'ktadd', realm.host_princ])
 realm.run(['./t_vfy_increds'])
 realm.run(['./t_vfy_increds', '-n'])
 
 # Bump the host key without updating the keytab and make sure that
 # verification fails as we expect it to.
+mark('outdated keytab')
 realm.run([kadminl, 'change_password', '-randkey', realm.host_princ])
 realm.run(['./t_vfy_increds'], expected_code=1)
 realm.run(['./t_vfy_increds', '-n'], expected_code=1)
@@ -47,6 +50,7 @@ realm.run(['./t_vfy_increds', '-n'], expected_code=1)
 # matches.  Verify after updating the keytab with a host service
 # principal that has hostname that doesn't match the host running the
 # test.  Verify should succeed, with or without nofail.
+mark('hostname mismatch')
 realm.run([kadminl, 'addprinc', '-randkey', 'host/wrong.hostname'])
 realm.run([kadminl, 'ktadd', 'host/wrong.hostname'])
 realm.run(['./t_vfy_increds'])
@@ -54,6 +58,7 @@ realm.run(['./t_vfy_increds', '-n'])
 
 # Remove the keytab and verify again.  This should succeed if nofail
 # is not set, and fail if it is set.
+mark('no keytab')
 os.remove(realm.keytab)
 realm.run(['./t_vfy_increds'])
 realm.run(['./t_vfy_increds', '-n'], expected_code=1)
@@ -65,6 +70,7 @@ realm.run(['./t_vfy_increds', '-n'], expected_code=1)
 # set.  (An empty keytab file appears as corrupt to keytab calls,
 # causing a KRB5_KEYTAB_BADVNO error, so any tightening of the
 # krb5_verify_init_creds semantics needs to take this into account.)
+mark('empty keytab')
 open(realm.keytab, 'w').close()
 realm.run(['./t_vfy_increds'])
 realm.run(['./t_vfy_increds', '-n'], expected_code=1)
@@ -73,6 +79,7 @@ os.remove(realm.keytab)
 # Add an NFS service principal to keytab.  Verify should ignore it by
 # default (succeeding unless nofail is set), but should verify with it
 # when it is specifically requested.
+mark('keytab with NFS principal')
 realm.run([kadminl, 'addprinc', '-randkey', realm.nfs_princ])
 realm.run([kadminl, 'ktadd', realm.nfs_princ])
 realm.run(['./t_vfy_increds'])
@@ -83,6 +90,7 @@ realm.run(['./t_vfy_increds', '-n', realm.nfs_princ])
 # Invalidating the NFS keys in the keytab.  We should get the same
 # results with the default principal argument, but verification should
 # now fail if we request it specifically.
+mark('keytab with outdated NFS principal')
 realm.run([kadminl, 'change_password', '-randkey', realm.nfs_princ])
 realm.run(['./t_vfy_increds'])
 realm.run(['./t_vfy_increds', '-n'], expected_code=1)
@@ -91,6 +99,7 @@ realm.run(['./t_vfy_increds', '-n', realm.nfs_princ], expected_code=1)
 
 # Spot-check that verify_ap_req_nofail works equivalently to the
 # programmatic nofail option.
+mark('verify_ap_req_nofail')
 realm.stop()
 conf = {'libdefaults': {'verify_ap_req_nofail': 'true'}}
 realm = K5Realm(krb5_conf=conf)
diff --git a/src/tests/t_authdata.py b/src/tests/t_authdata.py
index 8a577b4..eef701a 100644
--- a/src/tests/t_authdata.py
+++ b/src/tests/t_authdata.py
@@ -10,12 +10,14 @@ realm = K5Realm(krb5_conf=conf)
 # With no requested authdata, we expect to see SIGNTICKET (512) in an
 # if-relevant container and the greet authdata in a kdc-issued
 # container.
+mark('baseline authdata')
 out = realm.run(['./adata', realm.host_princ])
 if '?512: ' not in out or '^-42: Hello' not in out:
     fail('expected authdata not seen for basic request')
 
 # Requested authdata is copied into the ticket, with KDC-only types
 # filtered out.  (128 is win2k-pac, which should be filtered.)
+mark('request authdata')
 out = realm.run(['./adata', realm.host_princ, '-5', 'test1', '?-6', 'test2',
                  '128', 'fakepac', '?128', 'ifrelfakepac',
                  '^-8', 'fakekdcissued', '?^-8', 'ifrelfakekdcissued'])
@@ -24,11 +26,13 @@ if ' -5: test1' not in out or '?-6: test2' not in out:
 if 'fake' in out:
     fail('KDC-only authdata not filtered for request with authdata')
 
+mark('AD-MANDATORY-FOR-KDC')
 realm.run(['./adata', realm.host_princ, '!-1', 'mandatoryforkdc'],
           expected_code=1, expected_msg='KDC policy rejects request')
 
 # The no_auth_data_required server flag should suppress SIGNTICKET,
 # but not module or request authdata.
+mark('no_auth_data_required server flag')
 realm.run([kadminl, 'ank', '-randkey', '+no_auth_data_required', 'noauth'])
 realm.extract_keytab('noauth', realm.keytab)
 out = realm.run(['./adata', 'noauth', '-2', 'test'])
@@ -39,6 +43,7 @@ if '512: ' in out:
 
 # Cross-realm TGT requests should also suppress SIGNTICKET, but not
 # module or request authdata.
+mark('cross-realm')
 realm.addprinc('krbtgt/XREALM')
 realm.extract_keytab('krbtgt/XREALM', realm.keytab)
 out = realm.run(['./adata', 'krbtgt/XREALM', '-3', 'test'])
@@ -67,6 +72,7 @@ else:
 
     # SIGNTICKET and module authdata should be suppressed for
     # anonymous tickets, but not request authdata.
+    mark('anonymous')
     out = realm.run(['./adata', realm.host_princ, '-4', 'test'])
     if ' -4: test' not in out:
         fail('expected authdata not seen for anonymous request')
@@ -95,37 +101,45 @@ realm2.extract_keytab(realm2.host_princ, realm.keytab)
 realm2.extract_keytab('krbtgt/LOCAL', realm.keytab)
 
 # AS request to local-realm service
+mark('AS-REQ to local service auth indicator')
 realm.kinit(realm.user_princ, password('user'),
             ['-X', 'indicators=indcl', '-r', '2d', '-S', realm.host_princ])
 realm.run(['./adata', realm.host_princ], expected_msg='+97: [indcl]')
 
 # Ticket modification request
+mark('ticket modification auth indicator')
 realm.kinit(realm.user_princ, None, ['-R', '-S', realm.host_princ])
 realm.run(['./adata', realm.host_princ], expected_msg='+97: [indcl]')
 
 # AS request to cross TGT
+mark('AS-REQ to cross TGT auth indicator')
 realm.kinit(realm.user_princ, password('user'),
             ['-X', 'indicators=indcl', '-S', 'krbtgt/FOREIGN'])
 realm.run(['./adata', 'krbtgt/FOREIGN'], expected_msg='+97: [indcl]')
 
 # Multiple indicators
+mark('AS multiple indicators')
 realm.kinit(realm.user_princ, password('user'),
             ['-X', 'indicators=indcl indcl2 indcl3'])
 realm.run(['./adata', realm.krbtgt_princ],
           expected_msg='+97: [indcl, indcl2, indcl3]')
 
 # AS request to local TGT (resulting creds are used for TGS tests)
+mark('AS-REQ to local TGT auth indicator')
 realm.kinit(realm.user_princ, password('user'), ['-X', 'indicators=indcl'])
 realm.run(['./adata', realm.krbtgt_princ], expected_msg='+97: [indcl]')
 
 # Local TGS request for local realm service
+mark('TGS-REQ to local service auth indicator')
 realm.run(['./adata', realm.host_princ], expected_msg='+97: [indcl]')
 
 # Local TGS request for cross TGT service
+mark('TGS-REQ to cross TGT auth indicator')
 realm.run(['./adata', 'krbtgt/FOREIGN'], expected_msg='+97: [indcl]')
 
 # We don't yet have support for passing auth indicators across realms,
 # so just verify that indicators don't survive cross-realm requests.
+mark('TGS-REQ to foreign service auth indicator')
 out = realm.run(['./adata', realm2.krbtgt_princ])
 if '97:' in out:
     fail('auth-indicator seen in cross TGT request to local TGT')
@@ -137,10 +151,12 @@ if '97:' in out:
     fail('auth-indicator seen in cross TGT request to service')
 
 # Test that the CAMMAC signature still works during a krbtgt rollover.
+mark('CAMMAC signature across krbtgt rollover')
 realm.run([kadminl, 'cpw', '-randkey', '-keepold', realm.krbtgt_princ])
 realm.run(['./adata', realm.host_princ], expected_msg='+97: [indcl]')
 
 # Test indicator enforcement.
+mark('auth indicator enforcement')
 realm.addprinc('restricted')
 realm.run([kadminl, 'setstr', 'restricted', 'require_auth', 'superstrong'])
 realm.run([kvno, 'restricted'], expected_code=1,
@@ -155,6 +171,7 @@ realm.run([kvno, 'restricted'])
 # Regression test for one manifestation of #8139: ensure that
 # forwarded TGTs obtained across a TGT re-key still work when the
 # preferred krbtgt enctype changes.
+mark('#8139 regression test')
 realm.kinit(realm.user_princ, password('user'), ['-f'])
 realm.run([kadminl, 'cpw', '-randkey', '-keepold', '-e', 'des3-cbc-sha1',
            realm.krbtgt_princ])
@@ -162,6 +179,7 @@ realm.run(['./forward'])
 realm.run([kvno, realm.host_princ])
 
 # Repeat the above test using a renewed TGT.
+mark('#8139 regression test (renewed TGT)')
 realm.kinit(realm.user_princ, password('user'), ['-r', '2d'])
 realm.run([kadminl, 'cpw', '-randkey', '-keepold', '-e', 'aes128-cts',
            realm.krbtgt_princ])
@@ -195,6 +213,7 @@ realm.extract_keytab('noauthdata', realm.keytab)
 realm.start_kdc()
 
 # S4U2Self (should have no indicators since client did not authenticate)
+mark('S4U2Self (no auth indicators expected)')
 realm.kinit('service/1', None, ['-k', '-f', '-X', 'indicators=inds1'])
 realm.run([kvno, '-U', 'user', 'service/1'])
 out = realm.run(['./adata', '-p', realm.user_princ, 'service/1'])
@@ -202,6 +221,7 @@ if '97:' in out:
     fail('auth-indicator present in S4U2Self response')
 
 # S4U2Proxy (indicators should come from evidence ticket, not TGT)
+mark('S4U2Proxy (auth indicators from evidence ticket expected)')
 realm.kinit(realm.user_princ, None, ['-k', '-f', '-X', 'indicators=indcl',
                                      '-S', 'service/1', '-c', usercache])
 realm.run(['./s4u2proxy', usercache, 'service/2'])
@@ -211,6 +231,7 @@ if '+97: [indcl]' not in out or '[inds1]' in out:
 
 # Test that KDB module authdata is included in an AS request, by
 # default or with an explicit PAC request.
+mark('AS-REQ KDB module authdata')
 realm.kinit(realm.user_princ, None, ['-k'])
 realm.run(['./adata', realm.krbtgt_princ],
           expected_msg='-456: db-authdata-test')
@@ -220,16 +241,19 @@ realm.run(['./adata', realm.krbtgt_princ],
 
 # Test that KDB module authdata is suppressed in an AS request by a
 # negative PAC request.
+mark('AS-REQ KDB module authdata client supression')
 realm.kinit(realm.user_princ, None, ['-k', '--no-request-pac'])
 out = realm.run(['./adata', realm.krbtgt_princ])
 if '-456: db-authdata-test' in out:
     fail('DB authdata not suppressed by --no-request-pac')
 
 # Test that KDB authdata is included in a TGS request by default.
+mark('TGS-REQ KDB authdata')
 realm.run(['./adata', 'service/1'], expected_msg='-456: db-authdata-test')
 
 # Test that KDB authdata is suppressed in a TGS request by the
 # +no_auth_data_required flag.
+mark('TGS-REQ KDB authdata service suppression')
 out = realm.run(['./adata', 'noauthdata'])
 if '-456: db-authdata-test' in out:
     fail('DB authdata not suppressed by +no_auth_data_required')
diff --git a/src/tests/t_ccache.py b/src/tests/t_ccache.py
index 61d549b..4ddb83a 100755
--- a/src/tests/t_ccache.py
+++ b/src/tests/t_ccache.py
@@ -34,14 +34,17 @@ if not test_keyring:
     skipped('keyring ccache tests', 'keyring support not built')
 
 # Test kdestroy and klist of a non-existent ccache.
+mark('no ccache')
 realm.run([kdestroy])
 realm.run([klist], expected_code=1, expected_msg='No credentials cache found')
 
 # Test kinit with an inaccessible ccache.
+mark('inaccessible ccache')
 realm.kinit(realm.user_princ, password('user'), flags=['-c', 'testdir/xx/yy'],
             expected_code=1, expected_msg='Failed to store credentials')
 
 # Test klist -s with a single ccache.
+mark('klist -s single ccache')
 realm.run([klist, '-s'], expected_code=1)
 realm.kinit(realm.user_princ, password('user'))
 realm.run([klist, '-s'])
@@ -57,9 +60,11 @@ realm.addprinc('bob', password('bob'))
 realm.addprinc('carol', password('carol'))
 
 def collection_test(realm, ccname):
+    cctype = ccname.partition(':')[0]
     oldccname = realm.env['KRB5CCNAME']
     realm.env['KRB5CCNAME'] = ccname
 
+    mark('%s collection, single cache' % cctype)
     realm.run([klist, '-A', '-s'], expected_code=1)
     realm.kinit('alice', password('alice'))
     realm.run([klist], expected_msg='Default principal: alice@')
@@ -73,6 +78,7 @@ def collection_test(realm, ccname):
         fail('Initial kdestroy failed to empty cache collection.')
     realm.run([klist, '-A', '-s'], expected_code=1)
 
+    mark('%s collection, multiple caches' % cctype)
     realm.kinit('alice', password('alice'))
     realm.kinit('carol', password('carol'))
     output = realm.run([klist, '-l'])
@@ -96,6 +102,7 @@ def collection_test(realm, ccname):
     # (only works with klist/kdestroy for now, not kinit/kswitch).
     realm.env['KRB5CCNAME'] = oldccname
 
+    mark('%s collection, command-line specifier' % cctype)
     realm.run([kdestroy, '-c', ccname])
     output = realm.run([klist, '-l', ccname])
     if 'carol@' in output or 'bob@' not in output or output.count('\n') != 4:
@@ -127,6 +134,7 @@ if test_keyring:
     cleanup_keyring('@s', col_ringname)
 
     # Test legacy keyring cache linkage.
+    mark('legacy keyring cache linkage')
     realm.env['KRB5CCNAME'] = 'KEYRING:' + cname
     realm.run([kdestroy, '-A'])
     realm.kinit(realm.user_princ, password('user'))
@@ -150,6 +158,7 @@ if test_keyring:
     cleanup_keyring('@s', col_ringname)
 
 # Test parameter expansion in default_ccache_name
+mark('default_ccache_name parameter expansion')
 realm.stop()
 conf = {'libdefaults': {'default_ccache_name': 'testdir/%{null}abc%{uid}'}}
 realm = K5Realm(krb5_conf=conf, create_kdb=False)
diff --git a/src/tests/t_crossrealm.py b/src/tests/t_crossrealm.py
index 4d595dc..cd7f4aa 100755
--- a/src/tests/t_crossrealm.py
+++ b/src/tests/t_crossrealm.py
@@ -61,6 +61,7 @@ def tgt(r1, r2):
 
 
 # Basic two-realm test with cross TGTs in both directions.
+mark('two realms')
 r1, r2 = cross_realms(2)
 test_kvno(r1, r2.host_princ, 'basic r1->r2')
 check_klist(r1, (tgt(r1, r1), tgt(r2, r1), r2.host_princ))
@@ -72,6 +73,7 @@ stop(r1, r2)
 # client in A.X will ask for a cross TGT to B.X, but A.X's KDC only
 # has a TGT for the intermediate realm X, so it will return that
 # instead.  The client will use that to get a TGT for B.X.
+mark('hierarchical realms')
 r1, r2, r3 = cross_realms(3, xtgts=((0,1), (1,2)), 
                           args=({'realm': 'A.X'}, {'realm': 'X'},
                                 {'realm': 'B.X'}))
@@ -84,6 +86,7 @@ stop(r1, r2, r3)
 # The client will walk its A->D capaths to get TGTs for B, then C,
 # then D.  The KDCs for C and D need capaths settings to avoid failing
 # transited checks, including a capaths for A->C.
+mark('client capaths')
 capaths = {'capaths': {'A': {'D': ['B', 'C'], 'C': 'B'}}}
 r1, r2, r3, r4 = cross_realms(4, xtgts=((0,1), (1,2), (2,3)),
                               args=({'realm': 'A'},
@@ -99,6 +102,7 @@ stop(r1, r2, r3, r4)
 # Test KDC capaths.  The KDCs for A and B have appropriate capaths
 # settings to determine intermediate TGTs to return, but the client
 # has no idea.
+mark('kdc capaths')
 capaths = {'capaths': {'A': {'D': ['B', 'C'], 'C': 'B'}, 'B': {'D': 'C'}}}
 r1, r2, r3, r4 = cross_realms(4, xtgts=((0,1), (1,2), (2,3)),
                               args=({'realm': 'A', 'krb5_conf': capaths},
@@ -111,6 +115,7 @@ stop(r1, r2, r3, r4)
 
 # A capaths value of '.' should enforce direct cross-realm, with no
 # intermediate.
+mark('direct cross-realm enforcement')
 capaths = {'capaths': {'A.X': {'B.X': '.'}}}
 r1, r2, r3 = cross_realms(3, xtgts=((0,1), (1,2)),
                           args=({'realm': 'A.X', 'krb5_conf': capaths},
@@ -122,6 +127,7 @@ stop(r1, r2, r3)
 # Test transited error.  The KDC for C does not recognize B as an
 # intermediate realm for A->C, so it refuses to issue a service
 # ticket.
+mark('transited error (three realms)')
 capaths = {'capaths': {'A': {'C': 'B'}}}
 r1, r2, r3 = cross_realms(3, xtgts=((0,1), (1,2)),
                           args=({'realm': 'A', 'krb5_conf': capaths},
@@ -134,6 +140,7 @@ stop(r1, r2, r3)
 # Test a different kind of transited error.  The KDC for D does not
 # recognize B as an intermediate realm for A->C, so it refuses to
 # verify the krbtgt/C@B ticket in the TGS AP-REQ.
+mark('transited error (four realms)')
 capaths = {'capaths': {'A': {'D': ['B', 'C'], 'C': 'B'}, 'B': {'D': 'C'}}}
 r1, r2, r3, r4 = cross_realms(4, xtgts=((0,1), (1,2), (2,3)),
                               args=({'realm': 'A', 'krb5_conf': capaths},
diff --git a/src/tests/t_dump.py b/src/tests/t_dump.py
index 8a9462b..2a424d9 100755
--- a/src/tests/t_dump.py
+++ b/src/tests/t_dump.py
@@ -25,6 +25,7 @@ f.close()
 
 # Destroy and load the database; check that the policies exist.
 # Spot-check principal and policy fields.
+mark('reload after dump')
 realm.run([kdb5_util, 'destroy', '-f'])
 realm.run([kdb5_util, 'load', dumpfile])
 out = realm.run([kadminl, 'getprincs'])
@@ -42,6 +43,7 @@ realm.run([kadminl, 'getpol', 'barney'],
           expected_msg='Number of old keys kept: 1')
 
 # Dump/load again, and make sure everything is still there.
+mark('second reload')
 realm.run([kdb5_util, 'dump', dumpfile])
 realm.run([kdb5_util, 'load', dumpfile])
 out = realm.run([kadminl, 'getprincs'])
@@ -64,6 +66,7 @@ realm.run([kdb5_util, 'load', srcdump])
 realm.run([kdb5_util, 'stash', '-P', 'master'])
 
 def dump_compare(realm, opt, srcfile):
+    mark('dump comparison against %s' % os.path.basename(srcfile))
     realm.run([kdb5_util, 'dump'] + opt + [dumpfile])
     if not cmp(srcfile, dumpfile, False):
         fail('Dump output does not match %s' % srcfile)
@@ -77,6 +80,7 @@ dump_compare(realm, ['-b7'], srcdump_b7)
 dump_compare(realm, ['-ov'], srcdump_ov)
 
 def load_dump_check_compare(realm, opt, srcfile):
+    mark('load check from %s' % os.path.basename(srcfile))
     realm.run([kdb5_util, 'destroy', '-f'])
     realm.run([kdb5_util, 'load'] + opt + [srcfile])
     realm.run([kadminl, 'getprincs'], expected_msg='user@')
diff --git a/src/tests/t_etype_info.py b/src/tests/t_etype_info.py
index b2eb0f7..00113aa 100644
--- a/src/tests/t_etype_info.py
+++ b/src/tests/t_etype_info.py
@@ -16,6 +16,7 @@ realm.run([kadminl, 'addprinc', '-nokey', '+requires_preauth', 'nokeyuser'])
 # Run the test harness for the given principal and request enctype
 # list.  Compare the output to the expected lines, ignoring order.
 def test_etinfo(princ, enctypes, expected_lines):
+    mark('etinfo test: %s %s' % (princ.partition('@')[0], enctypes))
     lines = realm.run(['./etinfo', princ, enctypes]).splitlines()
     if sorted(lines) != sorted(expected_lines):
         fail('Unexpected output for princ %s, etypes %s' % (princ, enctypes))
@@ -75,6 +76,7 @@ test_etinfo('nokeyuser', 'des3', [])
 
 # Verify that etype-info2 is included in a MORE_PREAUTH_DATA_REQUIRED
 # error if the client does optimistic preauth.
+mark('MORE_PREAUTH_DATA_REQUIRED test')
 realm.stop()
 testpreauth = os.path.join(buildtop, 'plugins', 'preauth', 'test', 'test.so')
 conf = {'plugins': {'kdcpreauth': {'module': 'test:' + testpreauth},
diff --git a/src/tests/t_general.py b/src/tests/t_general.py
index 91ad0cb..3490d28 100755
--- a/src/tests/t_general.py
+++ b/src/tests/t_general.py
@@ -3,15 +3,18 @@ from k5test import *
 
 for realm in multipass_realms(create_host=False):
     # Check that kinit fails appropriately with the wrong password.
+    mark('kinit wrong password failure')
     msg = 'Password incorrect while getting initial credentials'
     realm.run([kinit, realm.user_princ], input='wrong\n', expected_code=1,
               expected_msg=msg)
 
     # Check that we can kinit as a different principal.
+    mark('kinit with specified principal')
     realm.kinit(realm.admin_princ, password('admin'))
     realm.klist(realm.admin_princ)
 
     # Test FAST kinit.
+    mark('FAST kinit')
     fastpw = password('fast')
     realm.run([kadminl, 'ank', '-pw', fastpw, '+requires_preauth',
                'user/fast'])
@@ -25,6 +28,7 @@ for realm in multipass_realms(create_host=False):
 # Test that we can get initial creds with an empty password via the
 # API.  We have to disable the "empty" pwqual module to create a
 # principal with an empty password.  (Regression test for #7642.)
+mark('initial creds with empty password')
 conf={'plugins': {'pwqual': {'disable': 'empty'}}}
 realm = K5Realm(create_user=False, create_host=False, krb5_conf=conf)
 realm.run([kadminl, 'addprinc', '-pw', '', 'user'])
@@ -36,16 +40,19 @@ realm = K5Realm(create_host=False)
 
 # Regression test for #8454 (responder callback isn't used when
 # preauth is not required).
+mark('#8454 regression test')
 realm.run(['./responder', '-r', 'password=%s' % password('user'),
            realm.user_princ])
 
 # Test that WRONG_REALM responses aren't treated as referrals unless
 # they contain a crealm field pointing to a different realm.
 # (Regression test for #8060.)
+mark('#8060 regression test')
 realm.run([kinit, '-C', 'notfoundprinc'], expected_code=1,
           expected_msg='not found in Kerberos database')
 
 # Spot-check KRB5_TRACE output
+mark('KRB5_TRACE spot check')
 expected_trace = ('Sending initial UDP request',
                   'Received answer',
                   'Selected etype info',
diff --git a/src/tests/t_hostrealm.py b/src/tests/t_hostrealm.py
index 224c067..c14c926 100755
--- a/src/tests/t_hostrealm.py
+++ b/src/tests/t_hostrealm.py
@@ -42,6 +42,7 @@ def testd_error(realm, expected_error, msg, env=None):
 
 # The test2 module returns a fatal error on hosts beginning with 'z',
 # and an answer on hosts begining with 'a'.
+mark('test2 module')
 testh_error(realm, 'zoo', 'service not available', 'host_realm test2 z')
 testh(realm, 'abacus', ['a'], 'host_realm test2 a')
 
@@ -49,6 +50,7 @@ testh(realm, 'abacus', ['a'], 'host_realm test2 a')
 # 'X', due to [domain_realms].  There is also an entry for hostnames
 # ending in '1', but hostnames which appear to be IP or IPv6 addresses
 # should instead fall through to test1.
+mark('profile module')
 testh(realm, 'x', ['MATCH'], 'host_realm profile x')
 testh(realm, '.x', ['DOTMATCH'], 'host_realm profile .x')
 testh(realm, 'b.x', ['DOTMATCH'], 'host_realm profile b.x')
@@ -60,9 +62,11 @@ testh(realm, 'b:c.x', ['b:c', 'x'], 'host_realm profile b:c.x')
 testh(realm, 'X.', ['MATCH'], 'host_realm profile X.')
 
 # The test1 module returns a list of the hostname components.
+mark('test1 module')
 testh(realm, 'b.c.d', ['b', 'c', 'd'], 'host_realm test1')
 
 # If no module returns a result, we should get the referral realm.
+mark('no result')
 testh(realm, '', [''], 'host_realm referral realm')
 
 ###
@@ -76,10 +80,12 @@ def try_env(realm, testname, n):
 
 # The domain module will answer with the uppercased parent domain,
 # with no special configuration.
+mark('fallback: domain module')
 testf(realm, 'a.b.c', ['B.C'], 'fallback_realm domain a.b.c')
 
 # With realm_try_domains = 0, the hostname itself will be looked up as
 # a realm and returned if found.
+mark('fallback: realm_try_domains = 0')
 try0 = try_env(realm, 'try0', 0)
 testf(realm, 'krbtest.com', ['KRBTEST.COM'], 'fallback_realm try0', env=try0)
 testf(realm, 'a.b.krbtest.com', ['B.KRBTEST.COM'],
@@ -88,6 +94,7 @@ testf(realm, 'a.b.c', ['B.C'], 'fallback_realm try0 nomatch', env=try0)
 
 # With realm_try_domains = 2, the parent and grandparent will be
 # checked as well, but it stops there.
+mark('fallback: realm_try_domains = 2')
 try2 = try_env(realm, 'try2', 2)
 testf(realm, 'krbtest.com', ['KRBTEST.COM'], 'fallback_realm try2', env=try2)
 testf(realm, 'a.b.krbtest.com', ['KRBTEST.COM'],
@@ -97,10 +104,12 @@ testf(realm, 'a.b.c.krbtest.com', ['B.C.KRBTEST.COM'],
 
 # The test1 module answers with a list of components.  Use an IPv4
 # address to bypass the domain module.
+mark('fallback: test1 module')
 testf(realm, '1.2.3.4', ['1', '2', '3', '4'], 'fallback_realm test1')
 
 # If no module answers, the default realm is returned.  The test2
 # module returns an error when we try to look that up.
+mark('fallback: default realm')
 testf_error(realm, '', 'service not available', 'fallback_realm default')
 
 ###
@@ -108,10 +117,12 @@ testf_error(realm, '', 'service not available', 'fallback_realm default')
 ###
 
 # The test2 module returns an error.
+mark('default_realm: test2 module')
 testd_error(realm, 'service not available', 'default_realm test2')
 
 # The profile module returns the default realm from the profile.
 # Disable test2 to expose this behavior.
+mark('default_realm: profile module')
 disable_conf = {'plugins': {'hostrealm': {'disable': 'test2'}}}
 notest2 = realm.special_env('notest2', False, krb5_conf=disable_conf)
 testd(realm, 'KRBTEST.COM', 'default_realm profile', env=notest2)
@@ -119,6 +130,7 @@ testd(realm, 'KRBTEST.COM', 'default_realm profile', env=notest2)
 # The test1 module returns a list of two realms, of which we can only
 # see the first.  Remove the profile default_realm setting to expose
 # this behavior.
+mark('default_realm: test1 module')
 remove_default = {'libdefaults': {'default_realm': None}}
 nodefault_conf = dict(disable_conf.items() + remove_default.items())
 nodefault = realm.special_env('nodefault', False, krb5_conf=nodefault_conf)
diff --git a/src/tests/t_iprop.py b/src/tests/t_iprop.py
index 8e23cd5..13ef1ca 100755
--- a/src/tests/t_iprop.py
+++ b/src/tests/t_iprop.py
@@ -201,6 +201,7 @@ realm.run([kadminl, 'modprinc', '+allow_tix', pr2])
 check_ulog(6, 1, 6, [None, pr1, pr3, pr2, pr2, pr2])
 
 # Start kpropd for slave1 and get a full dump from master.
+mark('propagate M->1 full')
 kpropd1 = realm.start_kpropd(slave1, ['-d'])
 wait_for_prop(kpropd1, True, 1, 6)
 out = realm.run([kadminl, 'listprincs'], env=slave1)
@@ -209,6 +210,7 @@ if pr1 not in out or pr2 not in out or pr3 not in out:
 check_ulog(1, 6, 6, [None], slave1)
 
 # Make a change and check that it propagates incrementally.
+mark('propagate M->1 incremental')
 realm.run([kadminl, 'modprinc', '-allow_tix', pr2])
 check_ulog(7, 1, 7, [None, pr1, pr3, pr2, pr2, pr2, pr2])
 kpropd1.send_signal(signal.SIGUSR1)
@@ -227,6 +229,7 @@ realm.start_server([kadmind, '-r', realm.realm, '-nofork', '-proponly', '-W',
                     '-F', slave1_out_dump_path], 'starting...', slave1m)
 
 # Test similar default_realm and domain_realm map settings with -r realm.
+mark('propagate 1->3 full')
 slave3_in_dump_path = os.path.join(realm.testdir, 'dump.slave3.in')
 kpropd3 = realm.start_server([kpropd, '-d', '-D', '-r', realm.realm, '-P',
                               slave2_kprop_port, '-f', slave3_in_dump_path,
@@ -239,6 +242,7 @@ if pr1 not in out or pr2 not in out or pr3 not in out:
 check_ulog(1, 7, 7, [None], env=slave3)
 
 # Test an incremental propagation for the kpropd -r case.
+mark('propagate M->1->3 incremental')
 realm.run([kadminl, 'modprinc', '-maxlife', '20 minutes', pr1])
 check_ulog(8, 1, 8, [None, pr1, pr3, pr2, pr2, pr2, pr2, pr1])
 kpropd1.send_signal(signal.SIGUSR1)
@@ -254,6 +258,7 @@ realm.run([kadminl, '-r', realm.realm, 'getprinc', pr1], env=slave3,
 stop_daemon(kpropd3)
 
 # Test dissimilar default_realm and domain_realm map settings (no -r realm).
+mark('propagate 1->4 full')
 slave4_in_dump_path = os.path.join(realm.testdir, 'dump.slave4.in')
 kpropd4 = realm.start_server([kpropd, '-d', '-D', '-P', slave2_kprop_port,
                               '-f', slave4_in_dump_path, '-p', kdb5_util,
@@ -268,6 +273,7 @@ stop_daemon(kpropd4)
 # talking to the same host as master (we specify it anyway to exercise
 # the code), but slave2 defines iprop_port to $port8 so it will talk
 # to slave1.  Get a full dump from slave1.
+mark('propagate 1->2 full')
 kpropd2 = realm.start_server([kpropd, '-d', '-D', '-P', slave2_kprop_port,
                               '-f', slave2_in_dump_path, '-p', kdb5_util,
                               '-a', acl_file, '-A', hostname], 'ready', slave2)
@@ -279,6 +285,7 @@ if pr1 not in out or pr2 not in out or pr3 not in out:
 
 # Make another change and check that it propagates incrementally to
 # both slaves.
+mark('propagate M->1->2 incremental')
 realm.run([kadminl, 'modprinc', '-maxrenewlife', '22 hours', pr1])
 check_ulog(9, 1, 9, [None, pr1, pr3, pr2, pr2, pr2, pr2, pr1, pr1])
 kpropd1.send_signal(signal.SIGUSR1)
@@ -296,6 +303,7 @@ realm.run([kadminl, 'getprinc', pr1], env=slave2,
 # resync will use the old dump file and then propagate changes.
 # slave2 should still be in sync with slave1 after the resync, so make
 # sure it doesn't take a full resync.
+mark('propagate M->1->2 full')
 realm.run([kproplog, '-R'], slave1)
 check_ulog(1, 1, 1, [None], slave1)
 kpropd1.send_signal(signal.SIGUSR1)
@@ -307,6 +315,7 @@ check_ulog(3, 7, 9, [None, pr1, pr1], slave2)
 
 # Make another change and check that it propagates incrementally to
 # both slaves.
+mark('propagate M->1->2 incremental (after reset)')
 realm.run([kadminl, 'modprinc', '+allow_tix', pr2])
 check_ulog(10, 1, 10, [None, pr1, pr3, pr2, pr2, pr2, pr2, pr1, pr1, pr2])
 kpropd1.send_signal(signal.SIGUSR1)
@@ -319,6 +328,7 @@ check_ulog(4, 7, 10, [None, pr1, pr1, pr2], slave2)
 realm.run([kadminl, 'getprinc', pr2], env=slave2, expected_msg='Attributes:\n')
 
 # Create a policy and check that it propagates via full resync.
+mark('propagate M->1->2 full (new policy)')
 realm.run([kadminl, 'addpol', '-minclasses', '2', 'testpol'])
 check_ulog(1, 1, 1, [None])
 kpropd1.send_signal(signal.SIGUSR1)
@@ -333,6 +343,7 @@ realm.run([kadminl, 'getpol', 'testpol'], env=slave2,
           expected_msg='Minimum number of password character classes: 2')
 
 # Modify the policy and test that it also propagates via full resync.
+mark('propagate M->1->2 full (policy change)')
 realm.run([kadminl, 'modpol', '-minlength', '17', 'testpol'])
 check_ulog(1, 1, 1, [None])
 kpropd1.send_signal(signal.SIGUSR1)
@@ -347,6 +358,7 @@ realm.run([kadminl, 'getpol', 'testpol'], env=slave2,
           expected_msg='Minimum password length: 17')
 
 # Delete the policy and test that it propagates via full resync.
+mark('propgate M->1->2 full (policy delete)')
 realm.run([kadminl, 'delpol', 'testpol'])
 check_ulog(1, 1, 1, [None])
 kpropd1.send_signal(signal.SIGUSR1)
@@ -361,6 +373,7 @@ realm.run([kadminl, 'getpol', 'testpol'], env=slave2, expected_code=1,
           expected_msg='Policy does not exist')
 
 # Modify a principal on the master and test that it propagates incrementally.
+mark('propagate M->1->2 incremental (after policy changes)')
 realm.run([kadminl, 'modprinc', '-maxlife', '10 minutes', pr1])
 check_ulog(2, 1, 2, [None, pr1])
 kpropd1.send_signal(signal.SIGUSR1)
@@ -375,6 +388,7 @@ realm.run([kadminl, 'getprinc', pr1], env=slave2,
           expected_msg='Maximum ticket life: 0 days 00:10:00')
 
 # Delete a principal and test that it propagates incrementally.
+mark('propagate M->1->2 incremental (princ delete)')
 realm.run([kadminl, 'delprinc', pr3])
 check_ulog(3, 1, 3, [None, pr1, pr3])
 kpropd1.send_signal(signal.SIGUSR1)
@@ -389,6 +403,7 @@ realm.run([kadminl, 'getprinc', pr3], env=slave2, expected_code=1,
           expected_msg='Principal does not exist')
 
 # Rename a principal and test that it propagates incrementally.
+mark('propagate M->1->2 incremental (princ rename)')
 renpr = "quacked@" + realm.realm
 realm.run([kadminl, 'renprinc', pr1, renpr])
 check_ulog(6, 1, 6, [None, pr1, pr3, renpr, pr1, renpr])
@@ -408,6 +423,7 @@ realm.run([kadminl, 'getprinc', renpr], env=slave2)
 pr1 = renpr
 
 # Reset the ulog on the master to force a full resync.
+mark('propagate M->1->2 full (ulog reset)')
 realm.run([kproplog, '-R'])
 check_ulog(1, 1, 1, [None])
 kpropd1.send_signal(signal.SIGUSR1)
@@ -420,6 +436,7 @@ check_ulog(1, 1, 1, [None], slave2)
 # Stop the kprop daemons so we can test kpropd -t.
 stop_daemon(kpropd1)
 stop_daemon(kpropd2)
+mark('kpropd -t')
 
 # Test the case where no updates are needed.
 out = realm.run_kpropd_once(slave1, ['-d'])
diff --git a/src/tests/t_kadmin_acl.py b/src/tests/t_kadmin_acl.py
index 42bdf42..d5a1326 100755
--- a/src/tests/t_kadmin_acl.py
+++ b/src/tests/t_kadmin_acl.py
@@ -84,6 +84,7 @@ realm.addprinc('selected', 'oldpw')
 realm.addprinc('unselected', 'oldpw')
 for pw in (['-pw', 'newpw'], ['-randkey']):
     for ks in ([], ['-e', 'aes256-cts']):
+        mark('cpw: %s %s' % (repr(pw), repr(ks)))
         args = pw + ks
         kadmin_as(all_changepw, ['cpw'] + args + ['unselected'])
         kadmin_as(some_changepw, ['cpw'] + args + ['selected'])
@@ -101,6 +102,7 @@ for pw in (['-pw', 'newpw'], ['-randkey']):
 realm.run([kadminl, 'delprinc', 'selected'])
 realm.run([kadminl, 'delprinc', 'unselected'])
 
+mark('addpol')
 kadmin_as(all_add, ['addpol', 'policy'])
 realm.run([kadminl, 'delpol', 'policy'])
 kadmin_as(none, ['addpol', 'policy'], expected_code=1,
@@ -108,6 +110,7 @@ kadmin_as(none, ['addpol', 'policy'], expected_code=1,
 
 # addprinc can generate two different RPC calls depending on options.
 for ks in ([], ['-e', 'aes256-cts']):
+    mark('addprinc: %s' % repr(ks))
     args = ['-pw', 'pw'] + ks
     kadmin_as(all_add, ['addprinc'] + args + ['unselected'])
     realm.run([kadminl, 'delprinc', 'unselected'])
@@ -122,6 +125,7 @@ for ks in ([], ['-e', 'aes256-cts']):
     kadmin_as(some_add, ['addprinc'] + args + ['unselected'], expected_code=1,
               expected_msg="Operation requires ``add'' privilege")
 
+mark('delprinc')
 realm.addprinc('unselected', 'pw')
 kadmin_as(all_delete, ['delprinc', 'unselected'])
 realm.addprinc('selected', 'pw')
@@ -133,6 +137,7 @@ kadmin_as(some_delete, ['delprinc', 'unselected'], expected_code=1,
           expected_msg="Operation requires ``delete'' privilege")
 realm.run([kadminl, 'delprinc', 'unselected'])
 
+mark('getpol')
 kadmin_as(all_inquire, ['getpol', 'minlife'], expected_msg='Policy: minlife')
 kadmin_as(none, ['getpol', 'minlife'], expected_code=1,
           expected_msg="Operation requires ``get'' privilege")
@@ -140,6 +145,7 @@ realm.run([kadminl, 'modprinc', '-policy', 'minlife', 'none'])
 kadmin_as(none, ['getpol', 'minlife'], expected_msg='Policy: minlife')
 realm.run([kadminl, 'modprinc', '-clearpolicy', 'none'])
 
+mark('getprinc')
 realm.addprinc('selected', 'pw')
 realm.addprinc('unselected', 'pw')
 kadmin_as(all_inquire, ['getprinc', 'unselected'],
@@ -155,10 +161,12 @@ kadmin_as(none, ['getprinc', 'none'],
 realm.run([kadminl, 'delprinc', 'selected'])
 realm.run([kadminl, 'delprinc', 'unselected'])
 
+mark('listprincs')
 kadmin_as(all_list, ['listprincs'], expected_msg='K/[email protected]')
 kadmin_as(none, ['listprincs'], expected_code=1,
           expected_msg="Operation requires ``list'' privilege")
 
+mark('getstrs')
 realm.addprinc('selected', 'pw')
 realm.addprinc('unselected', 'pw')
 realm.run([kadminl, 'setstr', 'selected', 'key', 'value'])
@@ -173,6 +181,7 @@ kadmin_as(none, ['getstrs', 'none'], expected_msg='(No string attributes.)')
 realm.run([kadminl, 'delprinc', 'selected'])
 realm.run([kadminl, 'delprinc', 'unselected'])
 
+mark('modpol')
 out = kadmin_as(all_modify, ['modpol', '-maxlife', '1 hour', 'policy'],
                 expected_code=1)
 if 'Operation requires' in out:
@@ -180,6 +189,7 @@ if 'Operation requires' in out:
 kadmin_as(none, ['modpol', '-maxlife', '1 hour', 'policy'], expected_code=1,
           expected_msg="Operation requires ``modify'' privilege")
 
+mark('modprinc')
 realm.addprinc('selected', 'pw')
 realm.addprinc('unselected', 'pw')
 kadmin_as(all_modify, ['modprinc', '-maxlife', '1 hour',  'unselected'])
@@ -195,6 +205,7 @@ kadmin_as(some_modify, ['modprinc', '-maxlife', '1 hour', 'unselected'],
 realm.run([kadminl, 'delprinc', 'selected'])
 realm.run([kadminl, 'delprinc', 'unselected'])
 
+mark('purgekeys')
 realm.addprinc('selected', 'pw')
 realm.addprinc('unselected', 'pw')
 kadmin_as(all_modify, ['purgekeys', 'unselected'])
@@ -207,6 +218,7 @@ kadmin_as(none, ['purgekeys', 'none'])
 realm.run([kadminl, 'delprinc', 'selected'])
 realm.run([kadminl, 'delprinc', 'unselected'])
 
+mark('renprinc')
 realm.addprinc('from', 'pw')
 kadmin_as(all_rename, ['renprinc', 'from', 'to'])
 realm.run([kadminl, 'renprinc', 'to', 'from'])
@@ -225,6 +237,7 @@ kadmin_as(restricted_rename, ['renprinc', 'notfrom', 'to'], expected_code=1,
           expected_msg="Insufficient authorization for operation")
 realm.run([kadminl, 'delprinc', 'notfrom'])
 
+mark('setstr')
 realm.addprinc('selected', 'pw')
 realm.addprinc('unselected', 'pw')
 kadmin_as(all_modify, ['setstr', 'unselected', 'key', 'value'])
@@ -236,6 +249,7 @@ kadmin_as(some_modify, ['setstr', 'unselected', 'key', 'value'],
 realm.run([kadminl, 'delprinc', 'selected'])
 realm.run([kadminl, 'delprinc', 'unselected'])
 
+mark('addprinc/delprinc (wildcard)')
 kadmin_as(admin, ['addprinc', '-pw', 'pw', 'anytarget'])
 realm.run([kadminl, 'delprinc', 'anytarget'])
 kadmin_as(wctarget, ['addprinc', '-pw', 'pw', 'wild/card'])
@@ -249,6 +263,7 @@ kadmin_as(admin, ['delprinc', 'none'], expected_code=1,
 realm.addprinc('four/one/three', 'pw')
 kadmin_as(onetwothreefour, ['delprinc', 'four/one/three'])
 
+mark('addprinc (restrictions)')
 kadmin_as(restrictions, ['addprinc', '-pw', 'pw', 'type1'])
 realm.run([kadminl, 'getprinc', 'type1'], expected_msg='Policy: minlife')
 realm.run([kadminl, 'delprinc', 'type1'])
@@ -268,6 +283,7 @@ kadmin_as(restrictions, ['addprinc', '-pw', 'pw', '-maxrenewlife', '1 day',
 realm.run([kadminl, 'getprinc', 'type3'],
           expected_msg='Maximum renewable life: 0 days 02:00:00')
 
+mark('extract')
 realm.run([kadminl, 'addprinc', '-pw', 'pw', 'extractkeys'])
 kadmin_as(all_wildcard, ['ktadd', '-norandkey', 'extractkeys'],
           expected_code=1,
@@ -276,6 +292,7 @@ kadmin_as(all_extract, ['ktadd', '-norandkey', 'extractkeys'])
 realm.kinit('extractkeys', flags=['-k'])
 os.remove(realm.keytab)
 
+mark('lockdown_keys')
 kadmin_as(all_modify, ['modprinc', '+lockdown_keys', 'extractkeys'])
 kadmin_as(all_changepw, ['cpw', '-pw', 'newpw', 'extractkeys'],
           expected_code=1,
@@ -297,6 +314,7 @@ realm.kinit('extractkeys', flags=['-k'])
 os.remove(realm.keytab)
 
 # Verify that self-service key changes require an initial ticket.
+mark('self-service initial ticket')
 realm.run([kadminl, 'cpw', '-pw', password('none'), 'none'])
 realm.run([kadminl, 'modprinc', '+allow_tgs_req', 'kadmin/admin'])
 realm.kinit('none', password('none'))
diff --git a/src/tests/t_kdb.py b/src/tests/t_kdb.py
index 6e563b1..8438a45 100755
--- a/src/tests/t_kdb.py
+++ b/src/tests/t_kdb.py
@@ -201,6 +201,7 @@ if out != 'KRBTEST.COM\n':
 # because we're sticking a krbPrincipalAux objectclass onto a subtree
 # krbContainer, but it works and it avoids having to load core.schema
 # in the test LDAP server.
+mark('LDAP specified dn')
 realm.run([kadminl, 'ank', '-randkey', '-x', 'dn=cn=krb5', 'princ1'],
           expected_code=1, expected_msg='DN is out of the realm subtree')
 # Check that the DN container check is a hierarchy test, not a simple
@@ -218,6 +219,7 @@ realm.run([kadminl, 'modprinc', '-x', 'linkdn=cn=t1,cn=krb5', 'princ1'],
           expected_code=1, expected_msg='link information can not be set')
 
 # Create a principal with a specified linkdn.
+mark('LDAP specified linkdn')
 realm.run([kadminl, 'ank', '-randkey', '-x', 'linkdn=cn=krb5', 'princ2'],
           expected_code=1, expected_msg='DN is out of the realm subtree')
 realm.run([kadminl, 'ank', '-randkey', '-x', 'linkdn=cn=t1,cn=krb5', 'princ2'])
@@ -226,6 +228,7 @@ realm.run([kadminl, 'modprinc', '-x', 'linkdn=cn=t2,cn=krb5', 'princ2'],
           expected_code=1, expected_msg='kerberos principal is already linked')
 
 # Create a principal with a specified containerdn.
+mark('LDAP specified containerdn')
 realm.run([kadminl, 'ank', '-randkey', '-x', 'containerdn=cn=krb5', 'princ3'],
           expected_code=1, expected_msg='DN is out of the realm subtree')
 realm.run([kadminl, 'ank', '-randkey', '-x', 'containerdn=cn=t1,cn=krb5',
@@ -238,6 +241,8 @@ realm.run([kadminl, 'ank', '-randkey', '-x', 'containerdn=cn=krb5',
            '-x', 'linkdn=cn=t2,cn=krb5', 'princ4'], expected_code=1,
           expected_msg='DN is out of the realm subtree')
 
+mark('LDAP ticket policy')
+
 # Create and modify a ticket policy.
 kldaputil(['create_policy', '-maxtktlife', '3hour', '-maxrenewlife', '6hour',
            '-allow_forwardable', 'tktpol'])
@@ -304,6 +309,7 @@ realm.run([kadminl, '-q', 'modprinc -policy tktpol2 princ4'],
 
 # Do some basic tests with a KDC against the LDAP module, exercising the
 # db_args processing code.
+mark('LDAP KDC operation')
 realm.start_kdc(['-x', 'nconns=3', '-x', 'host=' + ldap_uri,
                  '-x', 'binddn=' + admin_dn, '-x', 'bindpwd=' + admin_pw])
 realm.addprinc(realm.user_princ, password('user'))
@@ -313,6 +319,8 @@ realm.kinit(realm.user_princ, password('user'))
 realm.run([kvno, realm.host_princ])
 realm.klist(realm.user_princ, realm.host_princ)
 
+mark('LDAP auth indicator')
+
 # Test auth indicator support
 realm.addprinc('authind', password('authind'))
 realm.run([kadminl, 'setstr', 'authind', 'require_auth', 'otp radius'])
@@ -326,6 +334,8 @@ if 'krbPrincipalAuthInd: radius' not in out:
 realm.run([kadminl, 'getstrs', 'authind'],
           expected_msg='require_auth: otp radius')
 
+mark('LDAP service principal aliases')
+
 # Test service principal aliases.
 realm.addprinc('canon', password('canon'))
 ldap_modify('dn: [email protected],cn=t1,cn=krb5\n'
@@ -381,6 +391,8 @@ realm.run([kadminl, 'modprinc', '+requires_preauth', 'canon'])
 realm.kinit('canon', password('canon'))
 realm.kinit('alias', password('canon'), ['-C'])
 
+mark('LDAP password history')
+
 # Test password history.
 def test_pwhist(nhist):
     def cpw(n, **kwargs):
@@ -420,6 +432,7 @@ def get_princ(princ):
     out = realm.run([kadminl, 'getprinc', princ])
     return dict(map(str.strip, x.split(":", 1)) for x in out.splitlines())
 
+mark('LDAP principal renaming')
 realm.addprinc("rename", password('rename'))
 renameprinc = get_princ("rename")
 realm.run([kadminl, '-p', '[email protected]', 'renprinc', 'rename', 'renamed'])
@@ -428,6 +441,7 @@ if renameprinc['Last modified'] == renamedprinc['Last modified']:
     fail('Last modified data not updated when principal was renamed')
 
 # Regression test for #7980 (fencepost when dividing keys up by kvno).
+mark('#7980 regression test')
 realm.run([kadminl, 'addprinc', '-randkey', '-e', 'aes256-cts,aes128-cts',
            'kvnoprinc'])
 realm.run([kadminl, 'cpw', '-randkey', '-keepold', '-e',
@@ -438,6 +452,7 @@ realm.run([kadminl, 'cpw', '-randkey', '-keepold', '-e',
 realm.run([kadminl, 'getprinc', 'kvnoprinc'], expected_msg='Number of keys: 6')
 
 # Regression test for #8041 (NULL dereference on keyless principals).
+mark('#8041 regression test')
 realm.run([kadminl, 'addprinc', '-nokey', 'keylessprinc'])
 realm.run([kadminl, 'getprinc', 'keylessprinc'],
           expected_msg='Number of keys: 0')
@@ -452,6 +467,7 @@ realm.run([kadminl, 'getprinc', 'keylessprinc'],
           expected_msg='Number of keys: 0')
 
 # Test for 8354 (old password history entries when -keepold is used)
+mark('#8354 regression test')
 realm.run([kadminl, 'addpol', '-history', '2', 'keepoldpasspol'])
 realm.run([kadminl, 'addprinc', '-policy', 'keepoldpasspol', '-pw', 'aaaa',
            'keepoldpassprinc'])
@@ -468,6 +484,7 @@ else:
 realm.stop()
 
 # Briefly test dump and load.
+mark('LDAP dump and load')
 dumpfile = os.path.join(realm.testdir, 'dump')
 realm.run([kdb5_util, 'dump', dumpfile])
 realm.run([kdb5_util, 'load', dumpfile], expected_code=1,
@@ -488,6 +505,7 @@ if runenv.have_sasl != 'yes':
 
 # Test SASL EXTERNAL auth.  Remove the DNs and service password file
 # from the DB module config.
+mark('LDAP SASL EXTERNAL auth')
 os.remove(ldap_pwfile)
 dbmod = conf['dbmodules']['ldap']
 dbmod['ldap_kdc_sasl_mech'] = dbmod['ldap_kadmind_sasl_mech'] = 'EXTERNAL'
@@ -504,6 +522,7 @@ realm.run([kdb5_ldap_util, 'destroy', '-f'])
 # Test SASL DIGEST-MD5 auth.  We need to set a clear-text password for
 # the admin DN, so create a person entry (requires the core schema).
 # Restore the service password file in the config and set authcids.
+mark('LDAP SASL DIGEST-MD5 auth')
 ldap_add('cn=admin,cn=krb5', 'person',
          ['sn: dummy', 'userPassword: admin'])
 dbmod['ldap_kdc_sasl_mech'] = dbmod['ldap_kadmind_sasl_mech'] = 'DIGEST-MD5'
diff --git a/src/tests/t_keytab.py b/src/tests/t_keytab.py
index a48740b..91fd23d 100755
--- a/src/tests/t_keytab.py
+++ b/src/tests/t_keytab.py
@@ -8,16 +8,19 @@ for realm in multipass_realms(create_user=False):
 realm = K5Realm(get_creds=False, start_kadmind=True)
 
 # Test kinit with a partial keytab.
+mark('partial keytab')
 pkeytab = realm.keytab + '.partial'
 realm.run([ktutil], input=('rkt %s\ndelent 1\nwkt %s\n' %
                            (realm.keytab, pkeytab)))
 realm.kinit(realm.host_princ, flags=['-k', '-t', pkeytab])
 
 # Test kinit with no keys for client in keytab.
+mark('no keys for client')
 realm.kinit(realm.user_princ, flags=['-k'], expected_code=1,
             expected_msg='no suitable keys')
 
 # Test kinit and klist with client keytab defaults.
+mark('client keytab')
 realm.extract_keytab(realm.user_princ, realm.client_keytab);
 realm.run([kinit, '-k', '-i'])
 realm.klist(realm.user_princ)
@@ -29,6 +32,7 @@ if realm.client_keytab not in out or realm.user_princ not in out:
     fail('Expected output not seen from klist -k -i')
 
 # Test implicit request for keytab (-i or -t without -k)
+mark('implicit -k')
 realm.run([kdestroy])
 realm.kinit(realm.host_princ, flags=['-t', realm.keytab],
             expected_msg='keytab specified, forcing -k')
@@ -39,6 +43,7 @@ realm.kinit(realm.user_princ, flags=['-i'],
 realm.klist(realm.user_princ)
 
 # Test extracting keys with multiple key versions present.
+mark('multi-kvno extract')
 os.remove(realm.keytab)
 realm.run([kadminl, 'cpw', '-randkey', '-keepold', realm.host_princ])
 out = realm.run([kadminl, 'ktadd', '-norandkey', realm.host_princ])
@@ -49,6 +54,7 @@ if ' 1 host/' not in out or ' 2 host/' not in out:
     fail('Expected output not seen from klist -k -e')
 
 # Test again using kadmin over the network.
+mark('multi-kvno extract (via kadmin)')
 realm.prep_kadmin()
 os.remove(realm.keytab)
 out = realm.run_kadmin(['ktadd', '-norandkey', realm.host_princ])
@@ -72,6 +78,7 @@ def test_key_rotate(realm, princ, expected_kvno):
     msg = 'Key: vno %d,' % expected_kvno
     out = realm.run_kadmin(['getprinc', princ], expected_msg=msg)
 
+mark('key rotation across boundaries')
 princ = 'foo/bar@%s' % realm.realm
 realm.addprinc(princ)
 os.remove(realm.keytab)
@@ -89,6 +96,8 @@ test_key_rotate(realm, princ, 65535)
 test_key_rotate(realm, princ, 1)
 test_key_rotate(realm, princ, 2)
 
+mark('32-bit kvno')
+
 # Test that klist -k can read a keytab entry without a 32-bit kvno and
 # reports the 8-bit key version.
 record = '\x00\x01'             # principal component count
@@ -126,6 +135,7 @@ msg = '   3 %s' % realm.user_princ
 out = realm.run([klist, '-k'], expected_msg=msg)
 
 # Test parameter expansion in profile variables
+mark('parameter expansion')
 realm.stop()
 conf = {'libdefaults': {
         'default_keytab_name': 'testdir/%{null}abc%{uid}',
diff --git a/src/tests/t_localauth.py b/src/tests/t_localauth.py
index aa625d0..63fc563 100755
--- a/src/tests/t_localauth.py
+++ b/src/tests/t_localauth.py
@@ -26,6 +26,7 @@ def test_userok(env, aname, lname, ok, msg):
 # The default an2ln method works only in the default realm, and works
 # for a single-component principal or a two-component principal where
 # the second component is the default realm.
+mark('default')
 test_an2ln(None, '[email protected]', 'user', 'default rule 1')
 test_an2ln(None, 'user/[email protected]', 'user', 'default rule 2')
 test_an2ln_err(None, 'user/KRBTEST.COM/[email protected]', 'No translation',
@@ -35,6 +36,7 @@ test_an2ln_err(None, 'user/[email protected]', 'No translation',
 test_an2ln_err(None, 'user@X', 'No translation', 'default rule realm mismatch')
 
 # auth_to_local_names matches ignore the realm but are case-sensitive.
+mark('auth_to_local_names')
 conf_names1 = {'realms': {'$realm': {'auth_to_local_names': {'user': 'abcd'}}}}
 names1 = realm.special_env('names1', False, conf_names1)
 test_an2ln(names1, '[email protected]', 'abcd', 'auth_to_local_names match')
@@ -54,10 +56,12 @@ def a2l_realm(name, values):
     return realm.special_env(name, False, conf)
 
 # Test explicit use of default method.
+mark('explicit default')
 auth1 = a2l_realm('auth1', 'DEFAULT')
 test_an2ln(auth1, '[email protected]', 'user', 'default rule')
 
 # Test some invalid auth_to_local values.
+mark('auth_to_local invalid')
 auth2 = a2l_realm('auth2', 'RULE')
 test_an2ln_err(auth2, 'user@X', 'Improper format', 'null rule')
 auth3 = a2l_realm('auth3', 'UNRECOGNIZED:stuff')
@@ -65,6 +69,7 @@ test_an2ln_err(auth3, 'user@X', 'Improper format', 'null rule')
 
 # An empty rule has the default selection string (unparsed principal
 # without realm) and no match or substitutions.
+mark('rule (empty)')
 rule1 = a2l_realm('rule1', 'RULE:')
 test_an2ln(rule1, '[email protected]', 'user', 'empty rule')
 test_an2ln(rule1, 'user@X', 'user', 'empty rule (foreign realm)')
@@ -72,23 +77,27 @@ test_an2ln(rule1, 'a/b/c@X', 'a/b/c', 'empty rule (multi-component)')
 
 # Test explicit selection string.  Also test that the default method
 # is suppressed when auth_to_local values are present.
+mark('rule (selection string)')
 rule2 = a2l_realm('rule2', 'RULE:[2:$$0.$$2.$$1]')
 test_an2ln(rule2, 'aaron/burr@REALM', 'REALM.burr.aaron', 'selection string')
 test_an2ln_err(rule2, '[email protected]', 'No translation', 'suppress default')
 
 # Test match string.
+mark('rule (match string)')
 rule3 = a2l_realm('rule3', 'RULE:(.*tail)')
 test_an2ln(rule3, 'withtail@X', 'withtail', 'rule match 1')
 test_an2ln(rule3, 'x/withtail@X', 'x/withtail', 'rule match 2')
 test_an2ln_err(rule3, 'tails@X', 'No translation', 'rule anchor mismatch')
 
 # Test substitutions.
+mark('rule (substitutions)')
 rule4 = a2l_realm('rule4', 'RULE:s/birds/bees/')
 test_an2ln(rule4, 'thebirdsbirdsbirds@X', 'thebeesbirdsbirds', 'subst 1')
 rule5 = a2l_realm('rule4', 'RULE:s/birds/bees/g  s/bees/birds/')
 test_an2ln(rule4, 'the/birdsbirdsbirds@x', 'the/birdsbeesbees', 'subst 2')
 
 # Test a bunch of auth_to_local values and rule features in combination.
+mark('rule (combo)')
 combo = a2l_realm('combo', ['RULE:[1:$$1-$$0](fred.*)s/-/ /g',
                             'DEFAULT',
                             'RULE:[3:$$1](z.*z)'])
@@ -100,11 +109,14 @@ test_an2ln(combo, 'zazz/b/c@X', 'zazz', 'combo 5')
 test_an2ln_err(combo, 'a/[email protected]', 'No translation', 'combo 6')
 
 # Test the an2ln userok method with the combo environment.
+mark('userok (an2ln)')
 test_userok(combo, 'fred@X', 'fred X', True, 'combo userok 1')
 test_userok(combo, '[email protected]', 'user', True, 'combo userok 2')
 test_userok(combo, '[email protected]', 'X', False, 'combo userok 3')
 test_userok(combo, 'a/[email protected]', 'a/b', False, 'combo userok 4')
 
+mark('test modules')
+
 # Register the two test modules and set up some auth_to_local and
 # auth_to_local_names entries.
 modpath = os.path.join(buildtop, 'plugins', 'localauth', 'test',
diff --git a/src/tests/t_mkey.py b/src/tests/t_mkey.py
index 615cd91..998297e 100755
--- a/src/tests/t_mkey.py
+++ b/src/tests/t_mkey.py
@@ -150,12 +150,14 @@ def update_princ_encryption(dry_run, expected_mkvno, expected_updated,
 
 
 # Check the initial state of the realm.
+mark('initial state')
 check_mkey_list((1, defetype, True, True))
 check_master_dbent(1, (1, defetype))
 check_stash((1, defetype))
 check_mkvno(realm.user_princ, 1)
 
 # Check that stash will fail if a temp stash file is already present.
+mark('temp stash collision')
 collisionfile = os.path.join(realm.testdir, 'stash_tmp')
 f = open(collisionfile, 'w')
 f.close()
@@ -170,6 +172,7 @@ os.unlink(collisionfile)
 #    encrypt that entry.
 # 3. The stash file is not modified (since we did not pass -s).
 # 4. The old key is used for password changes.
+mark('add_mkey (second master key)')
 add_mkey([])
 check_mkey_list((2, defetype, False, False), (1, defetype, True, True))
 check_master_dbent(2, (2, defetype), (1, defetype))
@@ -177,6 +180,7 @@ change_password_check_mkvno(True, realm.user_princ, 'abcd', 1)
 change_password_check_mkvno(False, realm.user_princ, 'user', 1)
 
 # Verify that use_mkey won't make all master keys inactive.
+mark('use_mkey (no active keys)')
 realm.run([kdb5_util, 'use_mkey', '1', 'now+1day'], expected_code=1,
           expected_msg='there must be one master key currently active')
 check_mkey_list((2, defetype, False, False), (1, defetype, True, True))
@@ -185,12 +189,14 @@ check_mkey_list((2, defetype, False, False), (1, defetype, True, True))
 # 1. The new key has an activation time in list_mkeys and is active.
 # 2. The new key is used for password changes.
 # 3. The running KDC can access the new key.
+mark('use_mkey')
 realm.run([kdb5_util, 'use_mkey', '2', 'now-1day'])
 check_mkey_list((2, defetype, True, True), (1, defetype, True, False))
 change_password_check_mkvno(True, realm.user_princ, 'abcd', 2)
 change_password_check_mkvno(False, realm.user_princ, 'user', 2)
 
 # Check purge_mkeys behavior with both master keys still in use.
+mark('purge_mkeys (nothing to purge)')
 realm.run([kdb5_util, 'purge_mkeys', '-f', '-v'],
           expected_msg='All keys in use, nothing purged.')
 
@@ -204,6 +210,7 @@ realm.run([kdb5_util, 'purge_mkeys', '-f', '-v'],
 # 4. The old stashed master key is sufficient to access the DB (via
 #    MKEY_AUX tl-data which keeps the current master key encrypted in
 #    each of the old master keys).
+mark('update_princ_encryption')
 update_princ_encryption(True, 2, nprincs - 2, 1)
 check_mkvno(realm.admin_princ, 1)
 update_princ_encryption(False, 2, nprincs - 2, 1)
@@ -214,6 +221,7 @@ realm.kinit(realm.user_princ, 'user')
 
 # Update all principals back to mkvno 1 and to mkvno 2 again, to
 # verify that update_princ_encryption targets the active master key.
+mark('update_princ_encryption (back and forth)')
 realm.run([kdb5_util, 'use_mkey', '2', 'now+1day'])
 update_princ_encryption(False, 1, nprincs - 1, 0)
 check_mkvno(realm.user_princ, 1)
@@ -222,11 +230,13 @@ update_princ_encryption(False, 2, nprincs - 1, 0)
 check_mkvno(realm.user_princ, 2)
 
 # Test the safety check for purging with an outdated stash file.
+mark('purge_mkeys (outdated stash file)')
 realm.run([kdb5_util, 'purge_mkeys', '-f'], expected_code=1,
           expected_msg='stash file needs updating')
 
 # Update the master stash file and check it.  Save a copy of the old
 # one for a later test.
+mark('update stash file')
 shutil.copy(stash_file, stash_file + '.old')
 realm.run([kdb5_util, 'stash'])
 check_stash((2, defetype), (1, defetype))
@@ -238,6 +248,7 @@ check_stash((2, defetype), (1, defetype))
 # 4. If the stash file is updated, it no longer contains mkvno 1.
 # 5. use_mkey now gives an error if we refer to mkvno 1.
 # 6. A second purge_mkeys gives the right message.
+mark('purge_mkeys')
 out = realm.run([kdb5_util, 'purge_mkeys', '-v', '-n', '-f'])
 if 'KVNO: 1' not in out or '1 key(s) would be purged' not in out:
     fail('Unexpected output from purge_mkeys dry-run')
@@ -263,6 +274,7 @@ realm.run([kdb5_util, 'purge_mkeys', '-f', '-v'],
 # 2. The enctype argument is respected.
 # 3. The new master key is stashed (by itself, at the moment).
 # 4. We can roll over to the new master key and use it.
+mark('add_mkey and update_princ_encryption (third master key)')
 add_mkey(['-s', '-e', aes128])
 check_mkey_list((3, aes128, False, False), (2, defetype, True, True))
 check_master_dbent(3, (3, aes128), (2, defetype))
@@ -274,6 +286,7 @@ check_mkvno(realm.user_princ, 3)
 
 # Regression test for #7994 (randkey does not update principal mkvno)
 # and #7995 (-keepold does not re-encrypt old keys).
+mark('#7994 and #7995 regression test')
 add_mkey(['-s'])
 realm.run([kdb5_util, 'use_mkey', '4', 'now-1day'])
 realm.run([kadminl, 'cpw', '-randkey', '-keepold', realm.user_princ])
@@ -295,6 +308,7 @@ realm.stop()
 # created prior to master key rollover support.  Verify that:
 # 1. We can access the database using the old-format stash file.
 # 2. list_mkeys displays the same list as for a post-1.7 KDB.
+mark('pre-1.7 stash file')
 dumpfile = os.path.join(srctop, 'tests', 'dumpfiles', 'dump.16')
 os.remove(stash_file)
 f = open(stash_file, 'w')
@@ -312,6 +326,7 @@ check_mkey_list((1, des3, True, True))
 # 2. update_princ_encryption still targets mkvno 1.
 # 3. libkadm5 still uses mkvno 1 for key changes.
 # 4. use_mkey creates the same list as for a post-1.7 KDB.
+mark('rollover from pre-1.7 KDB')
 add_mkey([])
 check_mkey_list((2, defetype, False, False), (1, des3, True, True))
 update_princ_encryption(False, 1, 0, nprincs - 1)
@@ -322,6 +337,7 @@ check_mkey_list((2, defetype, True, True), (1, des3, True, False))
 
 # Regression test for #8395.  Purge the master key and verify that a
 # master key fetch does not segfault.
+mark('#8395 regression test')
 realm.run([kadminl, 'purgekeys', '-all', 'K/M'])
 realm.run([kadminl, 'getprinc', realm.user_princ], expected_code=1,
           expected_msg='Cannot find master key record in database')
diff --git a/src/tests/t_otp.py b/src/tests/t_otp.py
index 9b18ff9..1142fc7 100755
--- a/src/tests/t_otp.py
+++ b/src/tests/t_otp.py
@@ -183,6 +183,7 @@ flags = ['-T', realm.ccache]
 server_addr = '127.0.0.1:' + str(realm.portbase + 9)
 
 ## Test UDP fail / custom username
+mark('UDP fail / custom username')
 daemon = UDPRadiusDaemon(args=(server_addr, secret_file, 'accept', queue))
 daemon.start()
 queue.get()
@@ -192,6 +193,7 @@ realm.kinit(realm.user_princ, 'reject', flags=flags, expected_code=1)
 verify(daemon, queue, False, 'custom', 'reject')
 
 ## Test UDP success / standard username
+mark('UDP success / standard username')
 daemon = UDPRadiusDaemon(args=(server_addr, secret_file, 'accept', queue))
 daemon.start()
 queue.get()
@@ -203,6 +205,7 @@ realm.run(['./adata', realm.krbtgt_princ],
           expected_msg='+97: [indotp1, indotp2]')
 
 # Repeat with an indicators override in the string attribute.
+mark('auth indicator override')
 daemon = UDPRadiusDaemon(args=(server_addr, secret_file, 'accept', queue))
 daemon.start()
 queue.get()
@@ -223,6 +226,7 @@ except AssertionError:
     skip_rest('OTP UNIX domain socket tests', 'pyrad assertion bug detected')
 
 ## Test Unix fail / custom username
+mark('Unix socket fail / custom username')
 daemon = UnixRadiusDaemon(args=(socket_file, '', 'accept', queue))
 daemon.start()
 queue.get()
@@ -232,6 +236,7 @@ realm.kinit(realm.user_princ, 'reject', flags=flags, expected_code=1)
 verify(daemon, queue, False, 'custom', 'reject')
 
 ## Test Unix success / standard username
+mark('Unix socket success / standard username')
 daemon = UnixRadiusDaemon(args=(socket_file, '', 'accept', queue))
 daemon.start()
 queue.get()
diff --git a/src/tests/t_pkinit.py b/src/tests/t_pkinit.py
index 1ba3536..769ad6a 100755
--- a/src/tests/t_pkinit.py
+++ b/src/tests/t_pkinit.py
@@ -79,6 +79,8 @@ realm = K5Realm(krb5_conf=pkinit_krb5_conf, kdc_conf=alias_kdc_conf,
                 create_kdb=False)
 realm.start_kdc()
 
+mark('UPN SANs')
+
 # Compatibility check: cert contains UPN "user", which matches the
 # request principal [email protected] if parsed as a normal principal.
 realm.kinit(realm.user_princ,
@@ -114,6 +116,7 @@ realm = K5Realm(krb5_conf=pkinit_krb5_conf, kdc_conf=pkinit_kdc_conf,
                 get_creds=False)
 
 # Sanity check - password-based preauth should still work.
+mark('password preauth sanity check')
 realm.run(['./responder', '-r', 'password=%s' % password('user'),
            realm.user_princ])
 realm.kinit(realm.user_princ, password=password('user'))
@@ -125,6 +128,7 @@ realm.run([kvno, realm.host_princ])
 realm.run([kadminl, 'purgekeys', '-all', realm.user_princ])
 
 # Test anonymous PKINIT.
+mark('anonymous')
 realm.kinit('@%s' % realm.realm, flags=['-n'], expected_code=1,
             expected_msg='not found in Kerberos database')
 realm.addprinc('WELLKNOWN/ANONYMOUS')
@@ -136,6 +140,7 @@ if '97:' in out:
     fail('auth indicators seen in anonymous PKINIT ticket')
 
 # Test anonymous kadmin.
+mark('anonymous kadmin')
 f = open(os.path.join(realm.testdir, 'acl'), 'a')
 f.write('WELLKNOWN/ANONYMOUS@WELLKNOWN:ANONYMOUS a *')
 f.close()
@@ -146,6 +151,7 @@ realm.run([kadmin, '-n', 'getprinc', 'testadd'], expected_code=1,
 realm.stop_kadmind()
 
 # Test with anonymous restricted; FAST should work but kvno should fail.
+mark('anonymous restricted')
 r_env = realm.special_env('restrict', True, kdc_conf=restrictive_kdc_conf)
 realm.stop_kdc()
 realm.start_kdc(env=r_env)
@@ -156,6 +162,7 @@ realm.run([kvno, realm.host_princ], expected_code=1,
 
 # Regression test for #8458: S4U2Self requests crash the KDC if
 # anonymous is restricted.
+mark('#8458 regression test')
 realm.kinit(realm.host_princ, flags=['-k'])
 realm.run([kvno, '-U', 'user', realm.host_princ])
 
@@ -164,6 +171,7 @@ realm.stop_kdc()
 realm.start_kdc()
 
 # Run the basic test - PKINIT with FILE: identity, with no password on the key.
+mark('FILE identity, no password')
 msgs = ('Sending unauthenticated request',
         '/Additional pre-authentication required',
         'Preauthenticating using KDC method data',
@@ -181,6 +189,7 @@ realm.klist(realm.user_princ)
 realm.run([kvno, realm.host_princ])
 
 # Try again using RSA instead of DH.
+mark('FILE identity, no password, RSA')
 realm.kinit(realm.user_princ,
             flags=['-X', 'X509_user_identity=%s' % file_identity,
                    '-X', 'flag_RSA_PROTOCOL=yes'],
@@ -191,6 +200,7 @@ realm.klist(realm.user_princ)
 # Test a DH parameter renegotiation by temporarily setting a 4096-bit
 # minimum on the KDC.  (Preauth type 16 is PKINIT PA_PK_AS_REQ;
 # 109 is PKINIT TD_DH_PARAMETERS; 133 is FAST PA-FX-COOKIE.)
+mark('DH parameter renegotiation')
 minbits_kdc_conf = {'realms': {'$realm': {'pkinit_dh_min_bits': '4096'}}}
 minbits_env = realm.special_env('restrict', True, kdc_conf=minbits_kdc_conf)
 realm.stop_kdc()
@@ -211,6 +221,7 @@ realm.kinit(realm.user_princ,
 
 # Test enforcement of required freshness tokens.  (We can leave
 # freshness tokens required after this test.)
+mark('freshness token enforcement')
 realm.kinit(realm.user_princ,
             flags=['-X', 'X509_user_identity=%s' % file_identity,
                    '-X', 'disable_freshness=yes'])
@@ -229,6 +240,7 @@ realm.kinit('@%s' % realm.realm, flags=['-n', '-X', 'disable_freshness=yes'])
 # Run the basic test - PKINIT with FILE: identity, with a password on the key,
 # supplied by the prompter.
 # Expect failure if the responder does nothing, and we have no prompter.
+mark('FILE identity, password on key (prompter)')
 realm.run(['./responder', '-x', 'pkinit={"%s": 0}' % file_enc_identity,
           '-X', 'X509_user_identity=%s' % file_enc_identity, realm.user_princ],
           expected_code=2)
@@ -243,6 +255,7 @@ realm.run(['./adata', realm.host_princ],
 # Run the basic test - PKINIT with FILE: identity, with a password on the key,
 # supplied by the responder.
 # Supply the response in raw form.
+mark('FILE identity, password on key (responder)')
 realm.run(['./responder', '-x', 'pkinit={"%s": 0}' % file_enc_identity,
            '-r', 'pkinit={"%s": "encrypted"}' % file_enc_identity,
            '-X', 'X509_user_identity=%s' % file_enc_identity,
@@ -254,6 +267,7 @@ realm.klist(realm.user_princ)
 realm.run([kvno, realm.host_princ])
 
 # PKINIT with DIR: identity, with no password on the key.
+mark('DIR identity, no password')
 os.mkdir(path)
 os.mkdir(path_enc)
 shutil.copy(privkey_pem, os.path.join(path, 'user.key'))
@@ -268,6 +282,7 @@ realm.run([kvno, realm.host_princ])
 # PKINIT with DIR: identity, with a password on the key, supplied by the
 # prompter.
 # Expect failure if the responder does nothing, and we have no prompter.
+mark('DIR identity, password on key (prompter)')
 realm.run(['./responder', '-x', 'pkinit={"%s": 0}' % dir_file_enc_identity,
            '-X', 'X509_user_identity=%s' % dir_enc_identity, realm.user_princ],
            expected_code=2)
@@ -280,6 +295,7 @@ realm.run([kvno, realm.host_princ])
 # PKINIT with DIR: identity, with a password on the key, supplied by the
 # responder.
 # Supply the response in raw form.
+mark('DIR identity, password on key (responder)')
 realm.run(['./responder', '-x', 'pkinit={"%s": 0}' % dir_file_enc_identity,
            '-r', 'pkinit={"%s": "encrypted"}' % dir_file_enc_identity,
            '-X', 'X509_user_identity=%s' % dir_enc_identity, realm.user_princ])
@@ -291,6 +307,7 @@ realm.klist(realm.user_princ)
 realm.run([kvno, realm.host_princ])
 
 # PKINIT with PKCS12: identity, with no password on the bundle.
+mark('PKCS12 identity, no password')
 realm.kinit(realm.user_princ,
             flags=['-X', 'X509_user_identity=%s' % p12_identity])
 realm.klist(realm.user_princ)
@@ -299,6 +316,7 @@ realm.run([kvno, realm.host_princ])
 # PKINIT with PKCS12: identity, with a password on the bundle, supplied by the
 # prompter.
 # Expect failure if the responder does nothing, and we have no prompter.
+mark('PKCS12 identity, password on bundle (prompter)')
 realm.run(['./responder', '-x', 'pkinit={"%s": 0}' % p12_enc_identity,
            '-X', 'X509_user_identity=%s' % p12_enc_identity, realm.user_princ],
            expected_code=2)
@@ -311,6 +329,7 @@ realm.run([kvno, realm.host_princ])
 # PKINIT with PKCS12: identity, with a password on the bundle, supplied by the
 # responder.
 # Supply the response in raw form.
+mark('PKCS12 identity, password on bundle (responder)')
 realm.run(['./responder', '-x', 'pkinit={"%s": 0}' % p12_enc_identity,
            '-r', 'pkinit={"%s": "encrypted"}' % p12_enc_identity,
            '-X', 'X509_user_identity=%s' % p12_enc_identity, realm.user_princ])
@@ -321,6 +340,8 @@ realm.run(['./responder', '-X', 'X509_user_identity=%s' % p12_enc_identity,
 realm.klist(realm.user_princ)
 realm.run([kvno, realm.host_princ])
 
+mark('pkinit_cert_match rules')
+
 # Match a single rule.
 rule = '<SAN>^[email protected]$'
 realm.run([kadminl, 'setstr', realm.user_princ, 'pkinit_cert_match', rule])
@@ -373,6 +394,7 @@ softpkcs11rc = os.path.join(os.getcwd(), 'testdir', 'soft-pkcs11.rc')
 realm.env['SOFTPKCS11RC'] = softpkcs11rc
 
 # PKINIT with PKCS11: identity, with no need for a PIN.
+mark('PKCS11 identity, no PIN')
 conf = open(softpkcs11rc, 'w')
 conf.write("%s\t%s\t%s\t%s\n" % ('user', 'user token', user_pem, privkey_pem))
 conf.close()
@@ -383,6 +405,7 @@ realm.klist(realm.user_princ)
 realm.run([kvno, realm.host_princ])
 
 # PKINIT with PKCS11: identity, with a PIN supplied by the prompter.
+mark('PKCS11 identity, with PIN (prompter)')
 os.remove(softpkcs11rc)
 conf = open(softpkcs11rc, 'w')
 conf.write("%s\t%s\t%s\t%s\n" % ('user', 'user token', user_pem,
@@ -400,6 +423,7 @@ realm.run([kvno, realm.host_princ])
 
 # Supply the wrong PIN, and verify that we ignore the draft9 padata offer
 # in the KDC method data after RFC 4556 PKINIT fails.
+mark('PKCS11 identity, wrong PIN')
 expected_trace = ('PKINIT client has no configured identity; giving up',
                   'PKINIT client ignoring draft 9 offer from RFC 4556 KDC')
 realm.kinit(realm.user_princ,
@@ -408,6 +432,7 @@ realm.kinit(realm.user_princ,
 
 # PKINIT with PKCS11: identity, with a PIN supplied by the responder.
 # Supply the response in raw form.
+mark('PKCS11 identity, with PIN (responder)')
 realm.run(['./responder', '-x', 'pkinit={"%s": 0}' % p11_token_identity,
            '-r', 'pkinit={"%s": "encrypted"}' % p11_token_identity,
            '-X', 'X509_user_identity=%s' % p11_identity, realm.user_princ])
diff --git a/src/tests/t_policy.py b/src/tests/t_policy.py
index 26c4e46..9d92ebd 100755
--- a/src/tests/t_policy.py
+++ b/src/tests/t_policy.py
@@ -5,6 +5,7 @@ import re
 realm = K5Realm(create_host=False, start_kadmind=True)
 
 # Test password quality enforcement.
+mark('password quality')
 realm.run([kadminl, 'addpol', '-minlength', '6', '-minclasses', '2', 'pwpol'])
 realm.run([kadminl, 'addprinc', '-randkey', '-policy', 'pwpol', 'pwuser'])
 realm.run([kadminl, 'cpw', '-pw', 'sh0rt', 'pwuser'], expected_code=1,
@@ -15,6 +16,7 @@ realm.run([kadminl, 'cpw', '-pw', 'l0ngenough', 'pwuser'])
 
 # Test some password history enforcement.  Even with no history value,
 # the current password should be denied.
+mark('password history')
 realm.run([kadminl, 'cpw', '-pw', 'l0ngenough', 'pwuser'], expected_code=1,
           expected_msg='Cannot reuse password')
 realm.run([kadminl, 'modpol', '-history', '2', 'pwpol'])
@@ -25,6 +27,7 @@ realm.run([kadminl, 'cpw', '-pw', '3rdpassword', 'pwuser'])
 realm.run([kadminl, 'cpw', '-pw', 'l0ngenough', 'pwuser'])
 
 # Test references to nonexistent policies.
+mark('nonexistent policy references')
 realm.run([kadminl, 'addprinc', '-randkey', '-policy', 'newpol', 'newuser'])
 realm.run([kadminl, 'getprinc', 'newuser'],
           expected_msg='Policy: newpol [does not exist]\n')
@@ -36,6 +39,7 @@ realm.run([kadmin, '-p', 'pwuser', '-w', '3rdpassword', 'cpw', '-pw',
            '3rdpassword', 'pwuser'])
 
 # Create newpol and verify that it is enforced.
+mark('create referenced policy')
 realm.run([kadminl, 'addpol', '-minlength', '3', 'newpol'])
 realm.run([kadminl, 'getprinc', 'pwuser'], expected_msg='Policy: newpol\n')
 realm.run([kadminl, 'cpw', '-pw', 'aa', 'pwuser'], expected_code=1,
@@ -48,13 +52,14 @@ realm.run([kadminl, 'cpw', '-pw', 'aa', 'newuser'], expected_code=1,
           expected_msg='Password is too short')
 
 # Delete the policy and verify that it is no longer enforced.
+mark('delete referenced policy')
 realm.run([kadminl, 'delpol', 'newpol'])
 realm.run([kadminl, 'getpol', 'newpol'], expected_code=1,
           expected_msg='Policy does not exist')
 realm.run([kadminl, 'cpw', '-pw', 'aa', 'pwuser'])
 
 # Test basic password lockout support.
-
+mark('password lockout')
 realm.run([kadminl, 'addpol', '-maxfailure', '2', '-failurecountinterval',
            '5m', 'lockout'])
 realm.run([kadminl, 'modprinc', '+requires_preauth', '-policy', 'lockout',
@@ -81,7 +86,7 @@ realm.kinit(realm.user_princ, password('user'))
 # Regression test for issue #7099: databases created prior to krb5 1.3 have
 # multiple history keys, and kadmin prior to 1.7 didn't necessarily use the
 # first one to create history entries.
-
+mark('#7099 regression test')
 realm.stop()
 realm = K5Realm(start_kdc=False)
 # Create a history principal with two keys.
@@ -96,6 +101,7 @@ realm.run([kadminl, 'cpw', '-pw', password('user'), 'user'], expected_code=1,
           expected_msg='Cannot reuse password')
 
 # Test key/salt constraints.
+mark('allowedkeysalts')
 
 realm.stop()
 krb5_conf1 = {'libdefaults': {'supported_enctypes': 'aes256-cts'}}
diff --git a/src/tests/t_preauth.py b/src/tests/t_preauth.py
index 32e35b0..be6d2b3 100644
--- a/src/tests/t_preauth.py
+++ b/src/tests/t_preauth.py
@@ -18,6 +18,7 @@ realm.kinit('nokeyuser', password('user'), expected_code=1,
 # PA-FX-COOKIE; 2 is encrypted timestamp.
 
 # Test normal preauth flow.
+mark('normal')
 msgs = ('Sending unauthenticated request',
         '/Additional pre-authentication required',
         'Preauthenticating using KDC method data',
@@ -29,6 +30,7 @@ realm.run(['./icred', realm.user_princ, password('user')],
           expected_msg='testval', expected_trace=msgs)
 
 # Test successful optimistic preauth.
+mark('optimistic')
 expected_trace = ('Attempting optimistic preauth',
                   'Processing preauth types: -123',
                   'Preauth module test (-123) (real) returned: 0/Success',
@@ -39,6 +41,7 @@ realm.run(['./icred', '-o', '-123', realm.user_princ, password('user')],
 
 # Test optimistic preauth failing on client, falling back to encrypted
 # timestamp.
+mark('optimistic (client failure)')
 msgs = ('Attempting optimistic preauth',
         'Processing preauth types: -123',
         '/induced optimistic fail',
@@ -55,6 +58,7 @@ realm.run(['./icred', '-o', '-123', '-X', 'fail_optimistic', realm.user_princ,
 
 # Test optimistic preauth failing on KDC, falling back to encrypted
 # timestamp.
+mark('optimistic (KDC failure)')
 realm.run([kadminl, 'setstr', realm.user_princ, 'failopt', 'yes'])
 msgs = ('Attempting optimistic preauth',
         'Processing preauth types: -123',
@@ -73,6 +77,7 @@ realm.run(['./icred', '-o', '-123', realm.user_princ, password('user')],
 
 # Test optimistic preauth failing on KDC, stopping because the test
 # module disabled fallback.
+mark('optimistic (KDC failure, no fallback)')
 msgs = ('Attempting optimistic preauth',
         'Processing preauth types: -123',
         'Preauth module test (-123) (real) returned: 0/Success',
@@ -84,6 +89,7 @@ realm.run(['./icred', '-X', 'disable_fallback', '-o', '-123', realm.user_princ,
 realm.run([kadminl, 'delstr', realm.user_princ, 'failopt'])
 
 # Test KDC_ERR_MORE_PREAUTH_DATA_REQUIRED and secure cookies.
+mark('second round-trip')
 realm.run([kadminl, 'setstr', realm.user_princ, '2rt', 'secondtrip'])
 msgs = ('Sending unauthenticated request',
         '/Additional pre-authentication required',
@@ -101,6 +107,7 @@ realm.run(['./icred', realm.user_princ, password('user')],
 
 # Test client-side failure after KDC_ERR_MORE_PREAUTH_DATA_REQUIRED,
 # falling back to encrypted timestamp.
+mark('second round-trip (client failure)')
 msgs = ('Sending unauthenticated request',
         '/Additional pre-authentication required',
         'Preauthenticating using KDC method data',
@@ -122,6 +129,7 @@ realm.run(['./icred', '-X', 'fail_2rt', realm.user_princ, password('user')],
 
 # Test client-side failure after KDC_ERR_MORE_PREAUTH_DATA_REQUIRED,
 # stopping because the test module disabled fallback.
+mark('second round-trip (client failure, no fallback)')
 msgs = ('Sending unauthenticated request',
         '/Additional pre-authentication required',
         'Preauthenticating using KDC method data',
@@ -139,6 +147,7 @@ realm.run(['./icred', '-X', 'fail_2rt', '-X', 'disable_fallback',
 
 # Test KDC-side failure after KDC_ERR_MORE_PREAUTH_DATA_REQUIRED,
 # falling back to encrypted timestamp.
+mark('second round-trip (KDC failure)')
 realm.run([kadminl, 'setstr', realm.user_princ, 'fail2rt', 'yes'])
 msgs = ('Sending unauthenticated request',
         '/Additional pre-authentication required',
@@ -164,6 +173,7 @@ realm.run(['./icred', realm.user_princ, password('user')],
 
 # Test KDC-side failure after KDC_ERR_MORE_PREAUTH_DATA_REQUIRED,
 # stopping because the test module disabled fallback.
+mark('second round-trip (KDC failure, no fallback)')
 msgs = ('Sending unauthenticated request',
         '/Additional pre-authentication required',
         'Preauthenticating using KDC method data',
@@ -182,6 +192,7 @@ realm.run(['./icred', '-X', 'disable_fallback',
 realm.run([kadminl, 'delstr', realm.user_princ, 'fail2rt'])
 
 # Test tryagain flow by inducing a KDC_ERR_ENCTYPE_NOSUPP error on the KDC.
+mark('tryagain')
 realm.run([kadminl, 'setstr', realm.user_princ, 'err', 'testagain'])
 msgs = ('Sending unauthenticated request',
         '/Additional pre-authentication required',
@@ -200,6 +211,7 @@ realm.run(['./icred', realm.user_princ, password('user')],
 
 # Test a client-side tryagain failure, falling back to encrypted
 # timestamp.
+mark('tryagain (client failure)')
 msgs = ('Sending unauthenticated request',
         '/Additional pre-authentication required',
         'Preauthenticating using KDC method data',
@@ -221,6 +233,7 @@ realm.run(['./icred', '-X', 'fail_tryagain', realm.user_princ,
 
 # Test a client-side tryagain failure, stopping because the test
 # module disabled fallback.
+mark('tryagain (client failure, no fallback)')
 msgs = ('Sending unauthenticated request',
         '/Additional pre-authentication required',
         'Preauthenticating using KDC method data',
@@ -239,6 +252,7 @@ realm.run(['./icred', '-X', 'fail_tryagain', '-X', 'disable_fallback',
 # Test that multiple stepwise initial creds operations can be
 # performed with the same krb5_context, with proper tracking of
 # clpreauth module request handles.
+mark('interleaved')
 realm.run([kadminl, 'addprinc', '-pw', 'pw', 'u1'])
 realm.run([kadminl, 'addprinc', '+requires_preauth', '-pw', 'pw', 'u2'])
 realm.run([kadminl, 'addprinc', '+requires_preauth', '-pw', 'pw', 'u3'])
diff --git a/src/tests/t_proxy.py b/src/tests/t_proxy.py
index 4e86fce..428d2f6 100755
--- a/src/tests/t_proxy.py
+++ b/src/tests/t_proxy.py
@@ -66,6 +66,7 @@ def start_proxy(realm, keycertpem):
     return realm.start_server(cmd, sentinel='proxy server ready')
 
 # Fail: untrusted issuer and hostname doesn't match.
+mark('untrusted issuer, hostname mismatch')
 output("running pass 1: issuer not trusted and hostname doesn't match\n")
 realm = K5Realm(krb5_conf=unanchored_krb5_conf, get_creds=False,
                 create_host=False)
@@ -75,6 +76,7 @@ stop_daemon(proxy)
 realm.stop()
 
 # Fail: untrusted issuer, host name matches subject.
+mark('untrusted issuer, hostname subject match')
 output("running pass 2: subject matches, issuer not trusted\n")
 realm = K5Realm(krb5_conf=unanchored_krb5_conf, get_creds=False,
                 create_host=False)
@@ -84,6 +86,7 @@ stop_daemon(proxy)
 realm.stop()
 
 # Fail: untrusted issuer, host name matches subjectAltName.
+mark('untrusted issuer, hostname SAN match')
 output("running pass 3: subjectAltName matches, issuer not trusted\n")
 realm = K5Realm(krb5_conf=unanchored_krb5_conf, get_creds=False,
                 create_host=False)
@@ -93,6 +96,7 @@ stop_daemon(proxy)
 realm.stop()
 
 # Fail: untrusted issuer, certificate signature is bad.
+mark('untrusted issuer, bad signature')
 output("running pass 4: subject matches, issuer not trusted\n")
 realm = K5Realm(krb5_conf=unanchored_krb5_conf, get_creds=False,
                 create_host=False)
@@ -102,6 +106,7 @@ stop_daemon(proxy)
 realm.stop()
 
 # Fail: trusted issuer but hostname doesn't match.
+mark('trusted issuer, hostname mismatch')
 output("running pass 5: issuer trusted but hostname doesn't match\n")
 realm = K5Realm(krb5_conf=anchored_name_krb5_conf, get_creds=False,
                 create_host=False)
@@ -111,6 +116,7 @@ stop_daemon(proxy)
 realm.stop()
 
 # Succeed: trusted issuer and host name matches subject.
+mark('trusted issuer, hostname subject match')
 output("running pass 6: issuer trusted, subject matches\n")
 realm = K5Realm(krb5_conf=anchored_name_krb5_conf, start_kadmind=True,
                 get_creds=False)
@@ -122,6 +128,7 @@ stop_daemon(proxy)
 realm.stop()
 
 # Succeed: trusted issuer and host name matches subjectAltName.
+mark('trusted issuer, hostname SAN match')
 output("running pass 7: issuer trusted, subjectAltName matches\n")
 realm = K5Realm(krb5_conf=anchored_name_krb5_conf, start_kadmind=True,
                 get_creds=False)
@@ -133,6 +140,7 @@ stop_daemon(proxy)
 realm.stop()
 
 # Fail: certificate signature is bad.
+mark('bad signature')
 output("running pass 8: issuer trusted and subjectAltName matches, sig bad\n")
 realm = K5Realm(krb5_conf=anchored_name_krb5_conf,
                 get_creds=False,
@@ -143,6 +151,7 @@ stop_daemon(proxy)
 realm.stop()
 
 # Fail: trusted issuer but IP doesn't match.
+mark('trusted issuer, IP mismatch')
 output("running pass 9: issuer trusted but no name matches IP\n")
 realm = K5Realm(krb5_conf=anchored_ipv4_krb5_conf, get_creds=False,
                 create_host=False)
@@ -152,6 +161,7 @@ stop_daemon(proxy)
 realm.stop()
 
 # Fail: trusted issuer, but subject does not match.
+mark('trusted issuer, IP mismatch (hostname in subject)')
 output("running pass 10: issuer trusted, but subject does not match IP\n")
 realm = K5Realm(krb5_conf=anchored_ipv4_krb5_conf, get_creds=False,
                 create_host=False)
@@ -161,6 +171,7 @@ stop_daemon(proxy)
 realm.stop()
 
 # Succeed: trusted issuer and host name matches subjectAltName.
+mark('trusted issuer, IP SAN match')
 output("running pass 11: issuer trusted, subjectAltName matches IP\n")
 realm = K5Realm(krb5_conf=anchored_ipv4_krb5_conf, start_kadmind=True,
                 get_creds=False)
@@ -172,6 +183,7 @@ stop_daemon(proxy)
 realm.stop()
 
 # Fail: certificate signature is bad.
+mark('bad signature (IP hostname)')
 output("running pass 12: issuer trusted, names don't match, signature bad\n")
 realm = K5Realm(krb5_conf=anchored_ipv4_krb5_conf, get_creds=False,
                 create_host=False)
@@ -182,6 +194,7 @@ realm.stop()
 
 # Succeed: trusted issuer and host name matches subject, using kadmin
 # configuration to find kpasswdd.
+mark('trusted issuer, hostname subject match (kadmin)')
 output("running pass 13: issuer trusted, subject matches\n")
 realm = K5Realm(krb5_conf=anchored_kadmin_krb5_conf, start_kadmind=True,
                 get_creds=False, create_host=False)
@@ -192,6 +205,7 @@ realm.stop()
 
 # Succeed: trusted issuer and host name matches subjectAltName, using
 # kadmin configuration to find kpasswdd.
+mark('trusted issuer, hostname SAN match (kadmin)')
 output("running pass 14: issuer trusted, subjectAltName matches\n")
 realm = K5Realm(krb5_conf=anchored_kadmin_krb5_conf, start_kadmind=True,
                 get_creds=False, create_host=False)
@@ -202,6 +216,7 @@ realm.stop()
 
 # Succeed: trusted issuer and host name matches subjectAltName (give or take
 # case).
+mark('trusted issuer, hostname SAN case-insensitive match')
 output("running pass 15: issuer trusted, subjectAltName case-insensitive\n")
 realm = K5Realm(krb5_conf=anchored_upcasename_krb5_conf, start_kadmind=True,
                 get_creds=False, create_host=False)
diff --git a/src/tests/t_pwqual.py b/src/tests/t_pwqual.py
index 011110b..6114271 100755
--- a/src/tests/t_pwqual.py
+++ b/src/tests/t_pwqual.py
@@ -17,6 +17,8 @@ f.close()
 
 realm.run([kadminl, 'addpol', 'pol'])
 
+mark('pwqual modules')
+
 # The built-in "empty" module rejects empty passwords even without a policy.
 realm.run([kadminl, 'addprinc', '-pw', '', 'p1'], expected_code=1,
           expected_msg='Empty passwords are not allowed')
@@ -40,6 +42,8 @@ realm.run([kadminl, 'addprinc', '-pw', 'birdsoranges', 'p6'], expected_code=1,
 # These plugin ordering tests aren't specifically related to the
 # password quality interface, but are convenient to put here.
 
+mark('plugin module order')
+
 def test_order(realm, testname, conf, expected):
     conf = {'plugins': {'pwqual': conf}}
     env = realm.special_env(testname, False, krb5_conf=conf)
diff --git a/src/tests/t_rdreq.py b/src/tests/t_rdreq.py
index f67c348..1fb258b 100755
--- a/src/tests/t_rdreq.py
+++ b/src/tests/t_rdreq.py
@@ -25,46 +25,54 @@ def test(tserver, server, expected):
 
 
 # No keytab present.
+mark('no keytab')
 nokeytab_err = "45 Key table file '%s' not found" % realm.keytab
 test(princ1, None, nokeytab_err)
 test(princ1, princ1, nokeytab_err)
 test(princ1, matchprinc, nokeytab_err)
 
 # Keytab present, successful decryption.
+mark('success')
 realm.extract_keytab(princ1, realm.keytab)
 test(princ1, None, '0 success')
 test(princ1, princ1, '0 success')
 test(princ1, matchprinc, '0 success')
 
 # Explicit server principal not found in keytab.
+mark('explicit server not found')
 test(princ2, princ2, '45 No key table entry found for host/[email protected]')
 
 # Matching server principal does not match any entries in keytab (with
 # and without ticket server present in keytab).
+mark('matching server')
 nomatch_err = '45 Server principal x/@ does not match any keys in keytab'
 test(princ1, nomatchprinc, nomatch_err)
 test(princ2, nomatchprinc, nomatch_err)
 
 # Ticket server does not match explicit server principal (with and
 # without ticket server present in keytab).
+mark('ticket server mismatch')
 test(princ1, princ2, '45 No key table entry found for host/[email protected]')
 test(princ2, princ1,
      '35 Cannot decrypt ticket for host/[email protected] using keytab key for '
      'host/[email protected]')
 
 # Ticket server not found in keytab during iteration.
+mark('ticket server not found')
 test(princ2, None,
      '35 Request ticket server host/[email protected] not found in keytab '
      '(ticket kvno 1)')
 
 # Ticket server found in keytab but is not matched by server principal
 # (but other principals in keytab do match).
+mark('ticket server mismatch (matching)')
 realm.extract_keytab(princ3, realm.keytab)
 test(princ3, matchprinc,
      '35 Request ticket server HTTP/[email protected] found in keytab but does '
      'not match server principal host/@')
 
 # Service ticket is out of date.
+mark('outdated service ticket')
 os.remove(realm.keytab)
 realm.run([kadminl, 'ktadd', princ1])
 test(princ1, None,
@@ -74,11 +82,13 @@ test(princ1, princ1,
      '44 Cannot find key for host/[email protected] kvno 1 in keytab')
 
 # kvno mismatch due to ticket principal mismatch with explicit server.
+mark('ticket server mismatch (kvno)')
 test(princ2, princ1,
      '35 Cannot find key for host/[email protected] kvno 1 in keytab (request '
      'ticket server host/[email protected])')
 
 # Keytab is out of date.
+mark('outdated keytab')
 realm.run([kadminl, 'cpw', '-randkey', princ1])
 realm.kinit(realm.user_princ, password('user'))
 test(princ1, None,
@@ -88,6 +98,7 @@ test(princ1, princ1,
      '44 Cannot find key for host/[email protected] kvno 3 in keytab')
 
 # Ticket server and kvno found but not with ticket enctype.
+mark('missing enctype')
 os.remove(realm.keytab)
 realm.extract_keytab(princ1, realm.keytab)
 pkeytab = realm.keytab + '.partial'
@@ -105,6 +116,7 @@ test(princ1, None,
 test(princ1, princ1, '45 No key table entry found for host/[email protected]')
 
 # Ticket server, kvno, and enctype matched, but key does not work.
+mark('wrong key')
 realm.run([kadminl, 'cpw', '-randkey', princ1])
 realm.run([kadminl, 'modprinc', '-kvno', '3', princ1])
 os.remove(realm.keytab)
@@ -118,6 +130,7 @@ test(princ1, princ1,
 
 # Test that aliases work.  The ticket server (princ4) isn't present in
 # keytab, but there is a usable princ1 entry with the same key.
+mark('aliases')
 realm.run([kadminl, 'renprinc', princ1, princ4])
 test(princ4, None, '0 success')
 test(princ4, princ1, '0 success')
diff --git a/src/tests/t_referral.py b/src/tests/t_referral.py
index 98fdf29..5b9f74b 100755
--- a/src/tests/t_referral.py
+++ b/src/tests/t_referral.py
@@ -39,6 +39,7 @@ def restart_kdc(realm, kdc_conf):
 # With no KDC configuration besides [domain_realm], we should get a
 # referral for a NT-SRV-HST or NT-SRV-INST server name, but not an
 # NT-UNKNOWN or NT-PRINCIPAL server name.
+mark('[domain-realm] only')
 testref(realm, 'srv-hst')
 testref(realm, 'srv-inst')
 testfail(realm, 'principal')
@@ -50,6 +51,7 @@ testfail(realm, 'unknown')
 # section, with the realm values supplementing the kdcdefaults values.
 # NT-SRV-HST server names should be unaffected by host_based_services,
 # and NT-PRINCIPAL server names shouldn't get a referral regardless.
+mark('host_based_services')
 restart_kdc(realm, {'kdcdefaults': {'host_based_services': '*'}})
 testref(realm, 'unknown')
 testfail(realm, 'principal')
@@ -69,6 +71,7 @@ testref(realm, 'srv-hst')
 
 # With no_host_referrals matching the first server name component, we
 # should not get a referral even for NT-SRV-HOST server names
+mark('no_host_referral')
 restart_kdc(realm, {'kdcdefaults': {'no_host_referral': '*'}})
 testfail(realm, 'srv-hst')
 restart_kdc(realm, {'kdcdefaults': {'no_host_referral': ['b', 'a,c']}})
@@ -95,6 +98,7 @@ refrealm.stop()
 
 # Regression test for #7483: a KDC should not return a host referral
 # to its own realm.
+mark('#7483 regression test')
 drealm = {'domain_realm': {'d': 'KRBTEST.COM'}}
 realm = K5Realm(kdc_conf=drealm, create_host=False)
 tracefile = os.path.join(realm.testdir, 'trace')
@@ -110,6 +114,7 @@ realm.stop()
 # Test client referrals.  Use the test KDB module for KRBTEST1.COM to
 # simulate referrals since our built-in modules do not support them.
 # No cross-realm TGTs are necessary.
+mark('client referrals')
 kdcconf = {'realms': {'$realm': {'database_module': 'test'}},
            'dbmodules': {'test': {'db_library': 'test',
                                   'alias': {'user': '@KRBTEST2.COM',
diff --git a/src/tests/t_renew.py b/src/tests/t_renew.py
index 034190c..8840416 100755
--- a/src/tests/t_renew.py
+++ b/src/tests/t_renew.py
@@ -52,6 +52,7 @@ def test(testname, life, rlife, exp_life, exp_rlife, env=None):
 test('simple', '1h', '2h', 3600, 7200)
 
 # Renew twice, to test that renewed tickets are renewable.
+mark('renew twice')
 realm.kinit(realm.user_princ, flags=['-R'])
 realm.kinit(realm.user_princ, flags=['-R'])
 realm.klist(realm.user_princ)
@@ -60,16 +61,19 @@ realm.klist(realm.user_princ)
 realm.run([kvno, realm.user_princ])
 
 # Make sure we can't renew non-renewable tickets.
+mark('non-renewable')
 test('non-renewable', '1h', None, 3600, None)
 realm.kinit(realm.user_princ, flags=['-R'], expected_code=1,
             expected_msg="KDC can't fulfill requested option")
 
 # Test that -allow_renewable on the client principal works.
+mark('allow_renewable (client)')
 realm.run([kadminl, 'modprinc', '-allow_renewable', 'user'])
 test('disallowed client', '1h', '2h', 3600, None)
 realm.run([kadminl, 'modprinc', '+allow_renewable', 'user'])
 
 # Test that -allow_renewable on the server principal works.
+mark('allow_renewable (server)')
 realm.run([kadminl, 'modprinc', '-allow_renewable',  realm.krbtgt_princ])
 test('disallowed server', '1h', '2h', 3600, None)
 realm.run([kadminl, 'modprinc', '+allow_renewable', realm.krbtgt_princ])
@@ -77,10 +81,12 @@ realm.run([kadminl, 'modprinc', '+allow_renewable', realm.krbtgt_princ])
 # Test that trivially renewable tickets are issued if renew_till <=
 # till.  (Our client code bumps up the requested renewable life to the
 # requested life.)
+mark('trivially renewable')
 test('short', '2h', '1h', 7200, 7200)
 
 # Test that renewable tickets are issued if till > max life by
 # default, but not if we configure away the RENEWABLE-OK option.
+mark('renewable-ok')
 no_opts_conf = {'libdefaults': {'kdc_default_options': '0'}}
 no_opts = realm.special_env('no_opts', False, krb5_conf=no_opts_conf)
 realm.run([kadminl, 'modprinc', '-maxlife', '10 hours', 'user'])
@@ -89,17 +95,20 @@ test('long noopts', '15h', None, 10 * 3600, None, env=no_opts)
 realm.run([kadminl, 'modprinc', '-maxlife', '20 hours', 'user'])
 
 # Test maximum renewable life on the client principal.
+mark('maxrenewlife (client)')
 realm.run([kadminl, 'modprinc', '-maxrenewlife', '5 hours', 'user'])
 test('maxrenewlife client 1', '4h', '5h', 4 * 3600, 5 * 3600)
 test('maxrenewlife client 2', '6h', '10h', 6 * 3600, 5 * 3600)
 
 # Test maximum renewable life on the server principal.
+mark('maxrenewlife (server)')
 realm.run([kadminl, 'modprinc', '-maxrenewlife', '3 hours',
            realm.krbtgt_princ])
 test('maxrenewlife server 1', '2h', '3h', 2 * 3600, 3 * 3600)
 test('maxrenewlife server 2', '4h', '8h', 4 * 3600, 3 * 3600)
 
 # Test realm maximum life.
+mark('realm maximum life')
 realm.run([kadminl, 'modprinc', '-maxrenewlife', '40 hours', 'user'])
 realm.run([kadminl, 'modprinc', '-maxrenewlife', '40 hours',
            realm.krbtgt_princ])
diff --git a/src/tests/t_skew.py b/src/tests/t_skew.py
index f2ae066..fffc634 100755
--- a/src/tests/t_skew.py
+++ b/src/tests/t_skew.py
@@ -7,6 +7,7 @@ realm.start_kdc(['-T', '-3600'])
 
 # kinit (no preauth) should work, and should set a clock skew allowing
 # kvno to work, with or without FAST.
+mark('kdc_timesync enabled, no preauth')
 realm.kinit(realm.user_princ, password('user'))
 realm.run([kvno, realm.host_princ])
 realm.kinit(realm.user_princ, password('user'), flags=['-T', realm.ccache])
@@ -14,6 +15,7 @@ realm.run([kvno, realm.host_princ])
 realm.run([kdestroy])
 
 # kinit (with preauth) should work, with or without FAST.
+mark('kdc_timesync enabled, with preauth')
 realm.run([kadminl, 'modprinc', '+requires_preauth', 'user'])
 realm.kinit(realm.user_princ, password('user'))
 realm.run([kvno, realm.host_princ])
@@ -37,12 +39,14 @@ realm.kinit(realm.user_princ, password('user'),
 
 # kinit should detect too much skew in the KDC response.  kinit with
 # FAST should fail from the KDC since the armor AP-REQ won't be valid.
+mark('KDC timesync disabled, no preauth')
 realm.kinit(realm.user_princ, password('user'), expected_code=1,
             expected_msg='Clock skew too great in KDC reply')
 realm.kinit(realm.user_princ, None, flags=['-T', fast_cache], expected_code=1,
             expected_msg='Clock skew too great while')
 
 # kinit (with preauth) should fail from the KDC, with or without FAST.
+mark('KDC timesync disabled, with preauth')
 realm.run([kadminl, 'modprinc', '+requires_preauth', 'user'])
 realm.kinit(realm.user_princ, password('user'), expected_code=1,
             expected_msg='Clock skew too great while')
diff --git a/src/tests/t_sn2princ.py b/src/tests/t_sn2princ.py
index 19a0d2f..66f31e2 100755
--- a/src/tests/t_sn2princ.py
+++ b/src/tests/t_sn2princ.py
@@ -40,6 +40,7 @@ def testu(host, princhost, princrealm):
 
 # With the unknown principal type, we do not canonicalize or downcase,
 # but we do remove a trailing period and look up the realm.
+mark('unknown type')
 testu('ptr-mismatch.kerberos.org', 'ptr-mismatch.kerberos.org', 'R1')
 testu('Example.COM', 'Example.COM', 'R2')
 testu('abcde', 'abcde', '')
@@ -47,6 +48,7 @@ testu('abcde', 'abcde', '')
 # A ':port' or ':instance' trailer should be ignored for realm lookup.
 # If there is more than one colon in the name, we assume it's an IPv6
 # address and don't treat it as having a trailer.
+mark('port trailer')
 testu('example.com.:123', 'example.com.:123', 'R2')
 testu('Example.COM:xyZ', 'Example.COM:xyZ', 'R2')
 testu('example.com.::123', 'example.com.::123', '')
@@ -54,6 +56,7 @@ testu('example.com.::123', 'example.com.::123', '')
 # With dns_canonicalize_hostname=false, we downcase and remove
 # trailing dots but do not canonicalize the hostname.  Trailers do not
 # get downcased.
+mark('dns_canonicalize_host=false')
 testnc('ptr-mismatch.kerberos.org', 'ptr-mismatch.kerberos.org', 'R1')
 testnc('Example.COM', 'example.com', 'R2')
 testnc('abcde', 'abcde', '')
@@ -80,6 +83,7 @@ if canonname.lower() != fname:
               '%s forward resolves to %s, not %s' % (oname, canonname, fname))
 
 # Test forward-only canonicalization (rdns=false).
+mark('rdns=false')
 testnr(oname, fname, 'R1')
 testnr(oname + ':123', fname + ':123', 'R1')
 testnr(oname + ':xyZ', fname + ':xyZ', 'R1')
@@ -96,6 +100,7 @@ if rname == fname:
               'which should be different from %s' % (oname, rname, fname))
 
 # Test default canonicalization (forward and reverse lookup).
+mark('default')
 test(oname, rname, 'R3')
 test(oname + ':123', rname + ':123', 'R3')
 test(oname + ':xyZ', rname + ':xyZ', 'R3')
diff --git a/src/tests/t_spake.py b/src/tests/t_spake.py
index 5b47e62..15a6439 100644
--- a/src/tests/t_spake.py
+++ b/src/tests/t_spake.py
@@ -10,7 +10,7 @@ else:
     groups = builtin_groups
 
 for gnum, gname in groups:
-    output('*** Testing group %s\n' % gname)
+    mark('group %s' % gname)
     conf = {'libdefaults': {'spake_preauth_groups': gname}}
     for realm in multipass_realms(create_user=False, create_host=False,
                                   krb5_conf=conf):
@@ -49,6 +49,7 @@ realm = K5Realm(create_user=False, krb5_conf=conf, kdc_conf=kdcconf)
 realm.run([kadminl, 'addprinc', '+preauth', '-pw', 'pw', 'user'])
 
 # Test with FAST.
+mark('FAST')
 msgs = ('Using FAST due to armor ccache negotiation',
         'FAST armor key:',
         'Sending unauthenticated request',
@@ -68,6 +69,7 @@ realm.kinit(realm.host_princ, flags=['-k'])
 realm.kinit('user', 'pw', flags=['-T', realm.ccache], expected_trace=msgs)
 
 # Test optimistic client preauth (151 is PA-SPAKE).
+mark('client optimistic')
 msgs = ('Attempting optimistic preauth',
         'Processing preauth types: PA-SPAKE (151)',
         'Sending SPAKE support message',
@@ -82,6 +84,7 @@ msgs = ('Attempting optimistic preauth',
 realm.run(['./icred', '-o', '151', 'user', 'pw'], expected_trace=msgs)
 
 # Test KDC optimistic challenge (accepted by client).
+mark('KDC optimistic')
 oconf = {'kdcdefaults': {'spake_preauth_kdc_challenge': 'edwards25519'}}
 oenv = realm.special_env('ochal', True, krb5_conf=oconf)
 realm.stop_kdc()
@@ -101,6 +104,7 @@ if runenv.have_spake_openssl != 'yes':
 
 # Test optimistic client preauth falling back to encrypted timestamp
 # because the KDC doesn't support any of the client groups.
+mark('client optimistic (fallback)')
 p256conf={'libdefaults': {'spake_preauth_groups': 'P-256'}}
 p256env = realm.special_env('p256', False, krb5_conf=p256conf)
 msgs = ('Attempting optimistic preauth',
@@ -117,6 +121,7 @@ realm.run(['./icred', '-o', '151', 'user', 'pw'], env=p256env,
           expected_trace=msgs)
 
 # Test KDC optimistic challenge (rejected by client).
+mark('KDC optimistic (rejected)')
 rconf = {'libdefaults': {'spake_preauth_groups': 'P-384,edwards25519'},
          'kdcdefaults': {'spake_preauth_kdc_challenge': 'P-384'}}
 renv = realm.special_env('ochal', True, krb5_conf=rconf)
@@ -138,6 +143,7 @@ msgs = ('Sending unauthenticated request',
 realm.kinit('user', 'pw', expected_trace=msgs)
 
 # Check that the auth indicator for SPAKE is properly included by the KDC.
+mark('auth indicator')
 realm.run([kvno, realm.host_princ])
 realm.run(['./adata', realm.host_princ], expected_msg='+97: [indspake]')
 
diff --git a/src/tests/t_y2038.py b/src/tests/t_y2038.py
index 02e946d..a9017b4 100644
--- a/src/tests/t_y2038.py
+++ b/src/tests/t_y2038.py
@@ -17,6 +17,7 @@ realm.start_kdc(['-T', '662256000'])
 # kinit without preauth should succeed with clock skew correction, but
 # will result in an expired ticket, because we sent an absolute end
 # time and didn't get a chance to correct it..
+mark('kinit, no preauth')
 realm.kinit(realm.user_princ, password('user'))
 realm.run([kvno, realm.host_princ], expected_code=1,
           expected_msg='Ticket expired')
@@ -24,6 +25,7 @@ realm.run([kvno, realm.host_princ], expected_code=1,
 # kinit with preauth should succeed and result in a valid ticket, as
 # we get a chance to correct the end time based on the KDC time.  Try
 # with encrypted timestamp and encrypted challenge.
+mark('kinit, with preauth')
 realm.run([kadminl, 'modprinc', '+requires_preauth', 'user'])
 realm.kinit(realm.user_princ, password('user'))
 realm.run([kvno, realm.host_princ])
@@ -32,6 +34,7 @@ realm.run([kvno, realm.host_princ])
 
 # Test that expiration warning works after y2038, by setting a
 # password expiration time ten minutes after the KDC time.
+mark('expiration warning')
 realm.run([kadminl, 'modprinc', '-pwexpire', '662256600 seconds', 'user'])
 out = realm.kinit(realm.user_princ, password('user'))
 if 'will expire in less than one hour' not in out:
@@ -48,11 +51,13 @@ realm.prep_kadmin()
 # Test getdate parsing of absolute timestamps after 2038 and
 # marshalling over the kadmin protocol.  The local time zone will
 # affect the display time by a little bit, so just look for the year.
+mark('kadmin marshalling')
 realm.run_kadmin(['modprinc', '-pwexpire', '2040-02-03', realm.host_princ])
 realm.run_kadmin(['getprinc', realm.host_princ], expected_msg=' 2040\n')
 
 # Get a ticket whose lifetime crosses the y2038 boundary and
 # range-check the expiration year as reported by klist.
+mark('ticket lifetime across y2038')
 realm.kinit(realm.user_princ, password('user'),
             flags=['-l', '8000d', '-r', '8500d'])
 realm.run([kvno, realm.host_princ])
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.