PageRenderTime 45ms CodeModel.GetById 8ms app.highlight 31ms RepoModel.GetById 1ms app.codeStats 0ms

/pypy/module/struct/test/test_struct.py

https://bitbucket.org/pypy/pypy/
Python | 490 lines | 479 code | 4 blank | 7 comment | 3 complexity | a90d566df9d7e4659bae5e4894fbd091 MD5 | raw file
  1"""
  2Tests for the struct module implemented at interp-level in pypy/module/struct.
  3"""
  4
  5from rpython.rlib.rstruct.nativefmttable import native_is_bigendian
  6
  7
  8class AppTestStruct(object):
  9    spaceconfig = dict(usemodules=['struct', 'array'])
 10
 11    def setup_class(cls):
 12        """
 13        Create a space with the struct module and import it for use by the
 14        tests.
 15        """
 16        cls.w_struct = cls.space.appexec([], """():
 17            import struct
 18            return struct
 19        """)
 20        cls.w_native_is_bigendian = cls.space.wrap(native_is_bigendian)
 21
 22    def test_error(self):
 23        """
 24        struct.error should be an exception class.
 25        """
 26        assert issubclass(self.struct.error, Exception)
 27        assert self.struct.error.__mro__ == (self.struct.error, Exception,
 28                                             BaseException, object)
 29        assert self.struct.error.__name__ == "error"
 30        assert self.struct.error.__module__ == "struct"
 31
 32    def test_calcsize_standard(self):
 33        """
 34        Check the standard size of the various format characters.
 35        """
 36        calcsize = self.struct.calcsize
 37        assert calcsize('=') == 0
 38        assert calcsize('<x') == 1
 39        assert calcsize('>c') == 1
 40        assert calcsize('!b') == 1
 41        assert calcsize('=B') == 1
 42        assert calcsize('<h') == 2
 43        assert calcsize('>H') == 2
 44        assert calcsize('!i') == 4
 45        assert calcsize('=I') == 4
 46        assert calcsize('<l') == 4
 47        assert calcsize('>L') == 4
 48        assert calcsize('!q') == 8
 49        assert calcsize('=Q') == 8
 50        assert calcsize('<f') == 4
 51        assert calcsize('>d') == 8
 52        assert calcsize('!13s') == 13
 53        assert calcsize('=500p') == 500
 54        # test with some repetitions and multiple format characters
 55        assert calcsize('=bQ3i') == 1 + 8 + 3*4
 56
 57    def test_index(self):
 58        class X(object):
 59            def __index__(self):
 60                return 3
 61        assert self.struct.unpack("i", self.struct.pack("i", X()))[0] == 3
 62
 63    def test_deprecation_warning(self):
 64        import warnings
 65        for code in 'b', 'B', 'h', 'H', 'i', 'I', 'l', 'L', 'q', 'Q':
 66            for val in [3., 3j]:
 67                with warnings.catch_warnings(record=True) as w:
 68                    warnings.simplefilter("always")
 69                    if type(val) is float:
 70                        self.struct.pack(code, val)
 71                    else:
 72                        raises(TypeError, self.struct.pack, code, val)
 73                assert len(w) == 1
 74                if type(val) is float:
 75                    assert str(w[0].message) == (
 76                        "integer argument expected, got float")
 77                else:
 78                    assert str(w[0].message) == (
 79                        "integer argument expected, got non-integer"
 80                        " (implicit conversion using __int__ is deprecated)")
 81                assert w[0].category is DeprecationWarning
 82
 83    def test_pack_standard_little(self):
 84        """
 85        Check packing with the '<' format specifier.
 86        """
 87        pack = self.struct.pack
 88        assert pack("<i", 0x41424344) == 'DCBA'
 89        assert pack("<i", -3) == '\xfd\xff\xff\xff'
 90        assert pack("<i", -2147483648) == '\x00\x00\x00\x80'
 91        assert pack("<I", 0x81424344) == 'DCB\x81'
 92        assert pack("<q", 0x4142434445464748) == 'HGFEDCBA'
 93        assert pack("<q", -0x41B2B3B4B5B6B7B8) == 'HHIJKLM\xbe'
 94        assert pack("<Q", 0x8142434445464748) == 'HGFEDCB\x81'
 95
 96    def test_unpack_standard_little(self):
 97        """
 98        Check unpacking with the '<' format specifier.
 99        """
