test_binascii.py 8.78 KB
Newer Older
1 2
"""Test the binascii C module."""

3
from test import support
4
import unittest
5
import binascii
6
import array
7

8 9 10 11 12 13 14 15
# Note: "*_hex" functions are aliases for "(un)hexlify"
b2a_functions = ['b2a_base64', 'b2a_hex', 'b2a_hqx', 'b2a_qp', 'b2a_uu',
                 'hexlify', 'rlecode_hqx']
a2b_functions = ['a2b_base64', 'a2b_hex', 'a2b_hqx', 'a2b_qp', 'a2b_uu',
                 'unhexlify', 'rledecode_hqx']
all_functions = a2b_functions + b2a_functions + ['crc32', 'crc_hqx']


16 17
class BinASCIITest(unittest.TestCase):

18
    type2test = bytes
19
    # Create binary test data
20
    rawdata = b"The quick brown fox jumps over the lazy dog.\r\n"
21
    # Be slow so we don't depend on other modules
22 23 24 25 26
    rawdata += bytes(range(256))
    rawdata += b"\r\nHello world.\n"

    def setUp(self):
        self.data = self.type2test(self.rawdata)
27 28 29

    def test_exceptions(self):
        # Check module exceptions
30 31
        self.assertTrue(issubclass(binascii.Error, Exception))
        self.assertTrue(issubclass(binascii.Incomplete, Exception))
32 33 34

    def test_functions(self):
        # Check presence of all functions
35
        for name in all_functions:
36
            self.assertTrue(hasattr(getattr(binascii, name), '__call__'))
37 38
            self.assertRaises(TypeError, getattr(binascii, name))

39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57
    def test_returned_value(self):
        # Limit to the minimum of all limits (b2a_uu)
        MAX_ALL = 45
        raw = self.rawdata[:MAX_ALL]
        for fa, fb in zip(a2b_functions, b2a_functions):
            a2b = getattr(binascii, fa)
            b2a = getattr(binascii, fb)
            try:
                a = b2a(self.type2test(raw))
                res = a2b(self.type2test(a))
            except Exception as err:
                self.fail("{}/{} conversion raises {!r}".format(fb, fa, err))
            if fb == 'b2a_hqx':
                # b2a_hqx returns a tuple
                res, _ = res
            self.assertEqual(res, raw, "{}/{} conversion: "
                             "{!r} != {!r}".format(fb, fa, res, raw))
            self.assertIsInstance(res, bytes)
            self.assertIsInstance(a, bytes)
58
            self.assertLess(max(a), 128)
59 60 61
        self.assertIsInstance(binascii.crc_hqx(raw, 0), int)
        self.assertIsInstance(binascii.crc32(raw), int)

62 63 64 65
    def test_base64valid(self):
        # Test base64 with valid data
        MAX_BASE64 = 57
        lines = []
66 67
        for i in range(0, len(self.rawdata), MAX_BASE64):
            b = self.type2test(self.rawdata[i:i+MAX_BASE64])
68 69
            a = binascii.b2a_base64(b)
            lines.append(a)
70
        res = bytes()
71
        for line in lines:
72 73
            a = self.type2test(line)
            b = binascii.a2b_base64(a)
74
            res += b
75
        self.assertEqual(res, self.rawdata)
76 77 78 79 80 81 82

    def test_base64invalid(self):
        # Test base64 with random invalid characters sprinkled throughout
        # (This requires a new version of binascii.)
        MAX_BASE64 = 57
        lines = []
        for i in range(0, len(self.data), MAX_BASE64):
83
            b = self.type2test(self.rawdata[i:i+MAX_BASE64])
84 85 86
            a = binascii.b2a_base64(b)
            lines.append(a)

87
        fillers = bytearray()
88
        valid = b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789+/"
89
        for i in range(256):
90 91
            if i not in valid:
                fillers.append(i)
92 93 94
        def addnoise(line):
            noise = fillers
            ratio = len(line) // len(noise)
95
            res = bytearray()
96 97 98 99 100
            while line and noise:
                if len(line) // len(noise) > ratio:
                    c, line = line[0], line[1:]
                else:
                    c, noise = noise[0], noise[1:]
101
                res.append(c)
102
            return res + noise + line
103
        res = bytearray()
104
        for line in map(addnoise, lines):
105 106
            a = self.type2test(line)
            b = binascii.a2b_base64(a)
107
            res += b
108
        self.assertEqual(res, self.rawdata)
109 110 111

        # Test base64 with just invalid characters, which should return
        # empty strings. TBD: shouldn't it raise an exception instead ?
112
        self.assertEqual(binascii.a2b_base64(self.type2test(fillers)), b'')
113 114 115 116 117

    def test_uu(self):
        MAX_UU = 45
        lines = []
        for i in range(0, len(self.data), MAX_UU):
118
            b = self.type2test(self.rawdata[i:i+MAX_UU])
119 120
            a = binascii.b2a_uu(b)
            lines.append(a)
121
        res = bytes()
122
        for line in lines:
123 124
            a = self.type2test(line)
            b = binascii.a2b_uu(a)
125
            res += b
126
        self.assertEqual(res, self.rawdata)
127

128 129 130 131 132
        self.assertEqual(binascii.a2b_uu(b"\x7f"), b"\x00"*31)
        self.assertEqual(binascii.a2b_uu(b"\x80"), b"\x00"*32)
        self.assertEqual(binascii.a2b_uu(b"\xff"), b"\x00"*31)
        self.assertRaises(binascii.Error, binascii.a2b_uu, b"\xff\x00")
        self.assertRaises(binascii.Error, binascii.a2b_uu, b"!!!!")
