0001 from test.test_support import TestFailed, verbose, verify 0002 import struct 0003 0004 import sys 0005 ISBIGENDIAN = sys.byteorder == "big" 0006 del sys 0007 verify((struct.pack('=i', 1)[0] == chr(0)) == ISBIGENDIAN, 0008 "bigendian determination appears wrong") 0009 0010 def string_reverse(s): 0011 chars = list(s) 0012 chars.reverse() 0013 return "".join(chars) 0014 0015 def bigendian_to_native(value): 0016 if ISBIGENDIAN: 0017 return value 0018 else: 0019 return string_reverse(value) 0020 0021 def simple_err(func, *args): 0022 try: 0023 func(*args) 0024 except struct.error: 0025 pass 0026 else: 0027 raise TestFailed, "%s%s did not raise struct.error" % ( 0028 func.__name__, args) 0029 0030 def any_err(func, *args): 0031 try: 0032 func(*args) 0033 except (struct.error, OverflowError, TypeError): 0034 pass 0035 else: 0036 raise TestFailed, "%s%s did not raise error" % ( 0037 func.__name__, args) 0038 0039 0040 simple_err(struct.calcsize, 'Z') 0041 0042 sz = struct.calcsize('i') 0043 if sz * 3 != struct.calcsize('iii'): 0044 raise TestFailed, 'inconsistent sizes' 0045 0046 fmt = 'cbxxxxxxhhhhiillffd' 0047 fmt3 = '3c3b18x12h6i6l6f3d' 0048 sz = struct.calcsize(fmt) 0049 sz3 = struct.calcsize(fmt3) 0050 if sz * 3 != sz3: 0051 raise TestFailed, 'inconsistent sizes (3*%r -> 3*%d = %d, %r -> %d)' % ( 0052 fmt, sz, 3*sz, fmt3, sz3) 0053 0054 simple_err(struct.pack, 'iii', 3) 0055 simple_err(struct.pack, 'i', 3, 3, 3) 0056 simple_err(struct.pack, 'i', 'foo') 0057 simple_err(struct.pack, 'P', 'foo') 0058 simple_err(struct.unpack, 'd', 'flap') 0059 s = struct.pack('ii', 1, 2) 0060 simple_err(struct.unpack, 'iii', s) 0061 simple_err(struct.unpack, 'i', s) 0062 0063 c = 'a' 0064 b = 1 0065 h = 255 0066 i = 65535 0067 l = 65536 0068 f = 3.1415 0069 d = 3.1415 0070 0071 for prefix in ('', '@', '<', '>', '=', '!'): 0072 for format in ('xcbhilfd', 'xcBHILfd'): 0073 format = prefix + format 0074 if verbose: 0075 print "trying:", format 0076 s = struct.pack(format, c, b, h, i, l, f, d) 0077 cp, bp, hp, ip, lp, fp, dp = struct.unpack(format, s) 0078 if (cp != c or bp != b or hp != h or ip != i or lp != l or 0079 int(100 * fp) != int(100 * f) or int(100 * dp) != int(100 * d)): 0080 # ^^^ calculate only to two decimal places 0081 raise TestFailed, "unpack/pack not transitive (%s, %s)" % ( 0082 str(format), str((cp, bp, hp, ip, lp, fp, dp))) 0083 0084 # Test some of the new features in detail 0085 0086 # (format, argument, big-endian result, little-endian result, asymmetric) 0087 tests = [ 0088 ('c', 'a', 'a', 'a', 0), 0089 ('xc', 'a', '\0a', '\0a', 0), 0090 ('cx', 'a', 'a\0', 'a\0', 0), 0091 ('s', 'a', 'a', 'a', 0), 0092 ('0s', 'helloworld', '', '', 1), 0093 ('1s', 'helloworld', 'h', 'h', 1), 0094 ('9s', 'helloworld', 'helloworl', 'helloworl', 1), 0095 ('10s', 'helloworld', 'helloworld', 'helloworld', 0), 0096 ('11s', 'helloworld', 'helloworld\0', 'helloworld\0', 1), 0097 ('20s', 'helloworld', 'helloworld'+10*'\0', 'helloworld'+10*'\0', 1), 0098 ('b', 7, '\7', '\7', 0), 0099 ('b', -7, '\371', '\371', 0), 0100 ('B', 7, '\7', '\7', 0), 0101 ('B', 249, '\371', '\371', 0), 0102 ('h', 700, '\002\274', '\274\002', 0), 0103 ('h', -700, '\375D', 'D\375', 0), 0104 ('H', 700, '\002\274', '\274\002', 0), 0105 ('H', 0x10000-700, '\375D', 'D\375', 0), 0106 ('i', 70000000, '\004,\035\200', '\200\035,\004', 0), 0107 ('i', -70000000, '\373\323\342\200', '\200\342\323\373', 0), 0108 ('I', 70000000L, '\004,\035\200', '\200\035,\004', 0), 0109 ('I', 0x100000000L-70000000, '\373\323\342\200', '\200\342\323\373', 0), 0110 ('l', 70000000, '\004,\035\200', '\200\035,\004', 0), 0111 ('l', -70000000, '\373\323\342\200', '\200\342\323\373', 0), 0112 ('L', 70000000L, '\004,\035\200', '\200\035,\004', 0), 0113 ('L', 0x100000000L-70000000, '\373\323\342\200', '\200\342\323\373', 0), 0114 ('f', 2.0, '@\000\000\000', '\000\000\000@', 0), 0115 ('d', 2.0, '@\000\000\000\000\000\000\000', 0116 '\000\000\000\000\000\000\000@', 0), 0117 ('f', -2.0, '\300\000\000\000', '\000\000\000\300', 0), 0118 ('d', -2.0, '\300\000\000\000\000\000\000\000', 0119 '\000\000\000\000\000\000\000\300', 0), 0120 ] 0121 0122 for fmt, arg, big, lil, asy in tests: 0123 if verbose: 0124 print "%r %r %r %r" % (fmt, arg, big, lil) 0125 for (xfmt, exp) in [('>'+fmt, big), ('!'+fmt, big), ('<'+fmt, lil), 0126 ('='+fmt, ISBIGENDIAN and big or lil)]: 0127 res = struct.pack(xfmt, arg) 0128 if res != exp: 0129 raise TestFailed, "pack(%r, %r) -> %r # expected %r" % ( 0130 fmt, arg, res, exp) 0131 n = struct.calcsize(xfmt) 0132 if n != len(res): 0133 raise TestFailed, "calcsize(%r) -> %d # expected %d" % ( 0134 xfmt, n, len(res)) 0135 rev = struct.unpack(xfmt, res)[0] 0136 if rev != arg and not asy: 0137 raise TestFailed, "unpack(%r, %r) -> (%r,) # expected (%r,)" % ( 0138 fmt, res, rev, arg) 0139 0140 ########################################################################### 0141 # Simple native q/Q tests. 0142 0143 has_native_qQ = 1 0144 try: 0145 struct.pack("q", 5) 0146 except struct.error: 0147 has_native_qQ = 0 0148 0149 if verbose: 0150 print "Platform has native q/Q?", has_native_qQ and "Yes." or "No." 0151 0152 any_err(struct.pack, "Q", -1) # can't pack -1 as unsigned regardless 0153 simple_err(struct.pack, "q", "a") # can't pack string as 'q' regardless 0154 simple_err(struct.pack, "Q", "a") # ditto, but 'Q' 0155 0156 def test_native_qQ(): 0157 bytes = struct.calcsize('q') 0158 # The expected values here are in big-endian format, primarily because 0159 # I'm on a little-endian machine and so this is the clearest way (for 0160 # me) to force the code to get exercised. 0161 for format, input, expected in ( 0162 ('q', -1, '\xff' * bytes), 0163 ('q', 0, '\x00' * bytes), 0164 ('Q', 0, '\x00' * bytes), 0165 ('q', 1L, '\x00' * (bytes-1) + '\x01'), 0166 ('Q', (1L << (8*bytes))-1, '\xff' * bytes), 0167 ('q', (1L << (8*bytes-1))-1, '\x7f' + '\xff' * (bytes - 1))): 0168 got = struct.pack(format, input) 0169 native_expected = bigendian_to_native(expected) 0170 verify(got == native_expected, 0171 "%r-pack of %r gave %r, not %r" % 0172 (format, input, got, native_expected)) 0173 retrieved = struct.unpack(format, got)[0] 0174 verify(retrieved == input, 0175 "%r-unpack of %r gave %r, not %r" % 0176 (format, got, retrieved, input)) 0177 0178 if has_native_qQ: 0179 test_native_qQ() 0180 0181 ########################################################################### 0182 # Standard integer tests (bBhHiIlLqQ). 0183 0184 import binascii 0185 0186 class IntTester: 0187 0188 # XXX Most std integer modes fail to test for out-of-range. 0189 # The "i" and "l" codes appear to range-check OK on 32-bit boxes, but 0190 # fail to check correctly on some 64-bit ones (Tru64 Unix + Compaq C 0191 # reported by Mark Favas). 0192 BUGGY_RANGE_CHECK = "bBhHiIlL" 0193 0194 def __init__(self, formatpair, bytesize): 0195 assert len(formatpair) == 2 0196 self.formatpair = formatpair 0197 for direction in "<>!=": 0198 for code in formatpair: 0199 format = direction + code 0200 verify(struct.calcsize(format) == bytesize) 0201 self.bytesize = bytesize 0202 self.bitsize = bytesize * 8 0203 self.signed_code, self.unsigned_code = formatpair 0204 self.unsigned_min = 0 0205 self.unsigned_max = 2L**self.bitsize - 1 0206 self.signed_min = -(2L**(self.bitsize-1)) 0207 self.signed_max = 2L**(self.bitsize-1) - 1 0208 0209 def test_one(self, x, pack=struct.pack, 0210 unpack=struct.unpack, 0211 unhexlify=binascii.unhexlify): 0212 if verbose: 0213 print "trying std", self.formatpair, "on", x, "==", hex(x) 0214 0215 # Try signed. 0216 code = self.signed_code 0217 if self.signed_min <= x <= self.signed_max: 0218 # Try big-endian. 0219 expected = long(x) 0220 if x < 0: 0221 expected += 1L << self.bitsize 0222 assert expected > 0 0223 expected = hex(expected)[2:-1] # chop "0x" and trailing 'L' 0224 if len(expected) & 1: 0225 expected = "0" + expected 0226 expected = unhexlify(expected) 0227 expected = "\x00" * (self.bytesize - len(expected)) + expected 0228 0229 # Pack work? 0230 format = ">" + code 0231 got = pack(format, x) 0232 verify(got == expected, 0233 "'%s'-pack of %r gave %r, not %r" % 0234 (format, x, got, expected)) 0235 0236 # Unpack work? 0237 retrieved = unpack(format, got)[0] 0238 verify(x == retrieved, 0239 "'%s'-unpack of %r gave %r, not %r" % 0240 (format, got, retrieved, x)) 0241 0242 # Adding any byte should cause a "too big" error. 0243 any_err(unpack, format, '\x01' + got) 0244 0245 # Try little-endian. 0246 format = "<" + code 0247 expected = string_reverse(expected) 0248 0249 # Pack work? 0250 got = pack(format, x) 0251 verify(got == expected, 0252 "'%s'-pack of %r gave %r, not %r" % 0253 (format, x, got, expected)) 0254 0255 # Unpack work? 0256 retrieved = unpack(format, got)[0] 0257 verify(x == retrieved, 0258 "'%s'-unpack of %r gave %r, not %r" % 0259 (format, got, retrieved, x)) 0260 0261 # Adding any byte should cause a "too big" error. 0262 any_err(unpack, format, '\x01' + got) 0263 0264 else: 0265 # x is out of range -- verify pack realizes that. 0266 if code in self.BUGGY_RANGE_CHECK: 0267 if verbose: 0268 print "Skipping buggy range check for code", code 0269 else: 0270 any_err(pack, ">" + code, x) 0271 any_err(pack, "<" + code, x) 0272 0273 # Much the same for unsigned. 0274 code = self.unsigned_code 0275 if self.unsigned_min <= x <= self.unsigned_max: 0276 # Try big-endian. 0277 format = ">" + code 0278 expected = long(x) 0279 expected = hex(expected)[2:-1] # chop "0x" and trailing 'L' 0280 if len(expected) & 1: 0281 expected = "0" + expected 0282 expected = unhexlify(expected) 0283 expected = "\x00" * (self.bytesize - len(expected)) + expected 0284 0285 # Pack work? 0286 got = pack(format, x) 0287 verify(got == expected, 0288 "'%s'-pack of %r gave %r, not %r" % 0289 (format, x, got, expected)) 0290 0291 # Unpack work? 0292 retrieved = unpack(format, got)[0] 0293 verify(x == retrieved, 0294 "'%s'-unpack of %r gave %r, not %r" % 0295 (format, got, retrieved, x)) 0296 0297 # Adding any byte should cause a "too big" error. 0298 any_err(unpack, format, '\x01' + got) 0299 0300 # Try little-endian. 0301 format = "<" + code 0302 expected = string_reverse(expected) 0303 0304 # Pack work? 0305 got = pack(format, x) 0306 verify(got == expected, 0307 "'%s'-pack of %r gave %r, not %r" % 0308 (format, x, got, expected)) 0309 0310 # Unpack work? 0311 retrieved = unpack(format, got)[0] 0312 verify(x == retrieved, 0313 "'%s'-unpack of %r gave %r, not %r" % 0314 (format, got, retrieved, x)) 0315 0316 # Adding any byte should cause a "too big" error. 0317 any_err(unpack, format, '\x01' + got) 0318 0319 else: 0320 # x is out of range -- verify pack realizes that. 0321 if code in self.BUGGY_RANGE_CHECK: 0322 if verbose: 0323 print "Skipping buggy range check for code", code 0324 else: 0325 any_err(pack, ">" + code, x) 0326 any_err(pack, "<" + code, x) 0327 0328 def run(self): 0329 from random import randrange 0330 0331 # Create all interesting powers of 2. 0332 values = [] 0333 for exp in range(self.bitsize + 3): 0334 values.append(1L << exp) 0335 0336 # Add some random values. 0337 for i in range(self.bitsize): 0338 val = 0L 0339 for j in range(self.bytesize): 0340 val = (val << 8) | randrange(256) 0341 values.append(val) 0342 0343 # Try all those, and their negations, and +-1 from them. Note 0344 # that this tests all power-of-2 boundaries in range, and a few out 0345 # of range, plus +-(2**n +- 1). 0346 for base in values: 0347 for val in -base, base: 0348 for incr in -1, 0, 1: 0349 x = val + incr 0350 try: 0351 x = int(x) 0352 except OverflowError: 0353 pass 0354 self.test_one(x) 0355 0356 # Some error cases. 0357 for direction in "<>": 0358 for code in self.formatpair: 0359 for badobject in "a string", 3+42j, randrange: 0360 any_err(struct.pack, direction + code, badobject) 0361 0362 for args in [("bB", 1), 0363 ("hH", 2), 0364 ("iI", 4), 0365 ("lL", 4), 0366 ("qQ", 8)]: 0367 t = IntTester(*args) 0368 t.run() 0369 0370 0371 ########################################################################### 0372 # The p ("Pascal string") code. 0373 0374 def test_p_code(): 0375 for code, input, expected, expectedback in [ 0376 ('p','abc', '\x00', ''), 0377 ('1p', 'abc', '\x00', ''), 0378 ('2p', 'abc', '\x01a', 'a'), 0379 ('3p', 'abc', '\x02ab', 'ab'), 0380 ('4p', 'abc', '\x03abc', 'abc'), 0381 ('5p', 'abc', '\x03abc\x00', 'abc'), 0382 ('6p', 'abc', '\x03abc\x00\x00', 'abc'), 0383 ('1000p', 'x'*1000, '\xff' + 'x'*999, 'x'*255)]: 0384 got = struct.pack(code, input) 0385 if got != expected: 0386 raise TestFailed("pack(%r, %r) == %r but expected %r" % 0387 (code, input, got, expected)) 0388 (got,) = struct.unpack(code, got) 0389 if got != expectedback: 0390 raise TestFailed("unpack(%r, %r) == %r but expected %r" % 0391 (code, input, got, expectedback)) 0392 0393 test_p_code() 0394 0395 0396 ########################################################################### 0397 # SF bug 705836. "<f" and ">f" had a severe rounding bug, where a carry 0398 # from the low-order discarded bits could propagate into the exponent 0399 # field, causing the result to be wrong by a factor of 2. 0400 0401 def test_705836(): 0402 import math 0403 0404 for base in range(1, 33): 0405 # smaller <- largest representable float less than base. 0406 delta = 0.5 0407 while base - delta / 2.0 != base: 0408 delta /= 2.0 0409 smaller = base - delta 0410 # Packing this rounds away a solid string of trailing 1 bits. 0411 packed = struct.pack("<f", smaller) 0412 unpacked = struct.unpack("<f", packed)[0] 0413 # This failed at base = 2, 4, and 32, with unpacked = 1, 2, and 0414 # 16, respectively. 0415 verify(base == unpacked) 0416 bigpacked = struct.pack(">f", smaller) 0417 verify(bigpacked == string_reverse(packed), 0418 ">f pack should be byte-reversal of <f pack") 0419 unpacked = struct.unpack(">f", bigpacked)[0] 0420 verify(base == unpacked) 0421 0422 # Largest finite IEEE single. 0423 big = (1 << 24) - 1 0424 big = math.ldexp(big, 127 - 23) 0425 packed = struct.pack(">f", big) 0426 unpacked = struct.unpack(">f", packed)[0] 0427 verify(big == unpacked) 0428 0429 # The same, but tack on a 1 bit so it rounds up to infinity. 0430 big = (1 << 25) - 1 0431 big = math.ldexp(big, 127 - 24) 0432 try: 0433 packed = struct.pack(">f", big) 0434 except OverflowError: 0435 pass 0436 else: 0437 TestFailed("expected OverflowError") 0438 0439 test_705836() 0440
Generated by PyXR 0.9.4