test.py 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500
  1. #-----------------------------------------------------------------------------
  2. # Copyright (c) 2010 Raymond L. Buvel
  3. # Copyright (c) 2010 Craig McQueen
  4. #
  5. # Permission is hereby granted, free of charge, to any person obtaining a copy
  6. # of this software and associated documentation files (the "Software"), to deal
  7. # in the Software without restriction, including without limitation the rights
  8. # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  9. # copies of the Software, and to permit persons to whom the Software is
  10. # furnished to do so, subject to the following conditions:
  11. #
  12. # The above copyright notice and this permission notice shall be included in
  13. # all copies or substantial portions of the Software.
  14. #
  15. # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  16. # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  17. # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  18. # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  19. # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  20. # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  21. # SOFTWARE.
  22. #-----------------------------------------------------------------------------
  23. '''Unit tests for crcmod functionality'''
  24. import unittest
  25. import binascii
  26. from crcmod import mkCrcFun, Crc
  27. try:
  28. from crcmod.crcmod import _usingExtension
  29. from crcmod.predefined import PredefinedCrc
  30. from crcmod.predefined import mkPredefinedCrcFun
  31. from crcmod.predefined import _crc_definitions as _predefined_crc_definitions
  32. except ImportError:
  33. from crcmod import _usingExtension
  34. from predefined import PredefinedCrc
  35. from predefined import mkPredefinedCrcFun
  36. from predefined import _crc_definitions as _predefined_crc_definitions
  37. #-----------------------------------------------------------------------------
  38. # This polynomial was chosen because it is the product of two irreducible
  39. # polynomials.
  40. # g8 = (x^7+x+1)*(x+1)
  41. g8 = 0x185
  42. #-----------------------------------------------------------------------------
  43. # The following reproduces all of the entries in the Numerical Recipes table.
  44. # This is the standard CCITT polynomial.
  45. g16 = 0x11021
  46. #-----------------------------------------------------------------------------
  47. g24 = 0x15D6DCB
  48. #-----------------------------------------------------------------------------
  49. # This is the standard AUTODIN-II polynomial which appears to be used in a
  50. # wide variety of standards and applications.
  51. g32 = 0x104C11DB7
  52. #-----------------------------------------------------------------------------
  53. # I was able to locate a couple of 64-bit polynomials on the web. To make it
  54. # easier to input the representation, define a function that builds a
  55. # polynomial from a list of the bits that need to be turned on.
  56. def polyFromBits(bits):
  57. p = 0L
  58. for n in bits:
  59. p = p | (1L << n)
  60. return p
  61. # The following is from the paper "An Improved 64-bit Cyclic Redundancy Check
  62. # for Protein Sequences" by David T. Jones
  63. g64a = polyFromBits([64, 63, 61, 59, 58, 56, 55, 52, 49, 48, 47, 46, 44, 41,
  64. 37, 36, 34, 32, 31, 28, 26, 23, 22, 19, 16, 13, 12, 10, 9, 6, 4,
  65. 3, 0])
  66. # The following is from Standard ECMA-182 "Data Interchange on 12,7 mm 48-Track
  67. # Magnetic Tape Cartridges -DLT1 Format-", December 1992.
  68. g64b = polyFromBits([64, 62, 57, 55, 54, 53, 52, 47, 46, 45, 40, 39, 38, 37,
  69. 35, 33, 32, 31, 29, 27, 24, 23, 22, 21, 19, 17, 13, 12, 10, 9, 7,
  70. 4, 1, 0])
  71. #-----------------------------------------------------------------------------
  72. # This class is used to check the CRC calculations against a direct
  73. # implementation using polynomial division.
  74. class poly:
  75. '''Class implementing polynomials over the field of integers mod 2'''
  76. def __init__(self,p):
  77. p = long(p)
  78. if p < 0: raise ValueError('invalid polynomial')
  79. self.p = p
  80. def __long__(self):
  81. return self.p
  82. def __eq__(self,other):
  83. return self.p == other.p
  84. def __ne__(self,other):
  85. return self.p != other.p
  86. # To allow sorting of polynomials, use their long integer form for
  87. # comparison
  88. def __cmp__(self,other):
  89. return cmp(self.p, other.p)
  90. def __nonzero__(self):
  91. return self.p != 0L
  92. def __neg__(self):
  93. return self # These polynomials are their own inverse under addition
  94. def __invert__(self):
  95. n = max(self.deg() + 1, 1)
  96. x = (1L << n) - 1
  97. return poly(self.p ^ x)
  98. def __add__(self,other):
  99. return poly(self.p ^ other.p)
  100. def __sub__(self,other):
  101. return poly(self.p ^ other.p)
  102. def __mul__(self,other):
  103. a = self.p
  104. b = other.p
  105. if a == 0 or b == 0: return poly(0)
  106. x = 0L
  107. while b:
  108. if b&1:
  109. x = x ^ a
  110. a = a<<1
  111. b = b>>1
  112. return poly(x)
  113. def __divmod__(self,other):
  114. u = self.p
  115. m = self.deg()
  116. v = other.p
  117. n = other.deg()
  118. if v == 0: raise ZeroDivisionError('polynomial division by zero')
  119. if n == 0: return (self,poly(0))
  120. if m < n: return (poly(0),self)
  121. k = m-n
  122. a = 1L << m
  123. v = v << k
  124. q = 0L
  125. while k > 0:
  126. if a & u:
  127. u = u ^ v
  128. q = q | 1L
  129. q = q << 1
  130. a = a >> 1
  131. v = v >> 1
  132. k -= 1
  133. if a & u:
  134. u = u ^ v
  135. q = q | 1L
  136. return (poly(q),poly(u))
  137. def __div__(self,other):
  138. return self.__divmod__(other)[0]
  139. def __mod__(self,other):
  140. return self.__divmod__(other)[1]
  141. def __repr__(self):
  142. return 'poly(0x%XL)' % self.p
  143. def __str__(self):
  144. p = self.p
  145. if p == 0: return '0'
  146. lst = { 0:[], 1:['1'], 2:['x'], 3:['1','x'] }[p&3]
  147. p = p>>2
  148. n = 2
  149. while p:
  150. if p&1: lst.append('x^%d' % n)
  151. p = p>>1
  152. n += 1
  153. lst.reverse()
  154. return '+'.join(lst)
  155. def deg(self):
  156. '''return the degree of the polynomial'''
  157. a = self.p
  158. if a == 0: return -1
  159. n = 0
  160. while a >= 0x10000L:
  161. n += 16
  162. a = a >> 16
  163. a = int(a)
  164. while a > 1:
  165. n += 1
  166. a = a >> 1
  167. return n
  168. #-----------------------------------------------------------------------------
  169. # The following functions compute the CRC using direct polynomial division.
  170. # These functions are checked against the result of the table driven
  171. # algorithms.
  172. g8p = poly(g8)
  173. x8p = poly(1L<<8)
  174. def crc8p(d):
  175. d = map(ord, d)
  176. p = 0L
  177. for i in d:
  178. p = p*256L + i
  179. p = poly(p)
  180. return long(p*x8p%g8p)
  181. g16p = poly(g16)
  182. x16p = poly(1L<<16)
  183. def crc16p(d):
  184. d = map(ord, d)
  185. p = 0L
  186. for i in d:
  187. p = p*256L + i
  188. p = poly(p)
  189. return long(p*x16p%g16p)
  190. g24p = poly(g24)
  191. x24p = poly(1L<<24)
  192. def crc24p(d):
  193. d = map(ord, d)
  194. p = 0L
  195. for i in d:
  196. p = p*256L + i
  197. p = poly(p)
  198. return long(p*x24p%g24p)
  199. g32p = poly(g32)
  200. x32p = poly(1L<<32)
  201. def crc32p(d):
  202. d = map(ord, d)
  203. p = 0L
  204. for i in d:
  205. p = p*256L + i
  206. p = poly(p)
  207. return long(p*x32p%g32p)
  208. g64ap = poly(g64a)
  209. x64p = poly(1L<<64)
  210. def crc64ap(d):
  211. d = map(ord, d)
  212. p = 0L
  213. for i in d:
  214. p = p*256L + i
  215. p = poly(p)
  216. return long(p*x64p%g64ap)
  217. g64bp = poly(g64b)
  218. def crc64bp(d):
  219. d = map(ord, d)
  220. p = 0L
  221. for i in d:
  222. p = p*256L + i
  223. p = poly(p)
  224. return long(p*x64p%g64bp)
  225. class KnownAnswerTests(unittest.TestCase):
  226. test_messages = [
  227. 'T',
  228. 'CatMouse987654321',
  229. ]
  230. known_answers = [
  231. [ (g8,0,0), (0xFE, 0x9D) ],
  232. [ (g8,-1,1), (0x4F, 0x9B) ],
  233. [ (g8,0,1), (0xFE, 0x62) ],
  234. [ (g16,0,0), (0x1A71, 0xE556) ],
  235. [ (g16,-1,1), (0x1B26, 0xF56E) ],
  236. [ (g16,0,1), (0x14A1, 0xC28D) ],
  237. [ (g24,0,0), (0xBCC49D, 0xC4B507) ],
  238. [ (g24,-1,1), (0x59BD0E, 0x0AAA37) ],
  239. [ (g24,0,1), (0xD52B0F, 0x1523AB) ],
  240. [ (g32,0,0), (0x6B93DDDB, 0x12DCA0F4) ],
  241. [ (g32,0xFFFFFFFFL,1), (0x41FB859FL, 0xF7B400A7L) ],
  242. [ (g32,0,1), (0x6C0695EDL, 0xC1A40EE5L) ],
  243. [ (g32,0,1,0xFFFFFFFF), (0xBE047A60L, 0x084BFF58L) ],
  244. ]
  245. def test_known_answers(self):
  246. for crcfun_params, v in self.known_answers:
  247. crcfun = mkCrcFun(*crcfun_params)
  248. self.assertEqual(crcfun('',0), 0, "Wrong answer for CRC parameters %s, input ''" % (crcfun_params,))
  249. for i, msg in enumerate(self.test_messages):
  250. self.assertEqual(crcfun(msg), v[i], "Wrong answer for CRC parameters %s, input '%s'" % (crcfun_params,msg))
  251. self.assertEqual(crcfun(msg[4:], crcfun(msg[:4])), v[i], "Wrong answer for CRC parameters %s, input '%s'" % (crcfun_params,msg))
  252. self.assertEqual(crcfun(msg[-1:], crcfun(msg[:-1])), v[i], "Wrong answer for CRC parameters %s, input '%s'" % (crcfun_params,msg))
  253. class CompareReferenceCrcTest(unittest.TestCase):
  254. test_messages = [
  255. '',
  256. 'T',
  257. '123456789',
  258. 'CatMouse987654321',
  259. ]
  260. test_poly_crcs = [
  261. [ (g8,0,0), crc8p ],
  262. [ (g16,0,0), crc16p ],
  263. [ (g24,0,0), crc24p ],
  264. [ (g32,0,0), crc32p ],
  265. [ (g64a,0,0), crc64ap ],
  266. [ (g64b,0,0), crc64bp ],
  267. ]
  268. @staticmethod
  269. def reference_crc32(d, crc=0):
  270. """This function modifies the return value of binascii.crc32
  271. to be an unsigned 32-bit value. I.e. in the range 0 to 2**32-1."""
  272. # Work around the future warning on constants.
  273. if crc > 0x7FFFFFFFL:
  274. x = int(crc & 0x7FFFFFFFL)
  275. crc = x | -2147483648
  276. x = binascii.crc32(d,crc)
  277. return long(x) & 0xFFFFFFFFL
  278. def test_compare_crc32(self):
  279. """The binascii module has a 32-bit CRC function that is used in a wide range
  280. of applications including the checksum used in the ZIP file format.
  281. This test compares the CRC-32 implementation of this crcmod module to
  282. that of binascii.crc32."""
  283. # The following function should produce the same result as
  284. # self.reference_crc32 which is derived from binascii.crc32.
  285. crc32 = mkCrcFun(g32,0,1,0xFFFFFFFF)
  286. for msg in self.test_messages:
  287. self.assertEqual(crc32(msg), self.reference_crc32(msg))
  288. def test_compare_poly(self):
  289. """Compare various CRCs of this crcmod module to a pure
  290. polynomial-based implementation."""
  291. for crcfun_params, crc_poly_fun in self.test_poly_crcs:
  292. # The following function should produce the same result as
  293. # the associated polynomial CRC function.
  294. crcfun = mkCrcFun(*crcfun_params)
  295. for msg in self.test_messages:
  296. self.assertEqual(crcfun(msg), crc_poly_fun(msg))
  297. class CrcClassTest(unittest.TestCase):
  298. """Verify the Crc class"""
  299. msg = 'CatMouse987654321'
  300. def test_simple_crc32_class(self):
  301. """Verify the CRC class when not using xorOut"""
  302. crc = Crc(g32)
  303. str_rep = \
  304. '''poly = 0x104C11DB7
  305. reverse = True
  306. initCrc = 0xFFFFFFFF
  307. xorOut = 0x00000000
  308. crcValue = 0xFFFFFFFF'''
  309. self.assertEqual(str(crc), str_rep)
  310. self.assertEqual(crc.digest(), '\xff\xff\xff\xff')
  311. self.assertEqual(crc.hexdigest(), 'FFFFFFFF')
  312. crc.update(self.msg)
  313. self.assertEqual(crc.crcValue, 0xF7B400A7L)
  314. self.assertEqual(crc.digest(), '\xf7\xb4\x00\xa7')
  315. self.assertEqual(crc.hexdigest(), 'F7B400A7')
  316. # Verify the .copy() method
  317. x = crc.copy()
  318. self.assertTrue(x is not crc)
  319. str_rep = \
  320. '''poly = 0x104C11DB7
  321. reverse = True
  322. initCrc = 0xFFFFFFFF
  323. xorOut = 0x00000000
  324. crcValue = 0xF7B400A7'''
  325. self.assertEqual(str(crc), str_rep)
  326. self.assertEqual(str(x), str_rep)
  327. def test_full_crc32_class(self):
  328. """Verify the CRC class when using xorOut"""
  329. crc = Crc(g32, initCrc=0, xorOut= ~0L)
  330. str_rep = \
  331. '''poly = 0x104C11DB7
  332. reverse = True
  333. initCrc = 0x00000000
  334. xorOut = 0xFFFFFFFF
  335. crcValue = 0x00000000'''
  336. self.assertEqual(str(crc), str_rep)
  337. self.assertEqual(crc.digest(), '\x00\x00\x00\x00')
  338. self.assertEqual(crc.hexdigest(), '00000000')
  339. crc.update(self.msg)
  340. self.assertEqual(crc.crcValue, 0x84BFF58L)
  341. self.assertEqual(crc.digest(), '\x08\x4b\xff\x58')
  342. self.assertEqual(crc.hexdigest(), '084BFF58')
  343. # Verify the .copy() method
  344. x = crc.copy()
  345. self.assertTrue(x is not crc)
  346. str_rep = \
  347. '''poly = 0x104C11DB7
  348. reverse = True
  349. initCrc = 0x00000000
  350. xorOut = 0xFFFFFFFF
  351. crcValue = 0x084BFF58'''
  352. self.assertEqual(str(crc), str_rep)
  353. self.assertEqual(str(x), str_rep)
  354. # Verify the .new() method
  355. y = crc.new()
  356. self.assertTrue(y is not crc)
  357. self.assertTrue(y is not x)
  358. str_rep = \
  359. '''poly = 0x104C11DB7
  360. reverse = True
  361. initCrc = 0x00000000
  362. xorOut = 0xFFFFFFFF
  363. crcValue = 0x00000000'''
  364. self.assertEqual(str(y), str_rep)
  365. class PredefinedCrcTest(unittest.TestCase):
  366. """Verify the predefined CRCs"""
  367. test_messages_for_known_answers = [
  368. '', # Test cases below depend on this first entry being the empty string.
  369. 'T',
  370. 'CatMouse987654321',
  371. ]
  372. known_answers = [
  373. [ 'crc-aug-ccitt', (0x1D0F, 0xD6ED, 0x5637) ],
  374. [ 'x-25', (0x0000, 0xE4D9, 0x0A91) ],
  375. [ 'crc-32', (0x00000000, 0xBE047A60, 0x084BFF58) ],
  376. ]
  377. def test_known_answers(self):
  378. for crcfun_name, v in self.known_answers:
  379. crcfun = mkPredefinedCrcFun(crcfun_name)
  380. self.assertEqual(crcfun('',0), 0, "Wrong answer for CRC '%s', input ''" % crcfun_name)
  381. for i, msg in enumerate(self.test_messages_for_known_answers):
  382. self.assertEqual(crcfun(msg), v[i], "Wrong answer for CRC %s, input '%s'" % (crcfun_name,msg))
  383. self.assertEqual(crcfun(msg[4:], crcfun(msg[:4])), v[i], "Wrong answer for CRC %s, input '%s'" % (crcfun_name,msg))
  384. self.assertEqual(crcfun(msg[-1:], crcfun(msg[:-1])), v[i], "Wrong answer for CRC %s, input '%s'" % (crcfun_name,msg))
  385. def test_class_with_known_answers(self):
  386. for crcfun_name, v in self.known_answers:
  387. for i, msg in enumerate(self.test_messages_for_known_answers):
  388. crc1 = PredefinedCrc(crcfun_name)
  389. crc1.update(msg)
  390. self.assertEqual(crc1.crcValue, v[i], "Wrong answer for crc1 %s, input '%s'" % (crcfun_name,msg))
  391. crc2 = crc1.new()
  392. # Check that crc1 maintains its same value, after .new() call.
  393. self.assertEqual(crc1.crcValue, v[i], "Wrong state for crc1 %s, input '%s'" % (crcfun_name,msg))
  394. # Check that the new class instance created by .new() contains the initialisation value.
  395. # This depends on the first string in self.test_messages_for_known_answers being
  396. # the empty string.
  397. self.assertEqual(crc2.crcValue, v[0], "Wrong state for crc2 %s, input '%s'" % (crcfun_name,msg))
  398. crc2.update(msg)
  399. # Check that crc1 maintains its same value, after crc2 has called .update()
  400. self.assertEqual(crc1.crcValue, v[i], "Wrong state for crc1 %s, input '%s'" % (crcfun_name,msg))
  401. # Check that crc2 contains the right value after calling .update()
  402. self.assertEqual(crc2.crcValue, v[i], "Wrong state for crc2 %s, input '%s'" % (crcfun_name,msg))
  403. def test_function_predefined_table(self):
  404. for table_entry in _predefined_crc_definitions:
  405. # Check predefined function
  406. crc_func = mkPredefinedCrcFun(table_entry['name'])
  407. calc_value = crc_func("123456789")
  408. self.assertEqual(calc_value, table_entry['check'], "Wrong answer for CRC '%s'" % table_entry['name'])
  409. def test_class_predefined_table(self):
  410. for table_entry in _predefined_crc_definitions:
  411. # Check predefined class
  412. crc1 = PredefinedCrc(table_entry['name'])
  413. crc1.update("123456789")
  414. self.assertEqual(crc1.crcValue, table_entry['check'], "Wrong answer for CRC '%s'" % table_entry['name'])
  415. def runtests():
  416. print "Using extension:", _usingExtension
  417. print
  418. unittest.main()
  419. if __name__ == '__main__':
  420. runtests()