blob: 5a63135a1a35f8364079d1721419daf15e7c4071 [file] [log] [blame]
Florent Xiclunadb4a3212010-03-20 22:21:02 +00001import os
Martin Blais2856e5f2006-05-26 12:03:27 +00002import array
Benjamin Petersond5299862008-06-11 01:31:28 +00003import unittest
4import struct
Florent Xiclunadb4a3212010-03-20 22:21:02 +00005import inspect
6from test.test_support import run_unittest, check_warnings, check_py3k_warnings
Benjamin Petersond5299862008-06-11 01:31:28 +00007
Tim Peters17e17d42001-06-13 22:45:27 +00008import sys
9ISBIGENDIAN = sys.byteorder == "big"
Mark Hammond69ed5242008-08-23 00:59:14 +000010IS32BIT = sys.maxsize == 0x7fffffff
Mark Dickinsonbb3895c2009-07-07 14:15:45 +000011
12integer_codes = 'b', 'B', 'h', 'H', 'i', 'I', 'l', 'L', 'q', 'Q'
13
Florent Xiclunadb4a3212010-03-20 22:21:02 +000014testmod_filename = os.path.splitext(__file__)[0] + '.py'
Mark Dickinsonbb3895c2009-07-07 14:15:45 +000015# Native 'q' packing isn't available on systems that don't have the C
16# long long type.
17try:
18 struct.pack('q', 5)
19except struct.error:
20 HAVE_LONG_LONG = False
21else:
22 HAVE_LONG_LONG = True
Tim Peters17e17d42001-06-13 22:45:27 +000023
24def string_reverse(s):
Tim Peters852eae12006-06-05 20:48:49 +000025 return "".join(reversed(s))
Tim Peters17e17d42001-06-13 22:45:27 +000026
27def bigendian_to_native(value):
28 if ISBIGENDIAN:
29 return value
30 else:
31 return string_reverse(value)
32
Benjamin Petersond5299862008-06-11 01:31:28 +000033class StructTest(unittest.TestCase):
Barry Warsaw07a0eec1996-12-12 23:34:06 +000034
Benjamin Petersond5299862008-06-11 01:31:28 +000035 def check_float_coerce(self, format, number):
Mark Dickinson154b7ad2010-03-07 16:24:45 +000036 # SF bug 1530559. struct.pack raises TypeError where it used
37 # to convert.
Florent Xiclunadb4a3212010-03-20 22:21:02 +000038 with check_warnings((".*integer argument expected, got float",
39 DeprecationWarning)) as w:
Mark Dickinson154b7ad2010-03-07 16:24:45 +000040 got = struct.pack(format, number)
Florent Xiclunadb4a3212010-03-20 22:21:02 +000041 lineno = inspect.currentframe().f_lineno - 1
42 self.assertEqual(w.filename, testmod_filename)
43 self.assertEqual(w.lineno, lineno)
44 self.assertEqual(len(w.warnings), 1)
Mark Dickinson154b7ad2010-03-07 16:24:45 +000045 expected = struct.pack(format, int(number))
46 self.assertEqual(got, expected)
Tim Peters7a3bfc32001-06-12 01:22:22 +000047
Benjamin Petersond5299862008-06-11 01:31:28 +000048 def test_isbigendian(self):
49 self.assertEqual((struct.pack('=i', 1)[0] == chr(0)), ISBIGENDIAN)
Tim Peters7a3bfc32001-06-12 01:22:22 +000050
Benjamin Petersond5299862008-06-11 01:31:28 +000051 def test_consistence(self):
52 self.assertRaises(struct.error, struct.calcsize, 'Z')
Tim Peters7a3bfc32001-06-12 01:22:22 +000053
Benjamin Petersond5299862008-06-11 01:31:28 +000054 sz = struct.calcsize('i')
55 self.assertEqual(sz * 3, struct.calcsize('iii'))
Tim Peters7a3bfc32001-06-12 01:22:22 +000056
Benjamin Petersond5299862008-06-11 01:31:28 +000057 fmt = 'cbxxxxxxhhhhiillffd?'
58 fmt3 = '3c3b18x12h6i6l6f3d3?'
59 sz = struct.calcsize(fmt)
60 sz3 = struct.calcsize(fmt3)
61 self.assertEqual(sz * 3, sz3)
Tim Peters7a3bfc32001-06-12 01:22:22 +000062
Benjamin Petersond5299862008-06-11 01:31:28 +000063 self.assertRaises(struct.error, struct.pack, 'iii', 3)
64 self.assertRaises(struct.error, struct.pack, 'i', 3, 3, 3)
Benjamin Peterson0b00b6b2010-07-09 13:33:03 +000065 self.assertRaises((TypeError, struct.error), struct.pack, 'i', 'foo')
66 self.assertRaises((TypeError, struct.error), struct.pack, 'P', 'foo')
Benjamin Petersond5299862008-06-11 01:31:28 +000067 self.assertRaises(struct.error, struct.unpack, 'd', 'flap')
68 s = struct.pack('ii', 1, 2)
69 self.assertRaises(struct.error, struct.unpack, 'iii', s)
70 self.assertRaises(struct.error, struct.unpack, 'i', s)
Tim Peters7a3bfc32001-06-12 01:22:22 +000071
Benjamin Petersond5299862008-06-11 01:31:28 +000072 def test_transitiveness(self):
73 c = 'a'
74 b = 1
75 h = 255
76 i = 65535
77 l = 65536
78 f = 3.1415
79 d = 3.1415
80 t = True
Tim Peters7a3bfc32001-06-12 01:22:22 +000081
Benjamin Petersond5299862008-06-11 01:31:28 +000082 for prefix in ('', '@', '<', '>', '=', '!'):
83 for format in ('xcbhilfd?', 'xcBHILfd?'):
84 format = prefix + format
85 s = struct.pack(format, c, b, h, i, l, f, d, t)
86 cp, bp, hp, ip, lp, fp, dp, tp = struct.unpack(format, s)
87 self.assertEqual(cp, c)
88 self.assertEqual(bp, b)
89 self.assertEqual(hp, h)
90 self.assertEqual(ip, i)
91 self.assertEqual(lp, l)
92 self.assertEqual(int(100 * fp), int(100 * f))
93 self.assertEqual(int(100 * dp), int(100 * d))
94 self.assertEqual(tp, t)
Tim Peters7a3bfc32001-06-12 01:22:22 +000095
Benjamin Petersond5299862008-06-11 01:31:28 +000096 def test_new_features(self):
97 # Test some of the new features in detail
98 # (format, argument, big-endian result, little-endian result, asymmetric)
99 tests = [
100 ('c', 'a', 'a', 'a', 0),
101 ('xc', 'a', '\0a', '\0a', 0),
102 ('cx', 'a', 'a\0', 'a\0', 0),
103 ('s', 'a', 'a', 'a', 0),
104 ('0s', 'helloworld', '', '', 1),
105 ('1s', 'helloworld', 'h', 'h', 1),
106 ('9s', 'helloworld', 'helloworl', 'helloworl', 1),
107 ('10s', 'helloworld', 'helloworld', 'helloworld', 0),
108 ('11s', 'helloworld', 'helloworld\0', 'helloworld\0', 1),
109 ('20s', 'helloworld', 'helloworld'+10*'\0', 'helloworld'+10*'\0', 1),
110 ('b', 7, '\7', '\7', 0),
111 ('b', -7, '\371', '\371', 0),
112 ('B', 7, '\7', '\7', 0),
113 ('B', 249, '\371', '\371', 0),
114 ('h', 700, '\002\274', '\274\002', 0),
115 ('h', -700, '\375D', 'D\375', 0),
116 ('H', 700, '\002\274', '\274\002', 0),
117 ('H', 0x10000-700, '\375D', 'D\375', 0),
118 ('i', 70000000, '\004,\035\200', '\200\035,\004', 0),
119 ('i', -70000000, '\373\323\342\200', '\200\342\323\373', 0),
120 ('I', 70000000L, '\004,\035\200', '\200\035,\004', 0),
121 ('I', 0x100000000L-70000000, '\373\323\342\200', '\200\342\323\373', 0),
122 ('l', 70000000, '\004,\035\200', '\200\035,\004', 0),
123 ('l', -70000000, '\373\323\342\200', '\200\342\323\373', 0),
124 ('L', 70000000L, '\004,\035\200', '\200\035,\004', 0),
125 ('L', 0x100000000L-70000000, '\373\323\342\200', '\200\342\323\373', 0),
126 ('f', 2.0, '@\000\000\000', '\000\000\000@', 0),
127 ('d', 2.0, '@\000\000\000\000\000\000\000',
128 '\000\000\000\000\000\000\000@', 0),
129 ('f', -2.0, '\300\000\000\000', '\000\000\000\300', 0),
130 ('d', -2.0, '\300\000\000\000\000\000\000\000',
131 '\000\000\000\000\000\000\000\300', 0),
132 ('?', 0, '\0', '\0', 0),
133 ('?', 3, '\1', '\1', 1),
134 ('?', True, '\1', '\1', 0),
135 ('?', [], '\0', '\0', 1),
136 ('?', (1,), '\1', '\1', 1),
137 ]
Tim Peters7a3bfc32001-06-12 01:22:22 +0000138
Benjamin Petersond5299862008-06-11 01:31:28 +0000139 for fmt, arg, big, lil, asy in tests:
140 for (xfmt, exp) in [('>'+fmt, big), ('!'+fmt, big), ('<'+fmt, lil),
141 ('='+fmt, ISBIGENDIAN and big or lil)]:
142 res = struct.pack(xfmt, arg)
143 self.assertEqual(res, exp)
144 self.assertEqual(struct.calcsize(xfmt), len(res))
145 rev = struct.unpack(xfmt, res)[0]
146 if rev != arg:
Benjamin Peterson5c8da862009-06-30 22:57:08 +0000147 self.assertTrue(asy)
Benjamin Petersond5299862008-06-11 01:31:28 +0000148
Mark Dickinsonbb3895c2009-07-07 14:15:45 +0000149 def test_calcsize(self):
150 expected_size = {
151 'b': 1, 'B': 1,
152 'h': 2, 'H': 2,
153 'i': 4, 'I': 4,
154 'l': 4, 'L': 4,
155 'q': 8, 'Q': 8,
156 }
Benjamin Petersond5299862008-06-11 01:31:28 +0000157
Mark Dickinsonbb3895c2009-07-07 14:15:45 +0000158 # standard integer sizes
159 for code in integer_codes:
160 for byteorder in ('=', '<', '>', '!'):
161 format = byteorder+code
162 size = struct.calcsize(format)
163 self.assertEqual(size, expected_size[code])
Tim Peters7a3bfc32001-06-12 01:22:22 +0000164
Mark Dickinsonbb3895c2009-07-07 14:15:45 +0000165 # native integer sizes, except 'q' and 'Q'
166 for format_pair in ('bB', 'hH', 'iI', 'lL'):
167 for byteorder in ['', '@']:
168 signed_size = struct.calcsize(byteorder + format_pair[0])
169 unsigned_size = struct.calcsize(byteorder + format_pair[1])
170 self.assertEqual(signed_size, unsigned_size)
171
172 # bounds for native integer sizes
Ezio Melottia28eb1c2010-04-04 07:00:02 +0000173 self.assertEqual(struct.calcsize('b'), 1)
174 self.assertLessEqual(2, struct.calcsize('h'))
175 self.assertLessEqual(4, struct.calcsize('l'))
176 self.assertLessEqual(struct.calcsize('h'), struct.calcsize('i'))
177 self.assertLessEqual(struct.calcsize('i'), struct.calcsize('l'))
Mark Dickinsonbb3895c2009-07-07 14:15:45 +0000178
179 # tests for native 'q' and 'Q' when applicable
180 if HAVE_LONG_LONG:
181 self.assertEqual(struct.calcsize('q'), struct.calcsize('Q'))
Ezio Melottia28eb1c2010-04-04 07:00:02 +0000182 self.assertLessEqual(8, struct.calcsize('q'))
183 self.assertLessEqual(struct.calcsize('l'), struct.calcsize('q'))
Mark Dickinsonbb3895c2009-07-07 14:15:45 +0000184
185 def test_integers(self):
186 # Integer tests (bBhHiIlLqQ).
Benjamin Petersond5299862008-06-11 01:31:28 +0000187 import binascii
Tim Peters17e17d42001-06-13 22:45:27 +0000188
Benjamin Petersond5299862008-06-11 01:31:28 +0000189 class IntTester(unittest.TestCase):
Mark Dickinsonbb3895c2009-07-07 14:15:45 +0000190 def __init__(self, format):
Gregory P. Smith28399852009-03-31 16:54:10 +0000191 super(IntTester, self).__init__(methodName='test_one')
Mark Dickinsonbb3895c2009-07-07 14:15:45 +0000192 self.format = format
193 self.code = format[-1]
194 self.direction = format[:-1]
195 if not self.direction in ('', '@', '=', '<', '>', '!'):
196 raise ValueError("unrecognized packing direction: %s" %
197 self.direction)
198 self.bytesize = struct.calcsize(format)
199 self.bitsize = self.bytesize * 8
200 if self.code in tuple('bhilq'):
201 self.signed = True
202 self.min_value = -(2L**(self.bitsize-1))
203 self.max_value = 2L**(self.bitsize-1) - 1
204 elif self.code in tuple('BHILQ'):
205 self.signed = False
206 self.min_value = 0
207 self.max_value = 2L**self.bitsize - 1
208 else:
209 raise ValueError("unrecognized format code: %s" %
210 self.code)
Tim Peters17e17d42001-06-13 22:45:27 +0000211
Benjamin Petersond5299862008-06-11 01:31:28 +0000212 def test_one(self, x, pack=struct.pack,
213 unpack=struct.unpack,
214 unhexlify=binascii.unhexlify):
Mark Dickinsonbb3895c2009-07-07 14:15:45 +0000215
216 format = self.format
217 if self.min_value <= x <= self.max_value:
Benjamin Petersond5299862008-06-11 01:31:28 +0000218 expected = long(x)
Mark Dickinsonbb3895c2009-07-07 14:15:45 +0000219 if self.signed and x < 0:
Benjamin Petersond5299862008-06-11 01:31:28 +0000220 expected += 1L << self.bitsize
Ezio Melottia28eb1c2010-04-04 07:00:02 +0000221 self.assertGreaterEqual(expected, 0)
Mark Dickinsonbb3895c2009-07-07 14:15:45 +0000222 expected = '%x' % expected
Benjamin Petersond5299862008-06-11 01:31:28 +0000223 if len(expected) & 1:
224 expected = "0" + expected
225 expected = unhexlify(expected)
Mark Dickinsonbb3895c2009-07-07 14:15:45 +0000226 expected = ("\x00" * (self.bytesize - len(expected)) +
227 expected)
228 if (self.direction == '<' or
229 self.direction in ('', '@', '=') and not ISBIGENDIAN):
230 expected = string_reverse(expected)
231 self.assertEqual(len(expected), self.bytesize)
Tim Peters0891ac02001-09-15 02:35:15 +0000232
Benjamin Petersond5299862008-06-11 01:31:28 +0000233 # Pack work?
234 got = pack(format, x)
235 self.assertEqual(got, expected)
Tim Peters0891ac02001-09-15 02:35:15 +0000236
Benjamin Petersond5299862008-06-11 01:31:28 +0000237 # Unpack work?
238 retrieved = unpack(format, got)[0]
239 self.assertEqual(x, retrieved)
Tim Petersd50ade62003-03-20 18:32:13 +0000240
Benjamin Petersond5299862008-06-11 01:31:28 +0000241 # Adding any byte should cause a "too big" error.
242 self.assertRaises((struct.error, TypeError), unpack, format,
243 '\x01' + got)
Benjamin Petersond5299862008-06-11 01:31:28 +0000244 else:
245 # x is out of range -- verify pack realizes that.
Benjamin Peterson9d8c4562010-07-10 15:17:08 +0000246 self.assertRaises((OverflowError, ValueError, struct.error),
247 pack, format, x)
Benjamin Petersond5299862008-06-11 01:31:28 +0000248
249 def run(self):
250 from random import randrange
251
252 # Create all interesting powers of 2.
253 values = []
254 for exp in range(self.bitsize + 3):
255 values.append(1L << exp)
256
257 # Add some random values.
258 for i in range(self.bitsize):
259 val = 0L
260 for j in range(self.bytesize):
261 val = (val << 8) | randrange(256)
262 values.append(val)
263
Mark Dickinsonbb3895c2009-07-07 14:15:45 +0000264 # Values absorbed from other tests
265 values.extend([300, 700000, sys.maxint*4])
266
267 # Try all those, and their negations, and +-1 from
268 # them. Note that this tests all power-of-2
269 # boundaries in range, and a few out of range, plus
270 # +-(2**n +- 1).
Benjamin Petersond5299862008-06-11 01:31:28 +0000271 for base in values:
272 for val in -base, base:
273 for incr in -1, 0, 1:
274 x = val + incr
Mark Dickinsonbb3895c2009-07-07 14:15:45 +0000275 self.test_one(int(x))
276 self.test_one(long(x))
Benjamin Petersond5299862008-06-11 01:31:28 +0000277
278 # Some error cases.
Mark Dickinson463dc4b2009-07-05 10:01:24 +0000279 class NotAnIntNS(object):
280 def __int__(self):
281 return 42
282
283 def __long__(self):
284 return 1729L
285
286 class NotAnIntOS:
287 def __int__(self):
Mark Dickinson154b7ad2010-03-07 16:24:45 +0000288 return 85
Mark Dickinson463dc4b2009-07-05 10:01:24 +0000289
290 def __long__(self):
291 return -163L
292
Mark Dickinsonfdaaa9c2010-04-04 08:43:04 +0000293 # Objects with an '__index__' method should be allowed
294 # to pack as integers. That is assuming the implemented
295 # '__index__' method returns and 'int' or 'long'.
296 class Indexable(object):
297 def __init__(self, value):
298 self._value = value
299
300 def __index__(self):
301 return self._value
302
303 # If the '__index__' method raises a type error, then
304 # '__int__' should be used with a deprecation warning.
305 class BadIndex(object):
306 def __index__(self):
307 raise TypeError
308
309 def __int__(self):
310 return 42
311
Florent Xiclunadb4a3212010-03-20 22:21:02 +0000312 self.assertRaises((TypeError, struct.error),
313 struct.pack, self.format,
314 "a string")
315 self.assertRaises((TypeError, struct.error),
316 struct.pack, self.format,
317 randrange)
318 with check_warnings(("integer argument expected, "
319 "got non-integer", DeprecationWarning)):
Benjamin Peterson8e93f4e2010-07-09 18:15:28 +0000320 with self.assertRaises((TypeError, struct.error)):
321 struct.pack(self.format, 3+42j)
Benjamin Petersond5299862008-06-11 01:31:28 +0000322
Mark Dickinson154b7ad2010-03-07 16:24:45 +0000323 # an attempt to convert a non-integer (with an
324 # implicit conversion via __int__) should succeed,
325 # with a DeprecationWarning
Mark Dickinsonfdaaa9c2010-04-04 08:43:04 +0000326 for nonint in NotAnIntNS(), NotAnIntOS(), BadIndex():
Florent Xiclunadb4a3212010-03-20 22:21:02 +0000327 with check_warnings((".*integer argument expected, got non"
328 "-integer", DeprecationWarning)) as w:
Mark Dickinson154b7ad2010-03-07 16:24:45 +0000329 got = struct.pack(self.format, nonint)
Florent Xiclunadb4a3212010-03-20 22:21:02 +0000330 lineno = inspect.currentframe().f_lineno - 1
331 self.assertEqual(w.filename, testmod_filename)
332 self.assertEqual(w.lineno, lineno)
333 self.assertEqual(len(w.warnings), 1)
Mark Dickinson154b7ad2010-03-07 16:24:45 +0000334 expected = struct.pack(self.format, int(nonint))
335 self.assertEqual(got, expected)
336
Mark Dickinsonfdaaa9c2010-04-04 08:43:04 +0000337 # Check for legitimate values from '__index__'.
Mark Dickinson4846a8e2010-04-03 14:05:10 +0000338 for obj in (Indexable(0), Indexable(10), Indexable(17),
339 Indexable(42), Indexable(100), Indexable(127)):
340 try:
341 struct.pack(format, obj)
342 except:
343 self.fail("integer code pack failed on object "
344 "with '__index__' method")
345
Mark Dickinsonfdaaa9c2010-04-04 08:43:04 +0000346 # Check for bogus values from '__index__'.
347 for obj in (Indexable('a'), Indexable(u'b'), Indexable(None),
348 Indexable({'a': 1}), Indexable([1, 2, 3])):
349 self.assertRaises((TypeError, struct.error),
350 struct.pack, self.format,
351 obj)
Mark Dickinson4846a8e2010-04-03 14:05:10 +0000352
Mark Dickinsonbb3895c2009-07-07 14:15:45 +0000353 byteorders = '', '@', '=', '<', '>', '!'
354 for code in integer_codes:
355 for byteorder in byteorders:
356 if (byteorder in ('', '@') and code in ('q', 'Q') and
357 not HAVE_LONG_LONG):
358 continue
359 format = byteorder+code
360 t = IntTester(format)
361 t.run()
Benjamin Petersond5299862008-06-11 01:31:28 +0000362
363 def test_p_code(self):
364 # Test p ("Pascal string") code.
365 for code, input, expected, expectedback in [
366 ('p','abc', '\x00', ''),
367 ('1p', 'abc', '\x00', ''),
368 ('2p', 'abc', '\x01a', 'a'),
369 ('3p', 'abc', '\x02ab', 'ab'),
370 ('4p', 'abc', '\x03abc', 'abc'),
371 ('5p', 'abc', '\x03abc\x00', 'abc'),
372 ('6p', 'abc', '\x03abc\x00\x00', 'abc'),
373 ('1000p', 'x'*1000, '\xff' + 'x'*999, 'x'*255)]:
374 got = struct.pack(code, input)
375 self.assertEqual(got, expected)
376 (got,) = struct.unpack(code, got)
377 self.assertEqual(got, expectedback)
378
379 def test_705836(self):
380 # SF bug 705836. "<f" and ">f" had a severe rounding bug, where a carry
381 # from the low-order discarded bits could propagate into the exponent
382 # field, causing the result to be wrong by a factor of 2.
383 import math
384
385 for base in range(1, 33):
386 # smaller <- largest representable float less than base.
387 delta = 0.5
388 while base - delta / 2.0 != base:
389 delta /= 2.0
390 smaller = base - delta
391 # Packing this rounds away a solid string of trailing 1 bits.
392 packed = struct.pack("<f", smaller)
393 unpacked = struct.unpack("<f", packed)[0]
394 # This failed at base = 2, 4, and 32, with unpacked = 1, 2, and
395 # 16, respectively.
396 self.assertEqual(base, unpacked)
397 bigpacked = struct.pack(">f", smaller)
398 self.assertEqual(bigpacked, string_reverse(packed))
399 unpacked = struct.unpack(">f", bigpacked)[0]
400 self.assertEqual(base, unpacked)
401
402 # Largest finite IEEE single.
403 big = (1 << 24) - 1
404 big = math.ldexp(big, 127 - 23)
Tim Petersd50ade62003-03-20 18:32:13 +0000405 packed = struct.pack(">f", big)
Benjamin Petersond5299862008-06-11 01:31:28 +0000406 unpacked = struct.unpack(">f", packed)[0]
407 self.assertEqual(big, unpacked)
Tim Petersd50ade62003-03-20 18:32:13 +0000408
Benjamin Petersond5299862008-06-11 01:31:28 +0000409 # The same, but tack on a 1 bit so it rounds up to infinity.
410 big = (1 << 25) - 1
411 big = math.ldexp(big, 127 - 24)
412 self.assertRaises(OverflowError, struct.pack, ">f", big)
Bob Ippolitoeb621272006-05-24 15:32:06 +0000413
Mark Dickinson463dc4b2009-07-05 10:01:24 +0000414 def test_1530559(self):
Benjamin Petersond5299862008-06-11 01:31:28 +0000415 # SF bug 1530559. struct.pack raises TypeError where it used to convert.
416 for endian in ('', '>', '<'):
Mark Dickinsonbb3895c2009-07-07 14:15:45 +0000417 for fmt in integer_codes:
Benjamin Petersond5299862008-06-11 01:31:28 +0000418 self.check_float_coerce(endian + fmt, 1.0)
419 self.check_float_coerce(endian + fmt, 1.5)
Bob Ippolitoeb621272006-05-24 15:32:06 +0000420
Florent Xiclunadb4a3212010-03-20 22:21:02 +0000421 def test_unpack_from(self, cls=str):
422 data = cls('abcd01234')
Benjamin Petersond5299862008-06-11 01:31:28 +0000423 fmt = '4s'
424 s = struct.Struct(fmt)
Florent Xiclunadb4a3212010-03-20 22:21:02 +0000425
426 self.assertEqual(s.unpack_from(data), ('abcd',))
427 self.assertEqual(struct.unpack_from(fmt, data), ('abcd',))
428 for i in xrange(6):
429 self.assertEqual(s.unpack_from(data, i), (data[i:i+4],))
430 self.assertEqual(struct.unpack_from(fmt, data, i), (data[i:i+4],))
431 for i in xrange(6, len(data) + 1):
432 self.assertRaises(struct.error, s.unpack_from, data, i)
433 self.assertRaises(struct.error, struct.unpack_from, fmt, data, i)
Martin Blais2856e5f2006-05-26 12:03:27 +0000434
Benjamin Petersond5299862008-06-11 01:31:28 +0000435 def test_pack_into(self):
436 test_string = 'Reykjavik rocks, eow!'
437 writable_buf = array.array('c', ' '*100)
438 fmt = '21s'
439 s = struct.Struct(fmt)
Bob Ippolitoe6c9f982006-08-04 23:59:21 +0000440
Benjamin Petersond5299862008-06-11 01:31:28 +0000441 # Test without offset
442 s.pack_into(writable_buf, 0, test_string)
443 from_buf = writable_buf.tostring()[:len(test_string)]
444 self.assertEqual(from_buf, test_string)
Bob Ippolitoe6c9f982006-08-04 23:59:21 +0000445
Benjamin Petersond5299862008-06-11 01:31:28 +0000446 # Test with offset.
447 s.pack_into(writable_buf, 10, test_string)
448 from_buf = writable_buf.tostring()[:len(test_string)+10]
449 self.assertEqual(from_buf, test_string[:10] + test_string)
Bob Ippolitoe6c9f982006-08-04 23:59:21 +0000450
Benjamin Petersond5299862008-06-11 01:31:28 +0000451 # Go beyond boundaries.
452 small_buf = array.array('c', ' '*10)
Benjamin Peterson86ac22e2010-07-07 23:26:57 +0000453 self.assertRaises((ValueError, struct.error), s.pack_into, small_buf, 0,
454 test_string)
455 self.assertRaises((ValueError, struct.error), s.pack_into, small_buf, 2,
456 test_string)
Bob Ippolitoe6c9f982006-08-04 23:59:21 +0000457
Georg Brandl0638a082009-02-13 11:03:59 +0000458 # Test bogus offset (issue 3694)
459 sb = small_buf
Benjamin Peterson0b00b6b2010-07-09 13:33:03 +0000460 self.assertRaises((TypeError, struct.error), struct.pack_into, b'', sb,
461 None)
Georg Brandl0638a082009-02-13 11:03:59 +0000462
Benjamin Petersond5299862008-06-11 01:31:28 +0000463 def test_pack_into_fn(self):
464 test_string = 'Reykjavik rocks, eow!'
465 writable_buf = array.array('c', ' '*100)
466 fmt = '21s'
467 pack_into = lambda *args: struct.pack_into(fmt, *args)
Martin Blais2856e5f2006-05-26 12:03:27 +0000468
Benjamin Petersond5299862008-06-11 01:31:28 +0000469 # Test without offset.
470 pack_into(writable_buf, 0, test_string)
471 from_buf = writable_buf.tostring()[:len(test_string)]
472 self.assertEqual(from_buf, test_string)
Martin Blais2856e5f2006-05-26 12:03:27 +0000473
Benjamin Petersond5299862008-06-11 01:31:28 +0000474 # Test with offset.
475 pack_into(writable_buf, 10, test_string)
476 from_buf = writable_buf.tostring()[:len(test_string)+10]
477 self.assertEqual(from_buf, test_string[:10] + test_string)
Martin Blais2856e5f2006-05-26 12:03:27 +0000478
Benjamin Petersond5299862008-06-11 01:31:28 +0000479 # Go beyond boundaries.
480 small_buf = array.array('c', ' '*10)
Benjamin Peterson86ac22e2010-07-07 23:26:57 +0000481 self.assertRaises((ValueError, struct.error), pack_into, small_buf, 0,
482 test_string)
483 self.assertRaises((ValueError, struct.error), pack_into, small_buf, 2,
484 test_string)
Martin Blais2856e5f2006-05-26 12:03:27 +0000485
Benjamin Petersond5299862008-06-11 01:31:28 +0000486 def test_unpack_with_buffer(self):
Florent Xiclunadb4a3212010-03-20 22:21:02 +0000487 with check_py3k_warnings(("buffer.. not supported in 3.x",
488 DeprecationWarning)):
489 # SF bug 1563759: struct.unpack doesn't support buffer protocol objects
490 data1 = array.array('B', '\x12\x34\x56\x78')
491 data2 = buffer('......\x12\x34\x56\x78......', 6, 4)
492 for data in [data1, data2]:
493 value, = struct.unpack('>I', data)
494 self.assertEqual(value, 0x12345678)
495
496 self.test_unpack_from(cls=buffer)
Martin Blais2856e5f2006-05-26 12:03:27 +0000497
Benjamin Petersond5299862008-06-11 01:31:28 +0000498 def test_bool(self):
Benjamin Peterson489113f2010-07-07 19:03:36 +0000499 class ExplodingBool(object):
500 def __nonzero__(self):
501 raise IOError
Benjamin Petersond5299862008-06-11 01:31:28 +0000502 for prefix in tuple("<>!=")+('',):
503 false = (), [], [], '', 0
Florent Xiclunadb4a3212010-03-20 22:21:02 +0000504 true = [1], 'test', 5, -1, 0xffffffffL+1, 0xffffffff//2
Martin Blais2856e5f2006-05-26 12:03:27 +0000505
Benjamin Petersond5299862008-06-11 01:31:28 +0000506 falseFormat = prefix + '?' * len(false)
507 packedFalse = struct.pack(falseFormat, *false)
508 unpackedFalse = struct.unpack(falseFormat, packedFalse)
Bob Ippolito1fcdc232006-05-27 12:11:36 +0000509
Benjamin Petersond5299862008-06-11 01:31:28 +0000510 trueFormat = prefix + '?' * len(true)
511 packedTrue = struct.pack(trueFormat, *true)
512 unpackedTrue = struct.unpack(trueFormat, packedTrue)
Bob Ippolito1fcdc232006-05-27 12:11:36 +0000513
Benjamin Petersond5299862008-06-11 01:31:28 +0000514 self.assertEqual(len(true), len(unpackedTrue))
515 self.assertEqual(len(false), len(unpackedFalse))
Bob Ippolito1fcdc232006-05-27 12:11:36 +0000516
Benjamin Petersond5299862008-06-11 01:31:28 +0000517 for t in unpackedFalse:
518 self.assertFalse(t)
519 for t in unpackedTrue:
520 self.assertTrue(t)
Bob Ippolito1fcdc232006-05-27 12:11:36 +0000521
Benjamin Petersond5299862008-06-11 01:31:28 +0000522 packed = struct.pack(prefix+'?', 1)
Bob Ippolito1fcdc232006-05-27 12:11:36 +0000523
Benjamin Petersond5299862008-06-11 01:31:28 +0000524 self.assertEqual(len(packed), struct.calcsize(prefix+'?'))
Tim Petersfe98f962006-05-26 12:26:21 +0000525
Benjamin Petersond5299862008-06-11 01:31:28 +0000526 if len(packed) != 1:
527 self.assertFalse(prefix, msg='encoded bool is not one byte: %r'
528 %packed)
Tim Petersc65a13f2006-06-04 01:22:53 +0000529
Benjamin Peterson489113f2010-07-07 19:03:36 +0000530 self.assertRaises(IOError, struct.pack, prefix + '?',
531 ExplodingBool())
532
533 for c in [b'\x01', b'\x7f', b'\xff', b'\x0f', b'\xf0']:
534 self.assertTrue(struct.unpack('>?', c)[0])
Martin v. Löwisaef4c6b2007-01-21 09:33:07 +0000535
Florent Xiclunadb4a3212010-03-20 22:21:02 +0000536 @unittest.skipUnless(IS32BIT, "Specific to 32bit machines")
537 def test_crasher(self):
538 self.assertRaises(MemoryError, struct.pack, "357913941c", "a")
Gregory P. Smith9d534572008-06-11 07:41:16 +0000539
Mark Dickinson40228912010-06-11 20:27:05 +0000540 def test_count_overflow(self):
541 hugecount = '{}b'.format(sys.maxsize+1)
542 self.assertRaises(struct.error, struct.calcsize, hugecount)
543
544 hugecount2 = '{}b{}H'.format(sys.maxsize//2, sys.maxsize//2)
545 self.assertRaises(struct.error, struct.calcsize, hugecount2)
Tim Petersf733abb2007-01-30 03:03:46 +0000546
Benjamin Petersond5299862008-06-11 01:31:28 +0000547def test_main():
Senthil Kumarance8e33a2010-01-08 19:04:16 +0000548 run_unittest(StructTest)
Tim Petersf733abb2007-01-30 03:03:46 +0000549
Benjamin Petersond5299862008-06-11 01:31:28 +0000550if __name__ == '__main__':
551 test_main()