krb5 commit: Add marks to longer Python test scripts

Greg Hudson ghudson at mit.edu
Sun Apr 22 13:11:01 EDT 2018


https://github.com/krb5/krb5/commit/0879b7d506c0a1bb77c85d7499a55d91743ad9a8
commit 0879b7d506c0a1bb77c85d7499a55d91743ad9a8
Author: Greg Hudson <ghudson at mit.edu>
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 at 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/M at KRBTEST.COM')
 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: krbPrincipalName=canon at KRBTEST.COM,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', 'fake at KRBTEST.COM', '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, 'user at KRBTEST.COM', 'user', 'default rule 1')
 test_an2ln(None, 'user/KRBTEST.COM at KRBTEST.COM', 'user', 'default rule 2')
 test_an2ln_err(None, 'user/KRBTEST.COM/x at KRBTEST.COM', 'No translation',
@@ -35,6 +36,7 @@ test_an2ln_err(None, 'user/X at KRBTEST.COM', 'No translation',
 test_an2ln_err(None, 'user at 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, 'user at KRBTEST.COM', '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, 'user at KRBTEST.COM', '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 at X', 'Improper format', 'null rule')
 auth3 = a2l_realm('auth3', 'UNRECOGNIZED:stuff')
@@ -65,6 +69,7 @@ test_an2ln_err(auth3, 'user at 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, 'user at KRBTEST.COM', 'user', 'empty rule')
 test_an2ln(rule1, 'user at X', 'user', 'empty rule (foreign realm)')
@@ -72,23 +77,27 @@ test_an2ln(rule1, 'a/b/c at 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 at REALM', 'REALM.burr.aaron', 'selection string')
 test_an2ln_err(rule2, 'user at KRBTEST.COM', 'No translation', 'suppress default')
 
 # Test match string.
+mark('rule (match string)')
 rule3 = a2l_realm('rule3', 'RULE:(.*tail)')
 test_an2ln(rule3, 'withtail at X', 'withtail', 'rule match 1')
 test_an2ln(rule3, 'x/withtail at X', 'x/withtail', 'rule match 2')
 test_an2ln_err(rule3, 'tails at X', 'No translation', 'rule anchor mismatch')
 
 # Test substitutions.
+mark('rule (substitutions)')
 rule4 = a2l_realm('rule4', 'RULE:s/birds/bees/')
 test_an2ln(rule4, 'thebirdsbirdsbirds at X', 'thebeesbirdsbirds', 'subst 1')
 rule5 = a2l_realm('rule4', 'RULE:s/birds/bees/g  s/bees/birds/')
 test_an2ln(rule4, 'the/birdsbirdsbirds at 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 at X', 'zazz', 'combo 5')
 test_an2ln_err(combo, 'a/b at KRBTEST.COM', 'No translation', 'combo 6')
 
 # Test the an2ln userok method with the combo environment.
+mark('userok (an2ln)')
 test_userok(combo, 'fred at X', 'fred X', True, 'combo userok 1')
 test_userok(combo, 'user at KRBTEST.COM', 'user', True, 'combo userok 2')
 test_userok(combo, 'user at KRBTEST.COM', 'X', False, 'combo userok 3')
 test_userok(combo, 'a/b at KRBTEST.COM', '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 user at KRBTEST.COM 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 at 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>^user at KRBTEST.COM$'
 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/2 at KRBTEST.COM')
 
 # 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/2 at KRBTEST.COM')
 test(princ2, princ1,
      '35 Cannot decrypt ticket for host/2 at KRBTEST.COM using keytab key for '
      'host/1 at KRBTEST.COM')
 
 # Ticket server not found in keytab during iteration.
+mark('ticket server not found')
 test(princ2, None,
      '35 Request ticket server host/2 at KRBTEST.COM 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/3 at KRBTEST.COM 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/1 at KRBTEST.COM 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/1 at KRBTEST.COM kvno 1 in keytab (request '
      'ticket server host/2 at KRBTEST.COM)')
 
 # 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/1 at KRBTEST.COM 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/1 at KRBTEST.COM')
 
 # 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])


More information about the cvs-krb5 mailing list