[Python-checkins] CVS: python/dist/src/Lib/test test_struct.py,1.9,1.10

Tim Peters tim_one@users.sourceforge.net
Mon, 11 Jun 2001 18:22:23 -0700


Update of /cvsroot/python/python/dist/src/Lib/test
In directory usw-pr-cvs1:/tmp/cvs-serv15300/python/dist/src/Lib/test

Modified Files:
	test_struct.py 
Log Message:
Added q/Q standard (x-platform 8-byte ints) mode in struct module.
This completes the q/Q project.

longobject.c _PyLong_AsByteArray:  The original code had a gross bug:
the most-significant Python digit doesn't necessarily have SHIFT
significant bits, and you really need to count how many copies of the sign
bit it has else spurious overflow errors result.

test_struct.py:  This now does exhaustive std q/Q testing at, and on both
sides of, all relevant power-of-2 boundaries, both positive and negative.

NEWS:  Added brief dict news while I was at it.


Index: test_struct.py
===================================================================
RCS file: /cvsroot/python/python/dist/src/Lib/test/test_struct.py,v
retrieving revision 1.9
retrieving revision 1.10
diff -C2 -r1.9 -r1.10
*** test_struct.py	2001/06/10 23:52:59	1.9
--- test_struct.py	2001/06/12 01:22:21	1.10
***************
*** 13,16 ****
--- 13,26 ----
  ##      pdb.set_trace()
  
+ def any_err(func, *args):
+     try:
+         apply(func, args)
+     except (struct.error, OverflowError, TypeError):
+         pass
+     else:
+         raise TestFailed, "%s%s did not raise error" % (
+             func.__name__, args)
+ ##      pdb.set_trace()
+ 
  simple_err(struct.calcsize, 'Z')
  
***************
*** 114,118 ****
                  `fmt`, `res`, `rev`, `arg`)
  
! # Some q/Q sanity checks.
  
  has_native_qQ = 1
--- 124,129 ----
                  `fmt`, `res`, `rev`, `arg`)
  
! ###########################################################################
! # q/Q tests.
  
  has_native_qQ = 1
***************
*** 125,140 ****
      print "Platform has native q/Q?", has_native_qQ and "Yes." or "No."
  
! simple_err(struct.pack, "Q", -1)   # can't pack -1 as unsigned regardless
  simple_err(struct.pack, "q", "a")  # can't pack string as 'q' regardless
  simple_err(struct.pack, "Q", "a")  # ditto, but 'Q'
  
  def bigendian_to_native(value):
      if isbigendian:
          return value
!     chars = list(value)
!     chars.reverse()
!     return "".join(chars)
  
! if has_native_qQ:
      bytes = struct.calcsize('q')
      # The expected values here are in big-endian format, primarily because
--- 136,155 ----
      print "Platform has native q/Q?", has_native_qQ and "Yes." or "No."
  
! any_err(struct.pack, "Q", -1)   # can't pack -1 as unsigned regardless
  simple_err(struct.pack, "q", "a")  # can't pack string as 'q' regardless
  simple_err(struct.pack, "Q", "a")  # ditto, but 'Q'
  
+ def string_reverse(s):
+     chars = list(s)
+     chars.reverse()
+     return "".join(chars)
+ 
  def bigendian_to_native(value):
      if isbigendian:
          return value
!     else:
!         return string_reverse(value)
  
! def test_native_qQ():
      bytes = struct.calcsize('q')
      # The expected values here are in big-endian format, primarily because
***************
*** 157,158 ****
--- 172,317 ----
                 "%r-unpack of %r gave %r, not %r" %
                      (format, got, retrieved, input))
