test_marshal.py 8.47 KB
Newer Older
1
#!/usr/bin/env python3
2

3
from test import support
4 5
import marshal
import sys
6 7
import unittest
import os
8

9 10 11 12 13
class HelperMixin:
    def helper(self, sample, *extra):
        new = marshal.loads(marshal.dumps(sample, *extra))
        self.assertEqual(sample, new)
        try:
14
            with open(support.TESTFN, "wb") as f:
15
                marshal.dump(sample, f, *extra)
16
            with open(support.TESTFN, "rb") as f:
17 18 19
                new = marshal.load(f)
            self.assertEqual(sample, new)
        finally:
20
            support.unlink(support.TESTFN)
21 22

class IntTestCase(unittest.TestCase, HelperMixin):
23 24
    def test_ints(self):
        # Test the full range of Python ints.
25
        n = sys.maxsize
26 27
        while n:
            for expected in (-n, n):
28
                self.helper(expected)
29
            n = n >> 1
30

31 32 33 34 35
    def test_int64(self):
        # Simulate int marshaling on a 64-bit box.  This is most interesting if
        # we're running the test on a 32-bit box, of course.

        def to_little_endian_string(value, nbytes):
36
            b = bytearray()
37
            for i in range(nbytes):
38
                b.append(value & 0xff)
39
                value >>= 8
40
            return b
41

42
        maxint64 = (1 << 63) - 1
43 44 45 46
        minint64 = -maxint64-1

        for base in maxint64, minint64, -maxint64, -(minint64 >> 1):
            while base:
47
                s = b'I' + to_little_endian_string(base, 8)
48 49 50 51 52 53 54 55 56
                got = marshal.loads(s)
                self.assertEqual(base, got)
                if base == -1:  # a fixed-point for shifting right 1
                    base = 0
                else:
                    base >>= 1

    def test_bool(self):
        for b in (True, False):
57 58 59
            self.helper(b)

class FloatTestCase(unittest.TestCase, HelperMixin):
60 61 62
    def test_floats(self):
        # Test a few floats
        small = 1e-25
63
        n = sys.maxsize * 3.7e250
64 65
        while n > small:
            for expected in (-n, n):
66
                self.helper(float(expected))
67 68 69
            n /= 123.4567

        f = 0.0
Michael W. Hudson's avatar
Michael W. Hudson committed
70
        s = marshal.dumps(f, 2)
71
        got = marshal.loads(s)
72
        self.assertEqual(f, got)
Michael W. Hudson's avatar
Michael W. Hudson committed
73 74
        # and with version <= 1 (floats marshalled differently then)
        s = marshal.dumps(f, 1)
Tim Peters's avatar
Tim Peters committed
75 76
        got = marshal.loads(s)
        self.assertEqual(f, got)
77

78
        n = sys.maxsize * 3.7e-250
79 80 81
        while n < small:
            for expected in (-n, n):
                f = float(expected)
82 83
                self.helper(f)
                self.helper(f, 1)
84 85
            n *= 123.4567

86
class StringTestCase(unittest.TestCase, HelperMixin):
87
    def test_unicode(self):
88 89
        for s in ["", "Andr\xe8 Previn", "abc", " "*10000]:
            self.helper(marshal.loads(marshal.dumps(s)))
90 91

    def test_string(self):
92 93
        for s in ["", "Andr\xe8 Previn", "abc", " "*10000]:
            self.helper(s)
94

95
    def test_bytes(self):
96
        for s in [b"", b"Andr\xe8 Previn", b"abc", b" "*10000]:
97
            self.helper(s)
Tim Peters's avatar
Tim Peters committed
98

99 100 101 102 103 104 105
class ExceptionTestCase(unittest.TestCase):
    def test_exceptions(self):
        new = marshal.loads(marshal.dumps(StopIteration))
        self.assertEqual(StopIteration, new)

class CodeTestCase(unittest.TestCase):
    def test_code(self):
106
        co = ExceptionTestCase.test_exceptions.__code__
107 108 109
        new = marshal.loads(marshal.dumps(co))
        self.assertEqual(co, new)

110 111 112 113 114 115
    def test_many_codeobjects(self):
        # Issue2957: bad recursion count on code objects
        count = 5000    # more than MAX_MARSHAL_STACK_DEPTH
        codes = (ExceptionTestCase.test_exceptions.__code__,) * count
        marshal.loads(marshal.dumps(codes))

116
class ContainerTestCase(unittest.TestCase, HelperMixin):
117 118 119
    d = {'astring': 'foo@bar.baz.spam',
         'afloat': 7283.43,
         'anint': 2**20,
120
         'ashortlong': 2,
121 122 123
         'alist': ['.zyx.41'],
         'atuple': ('.zyx.41',)*10,
         'aboolean': False,
124
         'aunicode': "Andr\xe8 Previn"
125
         }
126

127
    def test_dict(self):
128
        self.helper(self.d)
Tim Peters's avatar
Tim Peters committed
129

130
    def test_list(self):
131
        self.helper(list(self.d.items()))
