Skip to content

Commit 09ed62f

Browse files
gh-66788: Use test.support.subTests in utf-7-imap codec tests
Convert the parametrized utf-7-imap codec tests to the subTests() decorator. Add round-trip cases for "+", "+-" (literal, unlike in UTF-7) and a "," in the Base64 run, and replace the duplicate decode-error cases with a "&" at the end of the input and an unterminated but otherwise valid Base64 sequence. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 083de62 commit 09ed62f

1 file changed

Lines changed: 26 additions & 27 deletions

File tree

Lib/test/test_codecs.py

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1411,46 +1411,45 @@ def test_decode_invalid(self):
14111411
('&', b'&-'),
14121412
('&&', b'&-&-'),
14131413
('A&B', b'A&-B'),
1414+
# "+" and "+-" are literal, unlike in UTF-7 where "+" is the shift character.
1415+
('+', b'+'),
1416+
('+-', b'+-'),
14141417
# RFC 3501 section 5.1.3 example.
14151418
('~peter/mail/台北/日本語',
14161419
b'~peter/mail/&U,BTFw-/&ZeVnLIqe-'),
14171420
# Non-printable ASCII (including TAB) is Base64-encoded, not direct.
14181421
('a\tb', b'a&AAk-b'),
14191422
('\x00', b'&AAA-'),
14201423
('Entw\xfcrfe', b'Entw&APw-rfe'),
1424+
('ϰ', b'&A,A-'), # "," in the modified Base64 alphabet
14211425
('☃', b'&JgM-'), # snowman
14221426
('\U0001f600', b'&2D3eAA-'), # non-BMP (surrogate pair)
14231427
('Sent &\N{DELETE}', b'Sent &-&AH8-'),
14241428
]
14251429

14261430
class Utf7ImapTest(unittest.TestCase):
1427-
def test_encode(self):
1428-
for uni, encoded in utf_7_imap_testcases:
1429-
with self.subTest(uni=uni):
1430-
self.assertEqual(uni.encode('utf-7-imap'), encoded)
1431-
1432-
def test_decode(self):
1433-
for uni, encoded in utf_7_imap_testcases:
1434-
with self.subTest(encoded=encoded):
1435-
self.assertEqual(encoded.decode('utf-7-imap'), uni)
1436-
1437-
def test_decode_invalid(self):
1438-
# position of the first offending byte in each case
1439-
testcases = [
1440-
(b'&AAk', 0), # unterminated shift sequence
1441-
(b'&Jgg', 0), # unterminated shift sequence
1442-
(b'&AB-', 0), # Base64 length not a multiple of a code unit
1443-
(b'&@@@-', 0), # invalid Base64
1444-
(b'&====-', 0), # invalid Base64
1445-
(b'a\x80b', 1), # 8-bit byte outside a shift sequence
1446-
(b'a\x1fb', 1), # control byte outside a shift sequence
1447-
]
1448-
for encoded, start in testcases:
1449-
with self.subTest(encoded=encoded):
1450-
with self.assertRaises(UnicodeDecodeError) as cm:
1451-
encoded.decode('utf-7-imap')
1452-
self.assertEqual(cm.exception.encoding, 'utf-7-imap')
1453-
self.assertEqual(cm.exception.start, start)
1431+
@support.subTests('uni,encoded', utf_7_imap_testcases)
1432+
def test_encode(self, uni, encoded):
1433+
self.assertEqual(uni.encode('utf-7-imap'), encoded)
1434+
1435+
@support.subTests('uni,encoded', utf_7_imap_testcases)
1436+
def test_decode(self, uni, encoded):
1437+
self.assertEqual(encoded.decode('utf-7-imap'), uni)
1438+
1439+
# 'start' is the position of the first offending byte in each case.
1440+
@support.subTests('encoded,start', [
1441+
(b'x&', 1), # "&" just before the end, unterminated
1442+
(b'&AAAA', 0), # unterminated, though the Base64 is valid
1443+
(b'&AB-', 0), # Base64 length not a multiple of a code unit
1444+
(b'&@@@-', 0), # invalid Base64
1445+
(b'a\x80b', 1), # 8-bit byte outside a shift sequence
1446+
(b'a\x1fb', 1), # control byte outside a shift sequence
1447+
])
1448+
def test_decode_invalid(self, encoded, start):
1449+
with self.assertRaises(UnicodeDecodeError) as cm:
1450+
encoded.decode('utf-7-imap')
1451+
self.assertEqual(cm.exception.encoding, 'utf-7-imap')
1452+
self.assertEqual(cm.exception.start, start)
14541453

14551454
def test_encode_lone_surrogate(self):
14561455
with self.assertRaises(UnicodeEncodeError):

0 commit comments

Comments
 (0)