Tim Peters's avatar
Tim Peters committed
133

134
        self.assertRaises(binascii.Error, binascii.b2a_uu, 46*b"!")
135

136 137 138
        # Issue #7701 (crash on a pydebug build)
        self.assertEqual(binascii.b2a_uu(b'x'), b'!>   \n')

139
    def test_crc32(self):
140 141
        crc = binascii.crc32(self.type2test(b"Test the CRC-32 of"))
        crc = binascii.crc32(self.type2test(b" this string."), crc)
142 143 144 145
        self.assertEqual(crc, 1571220330)

        self.assertRaises(TypeError, binascii.crc32)

146 147 148 149 150 151 152 153 154
    def test_hqx(self):
        # Perform binhex4 style RLE-compression
        # Then calculate the hexbin4 binary-to-ASCII translation
        rle = binascii.rlecode_hqx(self.data)
        a = binascii.b2a_hqx(self.type2test(rle))
        b, _ = binascii.a2b_hqx(self.type2test(a))
        res = binascii.rledecode_hqx(b)

        self.assertEqual(res, self.rawdata)
155 156 157

    def test_hex(self):
        # test hexlification
158
        s = b'{s\005\000\000\000worldi\002\000\000\000s\005\000\000\000helloi\001\000\000\0000'
159 160
        t = binascii.b2a_hex(self.type2test(s))
        u = binascii.a2b_hex(self.type2test(t))
161
        self.assertEqual(s, u)
Guido van Rossum's avatar
Guido van Rossum committed
162 163
        self.assertRaises(binascii.Error, binascii.a2b_hex, t[:-1])
        self.assertRaises(binascii.Error, binascii.a2b_hex, t[:-1] + b'q')
164

165
        self.assertEqual(binascii.hexlify(b'a'), b'61')
166 167 168 169

    def test_qp(self):
        # A test for SF bug 534347 (segfaults without the proper fix)
        try:
170
            binascii.a2b_qp(b"", **{1:1})
171 172
        except TypeError:
            pass
173
        else:
174
            self.fail("binascii.a2b_qp(**{1:1}) didn't raise TypeError")
175 176 177
        self.assertEqual(binascii.a2b_qp(b"= "), b"= ")
        self.assertEqual(binascii.a2b_qp(b"=="), b"=")
        self.assertEqual(binascii.a2b_qp(b"=AX"), b"=AX")
178
        self.assertRaises(TypeError, binascii.b2a_qp, foo="bar")
179
        self.assertEqual(binascii.a2b_qp(b"=00\r\n=00"), b"\x00\r\n\x00")
180
        self.assertEqual(
181
            binascii.b2a_qp(b"\xff\r\n\xff\n\xff"),
182
            b"=FF\r\n=FF\r\n=FF")
183
        self.assertEqual(
184
            binascii.b2a_qp(b"0"*75+b"\xff\r\n\xff\r\n\xff"),
185
            b"0"*75+b"=\r\n=FF\r\n=FF\r\n=FF")
186

187 188 189 190 191
        self.assertEqual(binascii.b2a_qp(b'\0\n'), b'=00\n')
        self.assertEqual(binascii.b2a_qp(b'\0\n', quotetabs=True), b'=00\n')
        self.assertEqual(binascii.b2a_qp(b'foo\tbar\t\n'), b'foo\tbar=09\n')
        self.assertEqual(binascii.b2a_qp(b'foo\tbar\t\n', quotetabs=True),
                         b'foo=09bar=09\n')
192

193 194 195
        self.assertEqual(binascii.b2a_qp(b'.'), b'=2E')
        self.assertEqual(binascii.b2a_qp(b'.\n'), b'=2E\n')
        self.assertEqual(binascii.b2a_qp(b'a.\n'), b'a.\n')
196

197 198
    def test_empty_string(self):
        # A test for SF bug #1022953.  Make sure SystemError is not raised.
199 200 201 202 203 204 205
        empty = self.type2test(b'')
        for func in all_functions:
            if func == 'crc_hqx':
                # crc_hqx needs 2 arguments
                binascii.crc_hqx(empty, 0)
                continue
            f = getattr(binascii, func)
206
            try:
207 208 209
                f(empty)
            except Exception as err:
                self.fail("{}({!r}) raises {!r}".format(func, empty, err))
210

211 212 213 214 215 216 217 218 219
    def test_unicode_strings(self):
        # Unicode strings are not accepted.
        for func in all_functions:
            try:
                self.assertRaises(TypeError, getattr(binascii, func), "test")
            except Exception as err:
                self.fail('{}("test") raises {!r}'.format(func, err))
        # crc_hqx needs 2 arguments
        self.assertRaises(TypeError, binascii.crc_hqx, "test", 0)
220

221 222 223 224 225 226

class ArrayBinASCIITest(BinASCIITest):
    def type2test(self, s):
        return array.array('B', list(s))


227 228 229 230
class BytearrayBinASCIITest(BinASCIITest):
    type2test = bytearray


231 232 233 234
class MemoryviewBinASCIITest(BinASCIITest):
    type2test = memoryview


235
def test_main():
236 237
    support.run_unittest(BinASCIITest,
                         ArrayBinASCIITest,
238
                         BytearrayBinASCIITest,
239
                         MemoryviewBinASCIITest)
240 241 242

if __name__ == "__main__":
    test_main()