1
1
import unittest
2
2
import base64
3
3
import binascii
4
+ import string
4
5
import os
5
6
from array import array
6
7
from test .support import cpython_only
@@ -14,6 +15,8 @@ class LazyImportTest(unittest.TestCase):
14
15
def test_lazy_import (self ):
15
16
ensure_lazy_imports ("base64" , {"re" , "getopt" })
16
17
18
+ from test .support .hypothesis_helper import hypothesis
19
+
17
20
18
21
class LegacyBase64TestCase (unittest .TestCase ):
19
22
@@ -68,6 +71,13 @@ def test_decodebytes(self):
68
71
eq (base64 .decodebytes (array ('B' , b'YWJj\n ' )), b'abc' )
69
72
self .check_type_errors (base64 .decodebytes )
70
73
74
+ @hypothesis .given (payload = hypothesis .strategies .binary ())
75
+ @hypothesis .example (b'abcdefghijklmnopqrstuvwxyz' )
76
+ def test_bytes_encode_decode_round_trip (self , payload ):
77
+ encoded = base64 .encodebytes (payload )
78
+ decoded = base64 .decodebytes (encoded )
79
+ self .assertEqual (payload , decoded )
80
+
71
81
def test_encode (self ):
72
82
eq = self .assertEqual
73
83
from io import BytesIO , StringIO
@@ -96,6 +106,19 @@ def test_decode(self):
96
106
self .assertRaises (TypeError , base64 .encode , BytesIO (b'YWJj\n ' ), StringIO ())
97
107
self .assertRaises (TypeError , base64 .encode , StringIO ('YWJj\n ' ), StringIO ())
98
108
109
+ @hypothesis .given (payload = hypothesis .strategies .binary ())
110
+ @hypothesis .example (b'abcdefghijklmnopqrstuvwxyz' )
111
+ def test_legacy_encode_decode_round_trip (self , payload ):
112
+ from io import BytesIO
113
+ payload_file_r = BytesIO (payload )
114
+ encoded_file_w = BytesIO ()
115
+ base64 .encode (payload_file_r , encoded_file_w )
116
+ encoded_file_r = BytesIO (encoded_file_w .getvalue ())
117
+ decoded_file_w = BytesIO ()
118
+ base64 .decode (encoded_file_r , decoded_file_w )
119
+ decoded = decoded_file_w .getvalue ()
120
+ self .assertEqual (payload , decoded )
121
+
99
122
100
123
class BaseXYTestCase (unittest .TestCase ):
101
124
@@ -276,6 +299,44 @@ def test_b64decode_invalid_chars(self):
276
299
self .assertEqual (base64 .b64decode (b'++[[//]]' , b'[]' ), res )
277
300
self .assertEqual (base64 .urlsafe_b64decode (b'++--//__' ), res )
278
301
302
+
303
+ def _altchars_strategy ():
304
+ """Generate 'altchars' for base64 encoding."""
305
+ reserved_chars = (string .digits + string .ascii_letters + "=" ).encode ()
306
+ allowed_chars = hypothesis .strategies .sampled_from (
307
+ [n for n in range (256 ) if n not in reserved_chars ])
308
+ two_bytes_strategy = hypothesis .strategies .lists (
309
+ allowed_chars , min_size = 2 , max_size = 2 , unique = True ).map (bytes )
310
+ return (hypothesis .strategies .none ()
311
+ | hypothesis .strategies .just (b"_-" )
312
+ | two_bytes_strategy )
313
+
314
+ @hypothesis .given (
315
+ payload = hypothesis .strategies .binary (),
316
+ altchars = _altchars_strategy (),
317
+ validate = hypothesis .strategies .booleans ())
318
+ @hypothesis .example (b'abcdefghijklmnopqrstuvwxyz' , b"_-" , True )
319
+ @hypothesis .example (b'abcdefghijklmnopqrstuvwxyz' , b"_-" , False )
320
+ def test_b64_encode_decode_round_trip (self , payload , altchars , validate ):
321
+ encoded = base64 .b64encode (payload , altchars = altchars )
322
+ decoded = base64 .b64decode (encoded , altchars = altchars ,
323
+ validate = validate )
324
+ self .assertEqual (payload , decoded )
325
+
326
+ @hypothesis .given (payload = hypothesis .strategies .binary ())
327
+ @hypothesis .example (b'abcdefghijklmnopqrstuvwxyz' )
328
+ def test_standard_b64_encode_decode_round_trip (self , payload ):
329
+ encoded = base64 .standard_b64encode (payload )
330
+ decoded = base64 .standard_b64decode (encoded )
331
+ self .assertEqual (payload , decoded )
332
+
333
+ @hypothesis .given (payload = hypothesis .strategies .binary ())
334
+ @hypothesis .example (b'abcdefghijklmnopqrstuvwxyz' )
335
+ def test_urlsafe_b64_encode_decode_round_trip (self , payload ):
336
+ encoded = base64 .urlsafe_b64encode (payload )
337
+ decoded = base64 .urlsafe_b64decode (encoded )
338
+ self .assertEqual (payload , decoded )
339
+
279
340
def test_b32encode (self ):
280
341
eq = self .assertEqual
281
342
eq (base64 .b32encode (b'' ), b'' )
@@ -363,6 +424,19 @@ def test_b32decode_error(self):
363
424
with self .assertRaises (binascii .Error ):
364
425
base64 .b32decode (data .decode ('ascii' ))
365
426
427
+ @hypothesis .given (
428
+ payload = hypothesis .strategies .binary (),
429
+ casefold = hypothesis .strategies .booleans (),
430
+ map01 = (
431
+ hypothesis .strategies .none ()
432
+ | hypothesis .strategies .binary (min_size = 1 , max_size = 1 )))
433
+ @hypothesis .example (b'abcdefghijklmnopqrstuvwxyz' , True , None )
434
+ @hypothesis .example (b'abcdefghijklmnopqrstuvwxyz' , False , None )
435
+ def test_b32_encode_decode_round_trip (self , payload , casefold , map01 ):
436
+ encoded = base64 .b32encode (payload )
437
+ decoded = base64 .b32decode (encoded , casefold = casefold , map01 = map01 )
438
+ self .assertEqual (payload , decoded )
439
+
366
440
def test_b32hexencode (self ):
367
441
test_cases = [
368
442
# to_encode, expected
@@ -432,6 +506,15 @@ def test_b32hexdecode_error(self):
432
506
with self .assertRaises (binascii .Error ):
433
507
base64 .b32hexdecode (data .decode ('ascii' ))
434
508
509
+ @hypothesis .given (
510
+ payload = hypothesis .strategies .binary (),
511
+ casefold = hypothesis .strategies .booleans ())
512
+ @hypothesis .example (b'abcdefghijklmnopqrstuvwxyz' , True )
513
+ @hypothesis .example (b'abcdefghijklmnopqrstuvwxyz' , False )
514
+ def test_b32_hexencode_decode_round_trip (self , payload , casefold ):
515
+ encoded = base64 .b32hexencode (payload )
516
+ decoded = base64 .b32hexdecode (encoded , casefold = casefold )
517
+ self .assertEqual (payload , decoded )
435
518
436
519
def test_b16encode (self ):
437
520
eq = self .assertEqual
@@ -469,6 +552,16 @@ def test_b16decode(self):
469
552
# Incorrect "padding"
470
553
self .assertRaises (binascii .Error , base64 .b16decode , '010' )
471
554
555
+ @hypothesis .given (
556
+ payload = hypothesis .strategies .binary (),
557
+ casefold = hypothesis .strategies .booleans ())
558
+ @hypothesis .example (b'abcdefghijklmnopqrstuvwxyz' , True )
559
+ @hypothesis .example (b'abcdefghijklmnopqrstuvwxyz' , False )
560
+ def test_b16_encode_decode_round_trip (self , payload , casefold ):
561
+ endoded = base64 .b16encode (payload )
562
+ decoded = base64 .b16decode (endoded , casefold = casefold )
563
+ self .assertEqual (payload , decoded )
564
+
472
565
def test_a85encode (self ):
473
566
eq = self .assertEqual
474
567
@@ -799,6 +892,61 @@ def test_z85decode_errors(self):
799
892
self .assertRaises (ValueError , base64 .z85decode , b'%nSc' )
800
893
self .assertRaises (ValueError , base64 .z85decode , b'%nSc1' )
801
894
895
+ def add_padding (self , payload ):
896
+ """Add the expected padding for test_?85_encode_decode_round_trip."""
897
+ if len (payload ) % 4 != 0 :
898
+ padding = b"\0 " * ((- len (payload )) % 4 )
899
+ payload = payload + padding
900
+ return payload
901
+
902
+ @hypothesis .given (
903
+ payload = hypothesis .strategies .binary (),
904
+ foldspaces = hypothesis .strategies .booleans (),
905
+ wrapcol = (
906
+ hypothesis .strategies .just (0 )
907
+ | hypothesis .strategies .integers (1 , 1000 )),
908
+ pad = hypothesis .strategies .booleans (),
909
+ adobe = hypothesis .strategies .booleans (),
910
+ )
911
+ @hypothesis .example (b'abcdefghijklmnopqrstuvwxyz' , False , 0 , False , False )
912
+ @hypothesis .example (b'abcdefghijklmnopqrstuvwxyz' , False , 20 , True , True )
913
+ @hypothesis .example (b'abcdefghijklmnopqrstuvwxyz' , True , 0 , False , True )
914
+ @hypothesis .example (b'abcdefghijklmnopqrstuvwxyz' , True , 20 , True , False )
915
+ def test_a85_encode_decode_round_trip (
916
+ self , payload , foldspaces , wrapcol , pad , adobe
917
+ ):
918
+ encoded = base64 .a85encode (
919
+ payload , foldspaces = foldspaces , wrapcol = wrapcol ,
920
+ pad = pad , adobe = adobe ,
921
+ )
922
+ if wrapcol :
923
+ if adobe and wrapcol == 1 :
924
+ # "adobe" needs wrapcol to be at least 2.
925
+ # a85decode quietly uses 2 if 1 is given; it's not worth
926
+ # loudly deprecating this behavior.
927
+ wrapcol = 2
928
+ for line in encoded .splitlines (keepends = False ):
929
+ self .assertLessEqual (len (line ), wrapcol )
930
+ if adobe :
931
+ self .assertTrue (encoded .startswith (b'<~' ))
932
+ self .assertTrue (encoded .endswith (b'~>' ))
933
+ decoded = base64 .a85decode (encoded , foldspaces = foldspaces , adobe = adobe )
934
+ if pad :
935
+ payload = self .add_padding (payload )
936
+ self .assertEqual (payload , decoded )
937
+
938
+ @hypothesis .given (
939
+ payload = hypothesis .strategies .binary (),
940
+ pad = hypothesis .strategies .booleans ())
941
+ @hypothesis .example (b'abcdefghijklmnopqrstuvwxyz' , True )
942
+ @hypothesis .example (b'abcdefghijklmnopqrstuvwxyz' , False )
943
+ def test_b85_encode_decode_round_trip (self , payload , pad ):
944
+ encoded = base64 .b85encode (payload , pad = pad )
945
+ if pad :
946
+ payload = self .add_padding (payload )
947
+ decoded = base64 .b85decode (encoded )
948
+ self .assertEqual (payload , decoded )
949
+
802
950
def test_decode_nonascii_str (self ):
803
951
decode_funcs = (base64 .b64decode ,
804
952
base64 .standard_b64decode ,
0 commit comments