+ 
+ if has_native_qQ:
+     test_native_qQ()
+ 
+ # Standard q/Q (8 bytes; should work on all platforms).
+ 
+ MIN_Q, MAX_Q = 0, 2L**64 - 1
+ MIN_q, MAX_q = -(2L**63), 2L**63 - 1
+ 
+ import binascii
+ def test_one_qQ(x, pack=struct.pack,
+                    unpack=struct.unpack,
+                    unhexlify=binascii.unhexlify):
+     if verbose:
+         print "trying std q/Q on", x, "==", hex(x)
+ 
+     # Try 'q'.
+     if MIN_q <= x <= MAX_q:
+         # Try '>q'.
+         expected = long(x)
+         if x < 0:
+             expected += 1L << 64
+             assert expected > 0
+         expected = hex(expected)[2:-1] # chop "0x" and trailing 'L'
+         if len(expected) & 1:
+             expected = "0" + expected
+         expected = unhexlify(expected)
+         expected = "\x00" * (8 - len(expected)) + expected
+ 
+         # >q pack work?
+         got = pack(">q", x)
+         verify(got == expected,
+                "'>q'-pack of %r gave %r, not %r" %
+                 (x, got, expected))
+ 
+         # >q unpack work?
+         retrieved = unpack(">q", got)[0]
+         verify(x == retrieved,
+                "'>q'-unpack of %r gave %r, not %r" %
+                 (got, retrieved, x))
+ 
+         # Adding any byte should cause a "too big" error.
+         any_err(unpack, ">q", '\x01' + got)
+ 
+         # Try '<q'.
+         expected = string_reverse(expected)
+ 
+         # <q pack work?
+         got = pack("<q", x)
+         verify(got == expected,
+                "'<q'-pack of %r gave %r, not %r" %
+                 (x, got, expected))
+ 
+         # <q unpack work?
+         retrieved = unpack("<q", got)[0]
+         verify(x == retrieved,
+                "'<q'-unpack of %r gave %r, not %r" %
+                 (got, retrieved, x))
+ 
+         # Adding any byte should cause a "too big" error.
+         any_err(unpack, "<q", '\x01' + got)
+ 
+     else:
+         # x is out of q's range -- verify pack realizes that.
+         any_err(pack, '>q', x)
+         any_err(pack, '<q', x)
+ 
+     # Much the same for 'Q'.
+     if MIN_Q <= x <= MAX_Q:
+         # Try '>Q'.
+         expected = long(x)
+         expected = hex(expected)[2:-1] # chop "0x" and trailing 'L'
+         if len(expected) & 1:
+             expected = "0" + expected
+         expected = unhexlify(expected)
+         expected = "\x00" * (8 - len(expected)) + expected
+ 
+         # >Q pack work?
+         got = pack(">Q", x)
+         verify(got == expected,
+                "'>Q'-pack of %r gave %r, not %r" %
+                 (x, got, expected))
+ 
+         # >Q unpack work?
+         retrieved = unpack(">Q", got)[0]
+         verify(x == retrieved,
+                "'>Q'-unpack of %r gave %r, not %r" %
+                 (got, retrieved, x))
+ 
+         # Adding any byte should cause a "too big" error.
+         any_err(unpack, ">Q", '\x01' + got)
+ 
+         # Try '<Q'.
+         expected = string_reverse(expected)
+ 
+         # <Q pack work?
+         got = pack("<Q", x)
+         verify(got == expected,
+                "'<Q'-pack of %r gave %r, not %r" %
+                 (x, got, expected))
+ 
+         # <Q unpack work?
+         retrieved = unpack("<Q", got)[0]
+         verify(x == retrieved,
+                "'<Q'-unpack of %r gave %r, not %r" %
+                 (got, retrieved, x))
+ 
+         # Adding any byte should cause a "too big" error.
+         any_err(unpack, "<Q", '\x01' + got)
+ 
+     else:
+         # x is out of Q's range -- verify pack realizes that.
+         any_err(pack, '>Q', x)
+         any_err(pack, '<Q', x)
+ 
+ def test_std_qQ():
+     from random import randrange
+ 
+     # Create all interesting powers of 2.
+     values = []
+     for exp in range(70):
+         values.append(1L << exp)
+ 
+     # Add some random 64-bit values.
+     for i in range(50):
+         val = 0L
+         for j in range(8):
+             val = (val << 8) | randrange(256)
+         values.append(val)
+ 
+     # Try all those, and their negations, and +-1 from them.  Note
+     # that this tests all power-of-2 boundaries in range, and a few out
+     # of range, plus +-(2**n +- 1).
+     for base in values:
+         for val in -base, base:
+             for incr in -1, 0, 1:
+                 x = val + incr
+                 try:
+                     x = int(x)
+                 except OverflowError:
+                     pass
+                 test_one_qQ(x)
+ 
+ test_std_qQ()