@@ -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\t b' , b'a&AAk-b' ),
14191422 ('\x00 ' , b'&AAA-' ),
14201423 ('Entw\xfc rfe' , 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
14261430class 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\x80 b' , 1 ), # 8-bit byte outside a shift sequence
1446- (b'a\x1f b' , 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\x80 b' , 1 ), # 8-bit byte outside a shift sequence
1446+ (b'a\x1f b' , 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