blob: 933246639c9dbb287cdbf762626289e0a82902b6 [file] [log] [blame]
Barry Warsaw04f357c2002-07-23 19:04:11 +00001from test.test_support import TestFailed, verbose, verify
Barry Warsaw07a0eec1996-12-12 23:34:06 +00002import struct
Barry Warsaw07a0eec1996-12-12 23:34:06 +00003
Tim Peters17e17d42001-06-13 22:45:27 +00004import sys
5ISBIGENDIAN = sys.byteorder == "big"
6del sys
7verify((struct.pack('=i', 1)[0] == chr(0)) == ISBIGENDIAN,
8 "bigendian determination appears wrong")
9
10def string_reverse(s):
11 chars = list(s)
12 chars.reverse()
13 return "".join(chars)
14
15def bigendian_to_native(value):
16 if ISBIGENDIAN:
17 return value
18 else:
19 return string_reverse(value)
20
Barry Warsaw07a0eec1996-12-12 23:34:06 +000021def simple_err(func, *args):
22 try:
Guido van Rossum68468eb2003-02-27 20:14:51 +000023 func(*args)
Barry Warsaw07a0eec1996-12-12 23:34:06 +000024 except struct.error:
Guido van Rossum41360a41998-03-26 19:42:58 +000025 pass
Barry Warsaw07a0eec1996-12-12 23:34:06 +000026 else:
Guido van Rossum41360a41998-03-26 19:42:58 +000027 raise TestFailed, "%s%s did not raise struct.error" % (
28 func.__name__, args)
Barry Warsaw07a0eec1996-12-12 23:34:06 +000029
Tim Peters7a3bfc32001-06-12 01:22:22 +000030def any_err(func, *args):
31 try:
Guido van Rossum68468eb2003-02-27 20:14:51 +000032 func(*args)
Tim Peters7a3bfc32001-06-12 01:22:22 +000033 except (struct.error, OverflowError, TypeError):
34 pass
35 else:
36 raise TestFailed, "%s%s did not raise error" % (
37 func.__name__, args)
Tim Peters7a3bfc32001-06-12 01:22:22 +000038
Tim Peters17e17d42001-06-13 22:45:27 +000039
Tim Peters7b9542a2001-06-10 23:40:19 +000040simple_err(struct.calcsize, 'Z')
Barry Warsaw07a0eec1996-12-12 23:34:06 +000041
42sz = struct.calcsize('i')
Fred Drake132dce22000-12-12 23:11:42 +000043if sz * 3 != struct.calcsize('iii'):
Guido van Rossum2a378501996-12-31 17:25:47 +000044 raise TestFailed, 'inconsistent sizes'
Barry Warsaw07a0eec1996-12-12 23:34:06 +000045
Guido van Rossum04ebf5c1997-01-03 19:00:37 +000046fmt = 'cbxxxxxxhhhhiillffd'
47fmt3 = '3c3b18x12h6i6l6f3d'
48sz = struct.calcsize(fmt)
49sz3 = struct.calcsize(fmt3)
Fred Drake132dce22000-12-12 23:11:42 +000050if sz * 3 != sz3:
Walter Dörwald70a6b492004-02-12 17:35:32 +000051 raise TestFailed, 'inconsistent sizes (3*%r -> 3*%d = %d, %r -> %d)' % (
52 fmt, sz, 3*sz, fmt3, sz3)
Barry Warsaw07a0eec1996-12-12 23:34:06 +000053
54simple_err(struct.pack, 'iii', 3)
55simple_err(struct.pack, 'i', 3, 3, 3)
56simple_err(struct.pack, 'i', 'foo')
Armin Rigo9f904392004-09-27 19:27:51 +000057simple_err(struct.pack, 'P', 'foo')
Barry Warsaw07a0eec1996-12-12 23:34:06 +000058simple_err(struct.unpack, 'd', 'flap')
59s = struct.pack('ii', 1, 2)
60simple_err(struct.unpack, 'iii', s)
61simple_err(struct.unpack, 'i', s)
62
63c = 'a'
Guido van Rossum2a378501996-12-31 17:25:47 +000064b = 1
Barry Warsaw07a0eec1996-12-12 23:34:06 +000065h = 255
66i = 65535
67l = 65536
68f = 3.1415
69d = 3.1415
70
Guido van Rossum420c11c1997-01-03 00:09:46 +000071for prefix in ('', '@', '<', '>', '=', '!'):
72 for format in ('xcbhilfd', 'xcBHILfd'):
Guido van Rossum41360a41998-03-26 19:42:58 +000073 format = prefix + format
74 if verbose:
75 print "trying:", format
76 s = struct.pack(format, c, b, h, i, l, f, d)
77 cp, bp, hp, ip, lp, fp, dp = struct.unpack(format, s)
Fred Drake132dce22000-12-12 23:11:42 +000078 if (cp != c or bp != b or hp != h or ip != i or lp != l or
79 int(100 * fp) != int(100 * f) or int(100 * dp) != int(100 * d)):
Guido van Rossum41360a41998-03-26 19:42:58 +000080 # ^^^ calculate only to two decimal places
81 raise TestFailed, "unpack/pack not transitive (%s, %s)" % (
82 str(format), str((cp, bp, hp, ip, lp, fp, dp)))
Guido van Rossum2a378501996-12-31 17:25:47 +000083
Guido van Rossum420c11c1997-01-03 00:09:46 +000084# Test some of the new features in detail
Guido van Rossum2a378501996-12-31 17:25:47 +000085
86# (format, argument, big-endian result, little-endian result, asymmetric)
87tests = [
88 ('c', 'a', 'a', 'a', 0),
89 ('xc', 'a', '\0a', '\0a', 0),
90 ('cx', 'a', 'a\0', 'a\0', 0),
91 ('s', 'a', 'a', 'a', 0),
92 ('0s', 'helloworld', '', '', 1),
93 ('1s', 'helloworld', 'h', 'h', 1),
94 ('9s', 'helloworld', 'helloworl', 'helloworl', 1),
95 ('10s', 'helloworld', 'helloworld', 'helloworld', 0),
96 ('11s', 'helloworld', 'helloworld\0', 'helloworld\0', 1),
97 ('20s', 'helloworld', 'helloworld'+10*'\0', 'helloworld'+10*'\0', 1),
98 ('b', 7, '\7', '\7', 0),
99 ('b', -7, '\371', '\371', 0),
100 ('B', 7, '\7', '\7', 0),
101 ('B', 249, '\371', '\371', 0),
102 ('h', 700, '\002\274', '\274\002', 0),
103 ('h', -700, '\375D', 'D\375', 0),
104 ('H', 700, '\002\274', '\274\002', 0),
105 ('H', 0x10000-700, '\375D', 'D\375', 0),
106 ('i', 70000000, '\004,\035\200', '\200\035,\004', 0),
107 ('i', -70000000, '\373\323\342\200', '\200\342\323\373', 0),
108 ('I', 70000000L, '\004,\035\200', '\200\035,\004', 0),
109 ('I', 0x100000000L-70000000, '\373\323\342\200', '\200\342\323\373', 0),
110 ('l', 70000000, '\004,\035\200', '\200\035,\004', 0),
111 ('l', -70000000, '\373\323\342\200', '\200\342\323\373', 0),
112 ('L', 70000000L, '\004,\035\200', '\200\035,\004', 0),
113 ('L', 0x100000000L-70000000, '\373\323\342\200', '\200\342\323\373', 0),
Guido van Rossum420c11c1997-01-03 00:09:46 +0000114 ('f', 2.0, '@\000\000\000', '\000\000\000@', 0),
115 ('d', 2.0, '@\000\000\000\000\000\000\000',
116 '\000\000\000\000\000\000\000@', 0),
117 ('f', -2.0, '\300\000\000\000', '\000\000\000\300', 0),
118 ('d', -2.0, '\300\000\000\000\000\000\000\000',
119 '\000\000\000\000\000\000\000\300', 0),
Guido van Rossum2a378501996-12-31 17:25:47 +0000120]
121
Guido van Rossum2a378501996-12-31 17:25:47 +0000122for fmt, arg, big, lil, asy in tests:
123 if verbose:
Walter Dörwald70a6b492004-02-12 17:35:32 +0000124 print "%r %r %r %r" % (fmt, arg, big, lil)
Guido van Rossum2a378501996-12-31 17:25:47 +0000125 for (xfmt, exp) in [('>'+fmt, big), ('!'+fmt, big), ('<'+fmt, lil),
Tim Peters17e17d42001-06-13 22:45:27 +0000126 ('='+fmt, ISBIGENDIAN and big or lil)]:
Guido van Rossum41360a41998-03-26 19:42:58 +0000127 res = struct.pack(xfmt, arg)
128 if res != exp:
Walter Dörwald70a6b492004-02-12 17:35:32 +0000129 raise TestFailed, "pack(%r, %r) -> %r # expected %r" % (
130 fmt, arg, res, exp)
Guido van Rossum41360a41998-03-26 19:42:58 +0000131 n = struct.calcsize(xfmt)
132 if n != len(res):
Walter Dörwald70a6b492004-02-12 17:35:32 +0000133 raise TestFailed, "calcsize(%r) -> %d # expected %d" % (
134 xfmt, n, len(res))
Guido van Rossum41360a41998-03-26 19:42:58 +0000135 rev = struct.unpack(xfmt, res)[0]
136 if rev != arg and not asy:
Walter Dörwald70a6b492004-02-12 17:35:32 +0000137 raise TestFailed, "unpack(%r, %r) -> (%r,) # expected (%r,)" % (
138 fmt, res, rev, arg)
Tim Peters7b9542a2001-06-10 23:40:19 +0000139
Tim Peters7a3bfc32001-06-12 01:22:22 +0000140###########################################################################
Tim Peters17e17d42001-06-13 22:45:27 +0000141# Simple native q/Q tests.
Tim Peters7b9542a2001-06-10 23:40:19 +0000142
143has_native_qQ = 1
144try:
145 struct.pack("q", 5)
146except struct.error:
147 has_native_qQ = 0
148
149if verbose:
150 print "Platform has native q/Q?", has_native_qQ and "Yes." or "No."
151
Tim Peters7a3bfc32001-06-12 01:22:22 +0000152any_err(struct.pack, "Q", -1) # can't pack -1 as unsigned regardless
Tim Peters7b9542a2001-06-10 23:40:19 +0000153simple_err(struct.pack, "q", "a") # can't pack string as 'q' regardless
154simple_err(struct.pack, "Q", "a") # ditto, but 'Q'
155
Tim Peters7a3bfc32001-06-12 01:22:22 +0000156def test_native_qQ():
Tim Peters7b9542a2001-06-10 23:40:19 +0000157 bytes = struct.calcsize('q')
158 # The expected values here are in big-endian format, primarily because
159 # I'm on a little-endian machine and so this is the clearest way (for
160 # me) to force the code to get exercised.
161 for format, input, expected in (
162 ('q', -1, '\xff' * bytes),
163 ('q', 0, '\x00' * bytes),
164 ('Q', 0, '\x00' * bytes),
165 ('q', 1L, '\x00' * (bytes-1) + '\x01'),
166 ('Q', (1L << (8*bytes))-1, '\xff' * bytes),
167 ('q', (1L << (8*bytes-1))-1, '\x7f' + '\xff' * (bytes - 1))):
168 got = struct.pack(format, input)
Tim Petersc533edc2001-06-10 23:52:59 +0000169 native_expected = bigendian_to_native(expected)
170 verify(got == native_expected,
Tim Peters7b9542a2001-06-10 23:40:19 +0000171 "%r-pack of %r gave %r, not %r" %
Tim Petersc533edc2001-06-10 23:52:59 +0000172 (format, input, got, native_expected))
Tim Peters7b9542a2001-06-10 23:40:19 +0000173 retrieved = struct.unpack(format, got)[0]
174 verify(retrieved == input,
175 "%r-unpack of %r gave %r, not %r" %
176 (format, got, retrieved, input))
Tim Peters7a3bfc32001-06-12 01:22:22 +0000177
178if has_native_qQ:
179 test_native_qQ()
180
Tim Peters17e17d42001-06-13 22:45:27 +0000181###########################################################################
182# Standard integer tests (bBhHiIlLqQ).
Tim Peters7a3bfc32001-06-12 01:22:22 +0000183
184import binascii
Tim Peters7a3bfc32001-06-12 01:22:22 +0000185
Tim Peters17e17d42001-06-13 22:45:27 +0000186class IntTester:
Tim Peters7a3bfc32001-06-12 01:22:22 +0000187
Tim Peters17e17d42001-06-13 22:45:27 +0000188 # XXX Most std integer modes fail to test for out-of-range.
Tim Peters3eec38a2001-06-18 22:27:39 +0000189 # The "i" and "l" codes appear to range-check OK on 32-bit boxes, but
190 # fail to check correctly on some 64-bit ones (Tru64 Unix + Compaq C
191 # reported by Mark Favas).
192 BUGGY_RANGE_CHECK = "bBhHiIlL"
Tim Peters7a3bfc32001-06-12 01:22:22 +0000193
Tim Peters17e17d42001-06-13 22:45:27 +0000194 def __init__(self, formatpair, bytesize):
195 assert len(formatpair) == 2
196 self.formatpair = formatpair
197 for direction in "<>!=":
198 for code in formatpair:
199 format = direction + code
200 verify(struct.calcsize(format) == bytesize)
201 self.bytesize = bytesize
202 self.bitsize = bytesize * 8
203 self.signed_code, self.unsigned_code = formatpair
204 self.unsigned_min = 0
205 self.unsigned_max = 2L**self.bitsize - 1
206 self.signed_min = -(2L**(self.bitsize-1))
207 self.signed_max = 2L**(self.bitsize-1) - 1
Tim Peters7a3bfc32001-06-12 01:22:22 +0000208
Tim Peters17e17d42001-06-13 22:45:27 +0000209 def test_one(self, x, pack=struct.pack,
210 unpack=struct.unpack,
211 unhexlify=binascii.unhexlify):
212 if verbose:
213 print "trying std", self.formatpair, "on", x, "==", hex(x)
Tim Peters7a3bfc32001-06-12 01:22:22 +0000214
Tim Peters17e17d42001-06-13 22:45:27 +0000215 # Try signed.
216 code = self.signed_code
217 if self.signed_min <= x <= self.signed_max:
218 # Try big-endian.
219 expected = long(x)
220 if x < 0:
221 expected += 1L << self.bitsize
222 assert expected > 0
223 expected = hex(expected)[2:-1] # chop "0x" and trailing 'L'
224 if len(expected) & 1:
225 expected = "0" + expected
226 expected = unhexlify(expected)
227 expected = "\x00" * (self.bytesize - len(expected)) + expected
Tim Peters7a3bfc32001-06-12 01:22:22 +0000228
Tim Peters17e17d42001-06-13 22:45:27 +0000229 # Pack work?
230 format = ">" + code
231 got = pack(format, x)
232 verify(got == expected,
233 "'%s'-pack of %r gave %r, not %r" %
234 (format, x, got, expected))
Tim Peters7a3bfc32001-06-12 01:22:22 +0000235
Tim Peters17e17d42001-06-13 22:45:27 +0000236 # Unpack work?
237 retrieved = unpack(format, got)[0]
238 verify(x == retrieved,
239 "'%s'-unpack of %r gave %r, not %r" %
240 (format, got, retrieved, x))
Tim Peters7a3bfc32001-06-12 01:22:22 +0000241
Tim Peters17e17d42001-06-13 22:45:27 +0000242 # Adding any byte should cause a "too big" error.
243 any_err(unpack, format, '\x01' + got)
Tim Peters7a3bfc32001-06-12 01:22:22 +0000244
Tim Peters17e17d42001-06-13 22:45:27 +0000245 # Try little-endian.
246 format = "<" + code
247 expected = string_reverse(expected)
Tim Peters7a3bfc32001-06-12 01:22:22 +0000248
Tim Peters17e17d42001-06-13 22:45:27 +0000249 # Pack work?
250 got = pack(format, x)
251 verify(got == expected,
252 "'%s'-pack of %r gave %r, not %r" %
253 (format, x, got, expected))
Tim Peters7a3bfc32001-06-12 01:22:22 +0000254
Tim Peters17e17d42001-06-13 22:45:27 +0000255 # Unpack work?
256 retrieved = unpack(format, got)[0]
257 verify(x == retrieved,
258 "'%s'-unpack of %r gave %r, not %r" %
259 (format, got, retrieved, x))
Tim Peters7a3bfc32001-06-12 01:22:22 +0000260
Tim Peters17e17d42001-06-13 22:45:27 +0000261 # Adding any byte should cause a "too big" error.
262 any_err(unpack, format, '\x01' + got)
Tim Peters7a3bfc32001-06-12 01:22:22 +0000263
Tim Peters17e17d42001-06-13 22:45:27 +0000264 else:
265 # x is out of range -- verify pack realizes that.
266 if code in self.BUGGY_RANGE_CHECK:
267 if verbose:
268 print "Skipping buggy range check for code", code
269 else:
270 any_err(pack, ">" + code, x)
271 any_err(pack, "<" + code, x)
Tim Peters7a3bfc32001-06-12 01:22:22 +0000272
Tim Peters17e17d42001-06-13 22:45:27 +0000273 # Much the same for unsigned.
274 code = self.unsigned_code
275 if self.unsigned_min <= x <= self.unsigned_max:
276 # Try big-endian.
277 format = ">" + code
278 expected = long(x)
279 expected = hex(expected)[2:-1] # chop "0x" and trailing 'L'
280 if len(expected) & 1:
281 expected = "0" + expected
282 expected = unhexlify(expected)
283 expected = "\x00" * (self.bytesize - len(expected)) + expected
Tim Peters7a3bfc32001-06-12 01:22:22 +0000284
Tim Peters17e17d42001-06-13 22:45:27 +0000285 # Pack work?
286 got = pack(format, x)
287 verify(got == expected,
288 "'%s'-pack of %r gave %r, not %r" %
289 (format, x, got, expected))
Tim Peters7a3bfc32001-06-12 01:22:22 +0000290
Tim Peters17e17d42001-06-13 22:45:27 +0000291 # Unpack work?
292 retrieved = unpack(format, got)[0]
293 verify(x == retrieved,
294 "'%s'-unpack of %r gave %r, not %r" %
295 (format, got, retrieved, x))
Tim Peters7a3bfc32001-06-12 01:22:22 +0000296
Tim Peters17e17d42001-06-13 22:45:27 +0000297 # Adding any byte should cause a "too big" error.
298 any_err(unpack, format, '\x01' + got)
Tim Peters7a3bfc32001-06-12 01:22:22 +0000299
Tim Peters17e17d42001-06-13 22:45:27 +0000300 # Try little-endian.
301 format = "<" + code
302 expected = string_reverse(expected)
Tim Peters7a3bfc32001-06-12 01:22:22 +0000303
Tim Peters17e17d42001-06-13 22:45:27 +0000304 # Pack work?
305 got = pack(format, x)
306 verify(got == expected,
307 "'%s'-pack of %r gave %r, not %r" %
308 (format, x, got, expected))
Tim Peters7a3bfc32001-06-12 01:22:22 +0000309
Tim Peters17e17d42001-06-13 22:45:27 +0000310 # Unpack work?
311 retrieved = unpack(format, got)[0]
312 verify(x == retrieved,
313 "'%s'-unpack of %r gave %r, not %r" %
314 (format, got, retrieved, x))
Tim Peters7a3bfc32001-06-12 01:22:22 +0000315
Tim Peters17e17d42001-06-13 22:45:27 +0000316 # Adding any byte should cause a "too big" error.
317 any_err(unpack, format, '\x01' + got)
Tim Peters7a3bfc32001-06-12 01:22:22 +0000318
Tim Peters17e17d42001-06-13 22:45:27 +0000319 else:
320 # x is out of range -- verify pack realizes that.
321 if code in self.BUGGY_RANGE_CHECK:
322 if verbose:
323 print "Skipping buggy range check for code", code
324 else:
325 any_err(pack, ">" + code, x)
326 any_err(pack, "<" + code, x)
Tim Peters7a3bfc32001-06-12 01:22:22 +0000327
Tim Peters17e17d42001-06-13 22:45:27 +0000328 def run(self):
329 from random import randrange
330
331 # Create all interesting powers of 2.
332 values = []
333 for exp in range(self.bitsize + 3):
334 values.append(1L << exp)
335
336 # Add some random values.
337 for i in range(self.bitsize):
338 val = 0L
339 for j in range(self.bytesize):
340 val = (val << 8) | randrange(256)
341 values.append(val)
342
343 # Try all those, and their negations, and +-1 from them. Note
344 # that this tests all power-of-2 boundaries in range, and a few out
345 # of range, plus +-(2**n +- 1).
346 for base in values:
347 for val in -base, base:
348 for incr in -1, 0, 1:
349 x = val + incr
350 try:
351 x = int(x)
352 except OverflowError:
353 pass
354 self.test_one(x)
355
356 # Some error cases.
357 for direction in "<>":
358 for code in self.formatpair:
359 for badobject in "a string", 3+42j, randrange:
360 any_err(struct.pack, direction + code, badobject)
361
362for args in [("bB", 1),
363 ("hH", 2),
364 ("iI", 4),
365 ("lL", 4),
366 ("qQ", 8)]:
367 t = IntTester(*args)
368 t.run()
Tim Peters0891ac02001-09-15 02:35:15 +0000369
370
371###########################################################################
372# The p ("Pascal string") code.
373
374def test_p_code():
375 for code, input, expected, expectedback in [
376 ('p','abc', '\x00', ''),
377 ('1p', 'abc', '\x00', ''),
378 ('2p', 'abc', '\x01a', 'a'),
379 ('3p', 'abc', '\x02ab', 'ab'),
380 ('4p', 'abc', '\x03abc', 'abc'),
381 ('5p', 'abc', '\x03abc\x00', 'abc'),
382 ('6p', 'abc', '\x03abc\x00\x00', 'abc'),
383 ('1000p', 'x'*1000, '\xff' + 'x'*999, 'x'*255)]:
384 got = struct.pack(code, input)
385 if got != expected:
386 raise TestFailed("pack(%r, %r) == %r but expected %r" %
387 (code, input, got, expected))
388 (got,) = struct.unpack(code, got)
389 if got != expectedback:
390 raise TestFailed("unpack(%r, %r) == %r but expected %r" %
391 (code, input, got, expectedback))
392
393test_p_code()
Tim Petersd50ade62003-03-20 18:32:13 +0000394
395
396###########################################################################
397# SF bug 705836. "<f" and ">f" had a severe rounding bug, where a carry
398# from the low-order discarded bits could propagate into the exponent
399# field, causing the result to be wrong by a factor of 2.
400
401def test_705836():
402 import math
403
404 for base in range(1, 33):
405 # smaller <- largest representable float less than base.
406 delta = 0.5
407 while base - delta / 2.0 != base:
408 delta /= 2.0
409 smaller = base - delta
410 # Packing this rounds away a solid string of trailing 1 bits.
411 packed = struct.pack("<f", smaller)
412 unpacked = struct.unpack("<f", packed)[0]
413 # This failed at base = 2, 4, and 32, with unpacked = 1, 2, and
414 # 16, respectively.
415 verify(base == unpacked)
416 bigpacked = struct.pack(">f", smaller)
417 verify(bigpacked == string_reverse(packed),
418 ">f pack should be byte-reversal of <f pack")
419 unpacked = struct.unpack(">f", bigpacked)[0]
420 verify(base == unpacked)
421
422 # Largest finite IEEE single.
423 big = (1 << 24) - 1
424 big = math.ldexp(big, 127 - 23)
425 packed = struct.pack(">f", big)
426 unpacked = struct.unpack(">f", packed)[0]
427 verify(big == unpacked)
428
429 # The same, but tack on a 1 bit so it rounds up to infinity.
430 big = (1 << 25) - 1
431 big = math.ldexp(big, 127 - 24)
432 try:
433 packed = struct.pack(">f", big)
434 except OverflowError:
435 pass
436 else:
437 TestFailed("expected OverflowError")
438
439test_705836()