Move setting of ioready 'wait' earlier in call chain, to
[python/dscho.git] / Lib / test / test_struct.py
blobbc0ace8525faa4a7add6940f4572cf571627dba3
1 from test.test_support import TestFailed, verbose, verify
2 import struct
4 import sys
5 ISBIGENDIAN = sys.byteorder == "big"
6 del sys
7 verify((struct.pack('=i', 1)[0] == chr(0)) == ISBIGENDIAN,
8 "bigendian determination appears wrong")
10 def string_reverse(s):
11 chars = list(s)
12 chars.reverse()
13 return "".join(chars)
15 def bigendian_to_native(value):
16 if ISBIGENDIAN:
17 return value
18 else:
19 return string_reverse(value)
21 def simple_err(func, *args):
22 try:
23 func(*args)
24 except struct.error:
25 pass
26 else:
27 raise TestFailed, "%s%s did not raise struct.error" % (
28 func.__name__, args)
30 def any_err(func, *args):
31 try:
32 func(*args)
33 except (struct.error, OverflowError, TypeError):
34 pass
35 else:
36 raise TestFailed, "%s%s did not raise error" % (
37 func.__name__, args)
40 simple_err(struct.calcsize, 'Z')
42 sz = struct.calcsize('i')
43 if sz * 3 != struct.calcsize('iii'):
44 raise TestFailed, 'inconsistent sizes'
46 fmt = 'cbxxxxxxhhhhiillffd'
47 fmt3 = '3c3b18x12h6i6l6f3d'
48 sz = struct.calcsize(fmt)
49 sz3 = struct.calcsize(fmt3)
50 if sz * 3 != sz3:
51 raise TestFailed, 'inconsistent sizes (3*%s -> 3*%d = %d, %s -> %d)' % (
52 `fmt`, sz, 3*sz, `fmt3`, sz3)
54 simple_err(struct.pack, 'iii', 3)
55 simple_err(struct.pack, 'i', 3, 3, 3)
56 simple_err(struct.pack, 'i', 'foo')
57 simple_err(struct.unpack, 'd', 'flap')
58 s = struct.pack('ii', 1, 2)
59 simple_err(struct.unpack, 'iii', s)
60 simple_err(struct.unpack, 'i', s)
62 c = 'a'
63 b = 1
64 h = 255
65 i = 65535
66 l = 65536
67 f = 3.1415
68 d = 3.1415
70 for prefix in ('', '@', '<', '>', '=', '!'):
71 for format in ('xcbhilfd', 'xcBHILfd'):
72 format = prefix + format
73 if verbose:
74 print "trying:", format
75 s = struct.pack(format, c, b, h, i, l, f, d)
76 cp, bp, hp, ip, lp, fp, dp = struct.unpack(format, s)
77 if (cp != c or bp != b or hp != h or ip != i or lp != l or
78 int(100 * fp) != int(100 * f) or int(100 * dp) != int(100 * d)):
79 # ^^^ calculate only to two decimal places
80 raise TestFailed, "unpack/pack not transitive (%s, %s)" % (
81 str(format), str((cp, bp, hp, ip, lp, fp, dp)))
83 # Test some of the new features in detail
85 # (format, argument, big-endian result, little-endian result, asymmetric)
86 tests = [
87 ('c', 'a', 'a', 'a', 0),
88 ('xc', 'a', '\0a', '\0a', 0),
89 ('cx', 'a', 'a\0', 'a\0', 0),
90 ('s', 'a', 'a', 'a', 0),
91 ('0s', 'helloworld', '', '', 1),
92 ('1s', 'helloworld', 'h', 'h', 1),
93 ('9s', 'helloworld', 'helloworl', 'helloworl', 1),
94 ('10s', 'helloworld', 'helloworld', 'helloworld', 0),
95 ('11s', 'helloworld', 'helloworld\0', 'helloworld\0', 1),
96 ('20s', 'helloworld', 'helloworld'+10*'\0', 'helloworld'+10*'\0', 1),
97 ('b', 7, '\7', '\7', 0),
98 ('b', -7, '\371', '\371', 0),
99 ('B', 7, '\7', '\7', 0),
100 ('B', 249, '\371', '\371', 0),
101 ('h', 700, '\002\274', '\274\002', 0),
102 ('h', -700, '\375D', 'D\375', 0),
103 ('H', 700, '\002\274', '\274\002', 0),
104 ('H', 0x10000-700, '\375D', 'D\375', 0),
105 ('i', 70000000, '\004,\035\200', '\200\035,\004', 0),
106 ('i', -70000000, '\373\323\342\200', '\200\342\323\373', 0),
107 ('I', 70000000L, '\004,\035\200', '\200\035,\004', 0),
108 ('I', 0x100000000L-70000000, '\373\323\342\200', '\200\342\323\373', 0),
109 ('l', 70000000, '\004,\035\200', '\200\035,\004', 0),
110 ('l', -70000000, '\373\323\342\200', '\200\342\323\373', 0),
111 ('L', 70000000L, '\004,\035\200', '\200\035,\004', 0),
112 ('L', 0x100000000L-70000000, '\373\323\342\200', '\200\342\323\373', 0),
113 ('f', 2.0, '@\000\000\000', '\000\000\000@', 0),
114 ('d', 2.0, '@\000\000\000\000\000\000\000',
115 '\000\000\000\000\000\000\000@', 0),
116 ('f', -2.0, '\300\000\000\000', '\000\000\000\300', 0),
117 ('d', -2.0, '\300\000\000\000\000\000\000\000',
118 '\000\000\000\000\000\000\000\300', 0),
121 for fmt, arg, big, lil, asy in tests:
122 if verbose:
123 print `fmt`, `arg`, `big`, `lil`
124 for (xfmt, exp) in [('>'+fmt, big), ('!'+fmt, big), ('<'+fmt, lil),
125 ('='+fmt, ISBIGENDIAN and big or lil)]:
126 res = struct.pack(xfmt, arg)
127 if res != exp:
128 raise TestFailed, "pack(%s, %s) -> %s # expected %s" % (
129 `fmt`, `arg`, `res`, `exp`)
130 n = struct.calcsize(xfmt)
131 if n != len(res):
132 raise TestFailed, "calcsize(%s) -> %d # expected %d" % (
133 `xfmt`, n, len(res))
134 rev = struct.unpack(xfmt, res)[0]
135 if rev != arg and not asy:
136 raise TestFailed, "unpack(%s, %s) -> (%s,) # expected (%s,)" % (
137 `fmt`, `res`, `rev`, `arg`)
139 ###########################################################################
140 # Simple native q/Q tests.
142 has_native_qQ = 1
143 try:
144 struct.pack("q", 5)
145 except struct.error:
146 has_native_qQ = 0
148 if verbose:
149 print "Platform has native q/Q?", has_native_qQ and "Yes." or "No."
151 any_err(struct.pack, "Q", -1) # can't pack -1 as unsigned regardless
152 simple_err(struct.pack, "q", "a") # can't pack string as 'q' regardless
153 simple_err(struct.pack, "Q", "a") # ditto, but 'Q'
155 def test_native_qQ():
156 bytes = struct.calcsize('q')
157 # The expected values here are in big-endian format, primarily because
158 # I'm on a little-endian machine and so this is the clearest way (for
159 # me) to force the code to get exercised.
160 for format, input, expected in (
161 ('q', -1, '\xff' * bytes),
162 ('q', 0, '\x00' * bytes),
163 ('Q', 0, '\x00' * bytes),
164 ('q', 1L, '\x00' * (bytes-1) + '\x01'),
165 ('Q', (1L << (8*bytes))-1, '\xff' * bytes),
166 ('q', (1L << (8*bytes-1))-1, '\x7f' + '\xff' * (bytes - 1))):
167 got = struct.pack(format, input)
168 native_expected = bigendian_to_native(expected)
169 verify(got == native_expected,
170 "%r-pack of %r gave %r, not %r" %
171 (format, input, got, native_expected))
172 retrieved = struct.unpack(format, got)[0]
173 verify(retrieved == input,
174 "%r-unpack of %r gave %r, not %r" %
175 (format, got, retrieved, input))
177 if has_native_qQ:
178 test_native_qQ()
180 ###########################################################################
181 # Standard integer tests (bBhHiIlLqQ).
183 import binascii
185 class IntTester:
187 # XXX Most std integer modes fail to test for out-of-range.
188 # The "i" and "l" codes appear to range-check OK on 32-bit boxes, but
189 # fail to check correctly on some 64-bit ones (Tru64 Unix + Compaq C
190 # reported by Mark Favas).
191 BUGGY_RANGE_CHECK = "bBhHiIlL"
193 def __init__(self, formatpair, bytesize):
194 assert len(formatpair) == 2
195 self.formatpair = formatpair
196 for direction in "<>!=":
197 for code in formatpair:
198 format = direction + code
199 verify(struct.calcsize(format) == bytesize)
200 self.bytesize = bytesize
201 self.bitsize = bytesize * 8
202 self.signed_code, self.unsigned_code = formatpair
203 self.unsigned_min = 0
204 self.unsigned_max = 2L**self.bitsize - 1
205 self.signed_min = -(2L**(self.bitsize-1))
206 self.signed_max = 2L**(self.bitsize-1) - 1
208 def test_one(self, x, pack=struct.pack,
209 unpack=struct.unpack,
210 unhexlify=binascii.unhexlify):
211 if verbose:
212 print "trying std", self.formatpair, "on", x, "==", hex(x)
214 # Try signed.
215 code = self.signed_code
216 if self.signed_min <= x <= self.signed_max:
217 # Try big-endian.
218 expected = long(x)
219 if x < 0:
220 expected += 1L << self.bitsize
221 assert expected > 0
222 expected = hex(expected)[2:-1] # chop "0x" and trailing 'L'
223 if len(expected) & 1:
224 expected = "0" + expected
225 expected = unhexlify(expected)
226 expected = "\x00" * (self.bytesize - len(expected)) + expected
228 # Pack work?
229 format = ">" + code
230 got = pack(format, x)
231 verify(got == expected,
232 "'%s'-pack of %r gave %r, not %r" %
233 (format, x, got, expected))
235 # Unpack work?
236 retrieved = unpack(format, got)[0]
237 verify(x == retrieved,
238 "'%s'-unpack of %r gave %r, not %r" %
239 (format, got, retrieved, x))
241 # Adding any byte should cause a "too big" error.
242 any_err(unpack, format, '\x01' + got)
244 # Try little-endian.
245 format = "<" + code
246 expected = string_reverse(expected)
248 # Pack work?
249 got = pack(format, x)
250 verify(got == expected,
251 "'%s'-pack of %r gave %r, not %r" %
252 (format, x, got, expected))
254 # Unpack work?
255 retrieved = unpack(format, got)[0]
256 verify(x == retrieved,
257 "'%s'-unpack of %r gave %r, not %r" %
258 (format, got, retrieved, x))
260 # Adding any byte should cause a "too big" error.
261 any_err(unpack, format, '\x01' + got)
263 else:
264 # x is out of range -- verify pack realizes that.
265 if code in self.BUGGY_RANGE_CHECK:
266 if verbose:
267 print "Skipping buggy range check for code", code
268 else:
269 any_err(pack, ">" + code, x)
270 any_err(pack, "<" + code, x)
272 # Much the same for unsigned.
273 code = self.unsigned_code
274 if self.unsigned_min <= x <= self.unsigned_max:
275 # Try big-endian.
276 format = ">" + code
277 expected = long(x)
278 expected = hex(expected)[2:-1] # chop "0x" and trailing 'L'
279 if len(expected) & 1:
280 expected = "0" + expected
281 expected = unhexlify(expected)
282 expected = "\x00" * (self.bytesize - len(expected)) + expected
284 # Pack work?
285 got = pack(format, x)
286 verify(got == expected,
287 "'%s'-pack of %r gave %r, not %r" %
288 (format, x, got, expected))
290 # Unpack work?
291 retrieved = unpack(format, got)[0]
292 verify(x == retrieved,
293 "'%s'-unpack of %r gave %r, not %r" %
294 (format, got, retrieved, x))
296 # Adding any byte should cause a "too big" error.
297 any_err(unpack, format, '\x01' + got)
299 # Try little-endian.
300 format = "<" + code
301 expected = string_reverse(expected)
303 # Pack work?
304 got = pack(format, x)
305 verify(got == expected,
306 "'%s'-pack of %r gave %r, not %r" %
307 (format, x, got, expected))
309 # Unpack work?
310 retrieved = unpack(format, got)[0]
311 verify(x == retrieved,
312 "'%s'-unpack of %r gave %r, not %r" %
313 (format, got, retrieved, x))
315 # Adding any byte should cause a "too big" error.
316 any_err(unpack, format, '\x01' + got)
318 else:
319 # x is out of range -- verify pack realizes that.
320 if code in self.BUGGY_RANGE_CHECK:
321 if verbose:
322 print "Skipping buggy range check for code", code
323 else:
324 any_err(pack, ">" + code, x)
325 any_err(pack, "<" + code, x)
327 def run(self):
328 from random import randrange
330 # Create all interesting powers of 2.
331 values = []
332 for exp in range(self.bitsize + 3):
333 values.append(1L << exp)
335 # Add some random values.
336 for i in range(self.bitsize):
337 val = 0L
338 for j in range(self.bytesize):
339 val = (val << 8) | randrange(256)
340 values.append(val)
342 # Try all those, and their negations, and +-1 from them. Note
343 # that this tests all power-of-2 boundaries in range, and a few out
344 # of range, plus +-(2**n +- 1).
345 for base in values:
346 for val in -base, base:
347 for incr in -1, 0, 1:
348 x = val + incr
349 try:
350 x = int(x)
351 except OverflowError:
352 pass
353 self.test_one(x)
355 # Some error cases.
356 for direction in "<>":
357 for code in self.formatpair:
358 for badobject in "a string", 3+42j, randrange:
359 any_err(struct.pack, direction + code, badobject)
361 for args in [("bB", 1),
362 ("hH", 2),
363 ("iI", 4),
364 ("lL", 4),
365 ("qQ", 8)]:
366 t = IntTester(*args)
367 t.run()
370 ###########################################################################
371 # The p ("Pascal string") code.
373 def test_p_code():
374 for code, input, expected, expectedback in [
375 ('p','abc', '\x00', ''),
376 ('1p', 'abc', '\x00', ''),
377 ('2p', 'abc', '\x01a', 'a'),
378 ('3p', 'abc', '\x02ab', 'ab'),
379 ('4p', 'abc', '\x03abc', 'abc'),
380 ('5p', 'abc', '\x03abc\x00', 'abc'),
381 ('6p', 'abc', '\x03abc\x00\x00', 'abc'),
382 ('1000p', 'x'*1000, '\xff' + 'x'*999, 'x'*255)]:
383 got = struct.pack(code, input)
384 if got != expected:
385 raise TestFailed("pack(%r, %r) == %r but expected %r" %
386 (code, input, got, expected))
387 (got,) = struct.unpack(code, got)
388 if got != expectedback:
389 raise TestFailed("unpack(%r, %r) == %r but expected %r" %
390 (code, input, got, expectedback))
392 test_p_code()