100        unpack = self.struct.unpack
101        assert unpack("<i", 'DCBA') == (0x41424344,)
102        assert unpack("<i", '\xfd\xff\xff\xff') == (-3,)
103        assert unpack("<i", '\x00\x00\x00\x80') == (-2147483648,)
104        assert unpack("<I", 'DCB\x81') == (0x81424344,)
105        assert unpack("<q", 'HGFEDCBA') == (0x4142434445464748,)
106        assert unpack("<q", 'HHIJKLM\xbe') == (-0x41B2B3B4B5B6B7B8,)
107        assert unpack("<Q", 'HGFEDCB\x81') == (0x8142434445464748,)
108
109    def test_pack_standard_big(self):
110        """
111        Check packing with the '>' format specifier.
112        """
113        pack = self.struct.pack
114        assert pack(">i", 0x41424344) == 'ABCD'
115        assert pack(">i", -3) == '\xff\xff\xff\xfd'
116        assert pack(">i", -2147483648) == '\x80\x00\x00\x00'
117        assert pack(">I", 0x81424344) == '\x81BCD'
118        assert pack(">q", 0x4142434445464748) == 'ABCDEFGH'
119        assert pack(">q", -0x41B2B3B4B5B6B7B8) == '\xbeMLKJIHH'
120        assert pack(">Q", 0x8142434445464748) == '\x81BCDEFGH'
121
122    def test_unpack_standard_big(self):
123        """
124        Check unpacking with the '>' format specifier.
125        """
126        unpack = self.struct.unpack
127        assert unpack(">i", 'ABCD') == (0x41424344,)
128        assert unpack(">i", '\xff\xff\xff\xfd') == (-3,)
129        assert unpack(">i", '\x80\x00\x00\x00') == (-2147483648,)
130        assert unpack(">I", '\x81BCD') == (0x81424344,)
131        assert unpack(">q", 'ABCDEFGH') == (0x4142434445464748,)
132        assert unpack(">q", '\xbeMLKJIHH') == (-0x41B2B3B4B5B6B7B8,)
133        assert unpack(">Q", '\x81BCDEFGH') == (0x8142434445464748,)
134
135    def test_calcsize_native(self):
136        """
137        Check that the size of the various format characters is reasonable.
138        """
139        calcsize = self.struct.calcsize
140        assert calcsize('') == 0
141        assert calcsize('x') == 1
142        assert calcsize('c') == 1
143        assert calcsize('b') == 1
144        assert calcsize('B') == 1
145        assert (2 <= calcsize('h') == calcsize('H')
146                  <  calcsize('i') == calcsize('I')
147                  <= calcsize('l') == calcsize('L')
148                  <= calcsize('q') == calcsize('Q'))
149        assert 4 <= calcsize('f') <= 8 <= calcsize('d')
150        assert calcsize('13s') == 13
151        assert calcsize('500p') == 500
152        assert 4 <= calcsize('P') <= 8
153        # test with some repetitions and multiple format characters
154        assert 4 + 8 + 3*4 <= calcsize('bQ3i') <= 8 + 8 + 3*8
155        # test alignment
156        assert calcsize('bi') == calcsize('ii') == 2 * calcsize('i')
157        assert calcsize('bbi') == calcsize('ii') == 2 * calcsize('i')
158        assert calcsize('hi') == calcsize('ii') == 2 * calcsize('i')
159        # CPython adds no padding at the end, unlike a C compiler
160        assert calcsize('ib') == calcsize('i') + calcsize('b')
161        assert calcsize('ibb') == calcsize('i') + 2 * calcsize('b')
162        assert calcsize('ih') == calcsize('i') + calcsize('h')
163
164    def test_pack_native(self):
165        """
166        Check packing with the native format.
167        """
168        calcsize = self.struct.calcsize
169        pack = self.struct.pack
170        sizeofi = calcsize("i")
171        res = pack("bi", -2, 5)
172        assert len(res) == 2 * sizeofi
173        assert res[0] == '\xfe'
174        assert res[1:sizeofi] == '\x00' * (sizeofi-1)    # padding
175        if self.native_is_bigendian:
176            assert res[sizeofi:] == '\x00' * (sizeofi-1) + '\x05'
177        else:
178            assert res[sizeofi:] == '\x05' + '\x00' * (sizeofi-1)
179        assert pack("q", -1) == '\xff' * calcsize("q")
180
181    def test_unpack_native(self):
182        """
183        Check unpacking with the native format.
184        """
185        calcsize = self.struct.calcsize
186        pack = self.struct.pack
187        unpack = self.struct.unpack
188        assert unpack("bi", pack("bi", -2, 5)) == (-2, 5)
189        assert unpack("q", '\xff' * calcsize("q")) == (-1,)
190
191    def test_string_format(self):
192        """
193        Check the 's' format character.
194        """
195        pack = self.struct.pack
196        unpack = self.struct.unpack
197        assert pack("7s", "hello") == "hello\x00\x00"
198        assert pack("5s", "world") == "world"
199        assert pack("3s", "spam") == "spa"
200        assert pack("0s", "foo") == ""
201        assert unpack("7s", "hello\x00\x00") == ("hello\x00\x00",)
202        assert unpack("5s3s", "worldspa") == ("world", "spa")
203        assert unpack("0s", "") == ("",)
204
205    def test_pascal_format(self):
206        """
207        Check the 'p' format character.
208        """
209        pack = self.struct.pack
210        unpack = self.struct.unpack
211        longstring = str(range(70))     # this has 270 chars
212        longpacked300 = "\xff" + longstring + "\x00" * (299-len(longstring))
213        assert pack("8p", "hello") == "\x05hello\x00\x00"
214        assert pack("6p", "world") == "\x05world"
215        assert pack("4p", "spam") == "\x03spa"
216        assert pack("1p", "foo") == "\x00"
217        assert pack("10p", longstring) == "\x09" + longstring[:9]
218        assert pack("300p", longstring) == longpacked300
219        assert unpack("8p", "\x05helloxx") == ("hello",)
220        assert unpack("5p", "\x80abcd") == ("abcd",)
221        assert unpack("1p", "\x03") == ("",)
222        assert unpack("300p", longpacked300) == (longstring[:255],)
223
224    def test_char_format(self):
225        """
226        Check the 'c' format character.
227        """
228        pack = self.struct.pack
229        unpack = self.struct.unpack
230        assert pack("c", "?") == "?"
231        assert pack("5c", "a", "\xc0", "\x00", "\n", "-") == "a\xc0\x00\n-"
232        assert unpack("c", "?") == ("?",)
233        assert unpack("5c", "a\xc0\x00\n-") == ("a", "\xc0", "\x00", "\n", "-")
234
235    def test_pad_format(self):
236        """
237        Check the 'x' format character.
238        """
239        pack = self.struct.pack
240        unpack = self.struct.unpack
241        assert pack("x") == "\x00"
242        assert pack("5x") == "\x00" * 5
243        assert unpack("x", "?") == ()
244        assert unpack("5x", "hello") == ()
245
246    def test_native_floats(self):
247        """
248        Check the 'd' and 'f' format characters on native packing.
249        """
250        calcsize = self.struct.calcsize
251        pack = self.struct.pack
252        unpack = self.struct.unpack
253        data = pack("d", 12.34)
254        assert len(data) == calcsize("d")
255        assert unpack("d", data) == (12.34,)     # no precision lost
256        data = pack("f", 12.34)
257        assert len(data) == calcsize("f")
258        res, = unpack("f", data)
259        assert res != 12.34                      # precision lost
260        assert abs(res - 12.34) < 1E-6
261
262    def test_standard_floats(self):
263        """
264        Check the 'd' and 'f' format characters on standard packing.
265        """
266        pack = self.struct.pack
267        unpack = self.struct.unpack
268        assert pack("!d", 12.5) == '@)\x00\x00\x00\x00\x00\x00'
269        assert pack("<d", -12.5) == '\x00\x00\x00\x00\x00\x00)\xc0'
270        assert unpack("!d", '\xc0)\x00\x00\x00\x00\x00\x00') == (-12.5,)
271        assert unpack("<d", '\x00\x00\x00\x00\x00\x00)@') == (12.5,)
272        assert pack("!f", -12.5) == '\xc1H\x00\x00'
273        assert pack("<f", 12.5) == '\x00\x00HA'
274        assert unpack("!f", 'AH\x00\x00') == (12.5,)
275        assert unpack("<f", '\x00\x00H\xc1') == (-12.5,)
276        raises(OverflowError, pack, "<f", 10e100)
277
278    def test_bool(self):
279        pack = self.struct.pack
280        assert pack("!?", True) == '\x01'
281        assert pack(">?", True) == '\x01'
282        assert pack("!?", False) == '\x00'
283        assert pack(">?", False) == '\x00'
284        assert pack("@?", True) == '\x01'
285        assert pack("@?", False) == '\x00'
286
287    def test_transitiveness(self):
288        c = 'a'
289        b = 1
290        h = 255
291        i = 65535
292        l = 65536
293        f = 3.1415
294        d = 3.1415
295        t = True
296
297        for prefix in ('', '@', '<', '>', '=', '!'):
298            for format in ('xcbhilfd?', 'xcBHILfd?'):
299                format = prefix + format
300                s = self.struct.pack(format, c, b, h, i, l, f, d, t)
301                cp, bp, hp, ip, lp, fp, dp, tp = self.struct.unpack(format, s)
302                assert cp == c
303                assert bp == b
304                assert hp == h
305                assert ip == i
306                assert lp == l
307                assert int(100 * fp) == int(100 * f)
308                assert int(100 * dp) == int(100 * d)
309                assert tp == t
310
311    def test_struct_error(self):
312        """
313        Check the various ways to get a struct.error.  Note that CPython
314        and PyPy might disagree on the specific exception raised in a
315        specific situation, e.g. struct.error/TypeError/OverflowError.
316        """
317        import sys
318        calcsize = self.struct.calcsize
319        pack = self.struct.pack
320        unpack = self.struct.unpack
321        error = self.struct.error
322        try:
323            calcsize("12")              # incomplete struct format
324        except error:                   # (but ignored on CPython)
325            pass
326        raises(error, calcsize, "[")    # bad char in struct format
327        raises(error, calcsize, "!P")   # bad char in struct format
328        raises(error, pack, "ii", 15)   # struct format requires more arguments
329        raises(error, pack, "i", 3, 4)  # too many arguments for struct format
330        raises(error, unpack, "ii", "?")# unpack str size too short for format
331        raises(error, unpack, "b", "??")# unpack str size too long for format
332        raises(error, pack, "c", "foo") # expected a string of length 1
333        try:
334            pack("0p")                  # bad '0p' in struct format
335        except error:                   # (but ignored on CPython)
336            pass
337        if '__pypy__' in sys.builtin_module_names:
338            raises(error, unpack, "0p", "")   # segfaults on CPython 2.5.2!
339        raises(error, pack, "b", 150)   # argument out of range
340        # XXX the accepted ranges still differs between PyPy and CPython
341        exc = raises(error, pack, ">d", 'abc')
342        assert str(exc.value) == "required argument is not a float"
343        exc = raises(error, pack, ">l", 'abc')
344        assert str(exc.value) == "cannot convert argument to integer"
345        exc = raises(error, pack, ">H", 'abc')
346        assert str(exc.value) == "cannot convert argument to integer"
347
348    def test_overflow_error(self):
349        """
350        Check OverflowError cases.
351        """
352        import sys
353        calcsize = self.struct.calcsize
354        someerror = (OverflowError, self.struct.error)
355        raises(someerror, calcsize, "%dc" % (sys.maxint+1,))
356        raises(someerror, calcsize, "999999999999999999999999999c")
357        raises(someerror, calcsize, "%di" % (sys.maxint,))
358        raises(someerror, calcsize, "%dcc" % (sys.maxint,))
359        raises(someerror, calcsize, "c%dc" % (sys.maxint,))
360        raises(someerror, calcsize, "%dci" % (sys.maxint,))
361
362    def test_unicode(self):
363        """
364        A PyPy extension: accepts the 'u' format character in native mode,
365        just like the array module does.  (This is actually used in the
366        implementation of our interp-level array module.)
367        """
368        import sys
369        if '__pypy__' not in sys.builtin_module_names:
370            skip("PyPy extension")
371        data = self.struct.pack("uuu", u'X', u'Y', u'Z')
372        assert data == str(buffer(u'XYZ'))
373        assert self.struct.unpack("uuu", data) == (u'X', u'Y', u'Z')
374
375    def test_unpack_buffer(self):
376        """
377        Buffer objects can be passed to struct.unpack().
378        """
379        b = buffer(self.struct.pack("ii", 62, 12))
380        assert self.struct.unpack("ii", b) == (62, 12)
381        raises(self.struct.error, self.struct.unpack, "i", b)
382
383    def test_pack_unpack_buffer(self):
384        import array
385        b = array.array('c', '\x00' * 19)
386        sz = self.struct.calcsize("ii")
387        for offset in [2, -17]:
388            self.struct.pack_into("ii", b, offset, 17, 42)
389            assert str(buffer(b)) == ('\x00' * 2 +
390                                      self.struct.pack("ii", 17, 42) +
391                                      '\x00' * (19-sz-2))
392        exc = raises(TypeError, self.struct.pack_into, "ii", buffer(b), 0, 17, 42)
393        assert str(exc.value) == "must be read-write buffer, not buffer"
394        exc = raises(TypeError, self.struct.pack_into, "ii", 'test', 0, 17, 42)
395        assert str(exc.value) == "must be read-write buffer, not str"
396        exc = raises(self.struct.error, self.struct.pack_into, "ii", b[0:1], 0, 17, 42)
397        assert str(exc.value) == "pack_into requires a buffer of at least 8 bytes"
398
399        assert self.struct.unpack_from("ii", b, 2) == (17, 42)
400        assert self.struct.unpack_from("ii", b, -17) == (17, 42)
401        assert self.struct.unpack_from("ii", buffer(b, 2)) == (17, 42)
402        assert self.struct.unpack_from("ii", buffer(b), 2) == (17, 42)
403        assert self.struct.unpack_from("ii", memoryview(buffer(b)), 2) == (17, 42)
404        exc = raises(TypeError, self.struct.unpack_from, "ii", 123)
405        assert 'must be string or buffer, not int' in str(exc.value)
406        exc = raises(self.struct.error, self.struct.unpack_from, "ii", None)
407        assert str(exc.value) == "unpack_from requires a buffer argument"
408        exc = raises(self.struct.error, self.struct.unpack_from, "ii", '')
409        assert str(exc.value) == "unpack_from requires a buffer of at least 8 bytes"
410        exc = raises(self.struct.error, self.struct.unpack_from, "ii", memoryview(''))
411        assert str(exc.value) == "unpack_from requires a buffer of at least 8 bytes"
412
413    def test___float__(self):
414        class MyFloat(object):
415            def __init__(self, x):
416                self.x = x
417            def __float__(self):
418                return self.x
419
420        obj = MyFloat(42.3)
421        data = self.struct.pack('d', obj)
422        obj2, = self.struct.unpack('d', data)
423        assert type(obj2) is float
424        assert obj2 == 42.3
425
426    def test_struct_object(self):
427        s = self.struct.Struct('i')
428        assert s.unpack(s.pack(42)) == (42,)
429        assert s.unpack_from(memoryview(s.pack(42))) == (42,)
430
431    def test_overflow(self):
432        raises(self.struct.error, self.struct.pack, 'i', 1<<65)
433
434
435class AppTestStructBuffer(object):
436    spaceconfig = dict(usemodules=['struct', '__pypy__'])
437
438    def setup_class(cls):
439        cls.w_struct = cls.space.appexec([], """():
440            import struct
441            return struct
442        """)
443        cls.w_bytebuffer = cls.space.appexec([], """():
444            import __pypy__
445            return __pypy__.bytebuffer
446        """)
447
448    def test_pack_into(self):
449        b = self.bytebuffer(19)
450        sz = self.struct.calcsize("ii")
451        self.struct.pack_into("ii", b, 2, 17, 42)
452        assert b[:] == ('\x00' * 2 +
453                        self.struct.pack("ii", 17, 42) +
454                        '\x00' * (19-sz-2))
455        m = memoryview(b)
456        self.struct.pack_into("ii", m, 2, 17, 42)
457
458    def test_unpack_from(self):
459        b = self.bytebuffer(19)
460        sz = self.struct.calcsize("ii")
461        b[2:2+sz] = self.struct.pack("ii", 17, 42)
462        assert self.struct.unpack_from("ii", b, 2) == (17, 42)
463        b[:sz] = self.struct.pack("ii", 18, 43)
464        assert self.struct.unpack_from("ii", b) == (18, 43)
465
466
467class AppTestFastPath(object):
468    spaceconfig = dict(usemodules=['struct', '__pypy__'])
469
470    def setup_class(cls):
471        from rpython.rlib.rstruct import standardfmttable
472        standardfmttable.ALLOW_SLOWPATH = False
473        #
474        cls.w_struct = cls.space.appexec([], """():
475            import struct
476            return struct
477        """)
478        cls.w_bytebuffer = cls.space.appexec([], """():
479            import __pypy__
480            return __pypy__.bytebuffer
481        """)
482
483    def teardown_class(cls):
484        from rpython.rlib.rstruct import standardfmttable
485        standardfmttable.ALLOW_SLOWPATH = True
486
487    def test_unpack_from(self):
488        buf = self.struct.pack("iii", 0, 42, 43)
489        offset = self.struct.calcsize("i")
490        assert self.struct.unpack_from("ii", buf, offset) == (42, 43)