132 133

    def test_tuple(self):
134
        self.helper(tuple(self.d.keys()))
135 136 137

    def test_sets(self):
        for constructor in (set, frozenset):
138
            self.helper(constructor(self.d.keys()))
Tim Peters's avatar
Tim Peters committed
139

140 141 142 143 144
class BugsTestCase(unittest.TestCase):
    def test_bug_5888452(self):
        # Simple-minded check for SF 588452: Debug build crashes
        marshal.dumps([128] * 1000)

145 146 147
    def test_patch_873224(self):
        self.assertRaises(Exception, marshal.loads, '0')
        self.assertRaises(Exception, marshal.loads, 'f')
148
        self.assertRaises(Exception, marshal.loads, marshal.dumps(2**65)[:-1])
149

150 151
    def test_version_argument(self):
        # Python 2.4.0 crashes for any call to marshal.dumps(x, y)
152 153
        self.assertEqual(marshal.loads(marshal.dumps(5, 0)), 5)
        self.assertEqual(marshal.loads(marshal.dumps(5, 1)), 5)
154

Michael W. Hudson's avatar
Michael W. Hudson committed
155 156 157 158 159 160 161 162 163
    def test_fuzz(self):
        # simple test that it's at least not *totally* trivial to
        # crash from bad marshal data
        for c in [chr(i) for i in range(256)]:
            try:
                marshal.loads(c)
            except Exception:
                pass

164 165 166 167 168 169 170 171
    def test_loads_recursion(self):
        s = 'c' + ('X' * 4*4) + '{' * 2**20
        self.assertRaises(ValueError, marshal.loads, s)

    def test_recursion_limit(self):
        # Create a deeply nested structure.
        head = last = []
        # The max stack depth should match the value in Python/marshal.c.
172 173 174 175
        if os.name == 'nt' and hasattr(sys, 'gettotalrefcount'):
            MAX_MARSHAL_STACK_DEPTH = 1500
        else:
            MAX_MARSHAL_STACK_DEPTH = 2000
176 177 178 179 180 181 182 183 184 185 186 187 188 189 190
        for i in range(MAX_MARSHAL_STACK_DEPTH - 2):
            last.append([0])
            last = last[-1]

        # Verify we don't blow out the stack with dumps/load.
        data = marshal.dumps(head)
        new_head = marshal.loads(data)
        # Don't use == to compare objects, it can exceed the recursion limit.
        self.assertEqual(len(new_head), len(head))
        self.assertEqual(len(new_head[0]), len(head[0]))
        self.assertEqual(len(new_head[-1]), len(head[-1]))

        last.append([0])
        self.assertRaises(ValueError, marshal.dumps, head)

191 192 193 194 195 196
    def test_exact_type_match(self):
        # Former bug:
        #   >>> class Int(int): pass
        #   >>> type(loads(dumps(Int())))
        #   <type 'int'>
        for typ in (int, float, complex, tuple, list, dict, set, frozenset):
197
            # Note: str subclasses are not tested because they get handled
198 199 200 201
            # by marshal's routines for objects supporting the buffer API.
            subtyp = type('subtyp', (typ,), {})
            self.assertRaises(ValueError, marshal.dumps, subtyp())

202 203 204 205 206 207 208
    # Issue #1792 introduced a change in how marshal increases the size of its
    # internal buffer; this test ensures that the new code is exercised.
    def test_large_marshal(self):
        size = int(1e6)
        testString = 'abc' * size
        marshal.dumps(testString)

209 210 211 212 213
    def test_invalid_longs(self):
        # Issue #7019: marshal.loads shouldn't produce unnormalized PyLongs
        invalid_string = b'l\x02\x00\x00\x00\x00\x00\x00\x00'
        self.assertRaises(ValueError, marshal.loads, invalid_string)

214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
    def test_multiple_dumps_and_loads(self):
        # Issue 12291: marshal.load() should be callable multiple times
        # with interleaved data written by non-marshal code
        # Adapted from a patch by Engelbert Gruber.
        data = (1, 'abc', b'def', 1.0, (2, 'a', ['b', b'c']))
        for interleaved in (b'', b'0123'):
            ilen = len(interleaved)
            positions = []
            try:
                with open(support.TESTFN, 'wb') as f:
                    for d in data:
                        marshal.dump(d, f)
                        if ilen:
                            f.write(interleaved)
                        positions.append(f.tell())
                with open(support.TESTFN, 'rb') as f:
                    for i, d in enumerate(data):
                        self.assertEqual(d, marshal.load(f))
                        if ilen:
                            f.read(ilen)
                        self.assertEqual(positions[i], f.tell())
            finally:
                support.unlink(support.TESTFN)

238

239
def test_main():
240
    support.run_unittest(IntTestCase,
241 242 243 244 245 246 247 248 249
                              FloatTestCase,
                              StringTestCase,
                              CodeTestCase,
                              ContainerTestCase,
                              ExceptionTestCase,
                              BugsTestCase)

if __name__ == "__main__":
    test_main()