/Lib/bsddb/test/test_compare.py

http://unladen-swallow.googlecode.com/ · Python · 257 lines · 199 code · 43 blank · 15 comment · 23 complexity · b3f1e3cb3fd4aa4c6e833082e32850d7 MD5 · raw file

  1. """
  2. TestCases for python DB Btree key comparison function.
  3. """
  4. import sys, os, re
  5. import test_all
  6. from cStringIO import StringIO
  7. import unittest
  8. from test_all import db, dbshelve, test_support, \
  9. get_new_environment_path, get_new_database_path
  10. lexical_cmp = cmp
  11. def lowercase_cmp(left, right):
  12. return cmp (left.lower(), right.lower())
  13. def make_reverse_comparator (cmp):
  14. def reverse (left, right, delegate=cmp):
  15. return - delegate (left, right)
  16. return reverse
  17. _expected_lexical_test_data = ['', 'CCCP', 'a', 'aaa', 'b', 'c', 'cccce', 'ccccf']
  18. _expected_lowercase_test_data = ['', 'a', 'aaa', 'b', 'c', 'CC', 'cccce', 'ccccf', 'CCCP']
  19. class ComparatorTests (unittest.TestCase):
  20. def comparator_test_helper (self, comparator, expected_data):
  21. data = expected_data[:]
  22. import sys
  23. if sys.version_info[0] < 3 :
  24. if sys.version_info[:3] < (2, 4, 0):
  25. data.sort(comparator)
  26. else :
  27. data.sort(cmp=comparator)
  28. else : # Insertion Sort. Please, improve
  29. data2 = []
  30. for i in data :
  31. for j, k in enumerate(data2) :
  32. r = comparator(k, i)
  33. if r == 1 :
  34. data2.insert(j, i)
  35. break
  36. else :
  37. data2.append(i)
  38. data = data2
  39. self.failUnless (data == expected_data,
  40. "comparator `%s' is not right: %s vs. %s"
  41. % (comparator, expected_data, data))
  42. def test_lexical_comparator (self):
  43. self.comparator_test_helper (lexical_cmp, _expected_lexical_test_data)
  44. def test_reverse_lexical_comparator (self):
  45. rev = _expected_lexical_test_data[:]
  46. rev.reverse ()
  47. self.comparator_test_helper (make_reverse_comparator (lexical_cmp),
  48. rev)
  49. def test_lowercase_comparator (self):
  50. self.comparator_test_helper (lowercase_cmp,
  51. _expected_lowercase_test_data)
  52. class AbstractBtreeKeyCompareTestCase (unittest.TestCase):
  53. env = None
  54. db = None
  55. def setUp (self):
  56. self.filename = self.__class__.__name__ + '.db'
  57. self.homeDir = get_new_environment_path()
  58. env = db.DBEnv()
  59. env.open (self.homeDir,
  60. db.DB_CREATE | db.DB_INIT_MPOOL
  61. | db.DB_INIT_LOCK | db.DB_THREAD)
  62. self.env = env
  63. def tearDown (self):
  64. self.closeDB()
  65. if self.env is not None:
  66. self.env.close()
  67. self.env = None
  68. test_support.rmtree(self.homeDir)
  69. def addDataToDB (self, data):
  70. i = 0
  71. for item in data:
  72. self.db.put (item, str (i))
  73. i = i + 1
  74. def createDB (self, key_comparator):
  75. self.db = db.DB (self.env)
  76. self.setupDB (key_comparator)
  77. self.db.open (self.filename, "test", db.DB_BTREE, db.DB_CREATE)
  78. def setupDB (self, key_comparator):
  79. self.db.set_bt_compare (key_comparator)
  80. def closeDB (self):
  81. if self.db is not None:
  82. self.db.close ()
  83. self.db = None
  84. def startTest (self):
  85. pass
  86. def finishTest (self, expected = None):
  87. if expected is not None:
  88. self.check_results (expected)
  89. self.closeDB ()
  90. def check_results (self, expected):
  91. curs = self.db.cursor ()
  92. try:
  93. index = 0
  94. rec = curs.first ()
  95. while rec:
  96. key, ignore = rec
  97. self.failUnless (index < len (expected),
  98. "to many values returned from cursor")
  99. self.failUnless (expected[index] == key,
  100. "expected value `%s' at %d but got `%s'"
  101. % (expected[index], index, key))
  102. index = index + 1
  103. rec = curs.next ()
  104. self.failUnless (index == len (expected),
  105. "not enough values returned from cursor")
  106. finally:
  107. curs.close ()
  108. class BtreeKeyCompareTestCase (AbstractBtreeKeyCompareTestCase):
  109. def runCompareTest (self, comparator, data):
  110. self.startTest ()
  111. self.createDB (comparator)
  112. self.addDataToDB (data)
  113. self.finishTest (data)
  114. def test_lexical_ordering (self):
  115. self.runCompareTest (lexical_cmp, _expected_lexical_test_data)
  116. def test_reverse_lexical_ordering (self):
  117. expected_rev_data = _expected_lexical_test_data[:]
  118. expected_rev_data.reverse ()
  119. self.runCompareTest (make_reverse_comparator (lexical_cmp),
  120. expected_rev_data)
  121. def test_compare_function_useless (self):
  122. self.startTest ()
  123. def socialist_comparator (l, r):
  124. return 0
  125. self.createDB (socialist_comparator)
  126. self.addDataToDB (['b', 'a', 'd'])
  127. # all things being equal the first key will be the only key
  128. # in the database... (with the last key's value fwiw)
  129. self.finishTest (['b'])
  130. class BtreeExceptionsTestCase (AbstractBtreeKeyCompareTestCase):
  131. def test_raises_non_callable (self):
  132. self.startTest ()
  133. self.assertRaises (TypeError, self.createDB, 'abc')
  134. self.assertRaises (TypeError, self.createDB, None)
  135. self.finishTest ()
  136. def test_set_bt_compare_with_function (self):
  137. self.startTest ()
  138. self.createDB (lexical_cmp)
  139. self.finishTest ()
  140. def check_results (self, results):
  141. pass
  142. def test_compare_function_incorrect (self):
  143. self.startTest ()
  144. def bad_comparator (l, r):
  145. return 1
  146. # verify that set_bt_compare checks that comparator('', '') == 0
  147. self.assertRaises (TypeError, self.createDB, bad_comparator)
  148. self.finishTest ()
  149. def verifyStderr(self, method, successRe):
  150. """
  151. Call method() while capturing sys.stderr output internally and
  152. call self.fail() if successRe.search() does not match the stderr
  153. output. This is used to test for uncatchable exceptions.
  154. """
  155. stdErr = sys.stderr
  156. sys.stderr = StringIO()
  157. try:
  158. method()
  159. finally:
  160. temp = sys.stderr
  161. sys.stderr = stdErr
  162. errorOut = temp.getvalue()
  163. if not successRe.search(errorOut):
  164. self.fail("unexpected stderr output:\n"+errorOut)
  165. def _test_compare_function_exception (self):
  166. self.startTest ()
  167. def bad_comparator (l, r):
  168. if l == r:
  169. # pass the set_bt_compare test
  170. return 0
  171. raise RuntimeError, "i'm a naughty comparison function"
  172. self.createDB (bad_comparator)
  173. #print "\n*** test should print 2 uncatchable tracebacks ***"
  174. self.addDataToDB (['a', 'b', 'c']) # this should raise, but...
  175. self.finishTest ()
  176. def test_compare_function_exception(self):
  177. self.verifyStderr(
  178. self._test_compare_function_exception,
  179. re.compile('(^RuntimeError:.* naughty.*){2}', re.M|re.S)
  180. )
  181. def _test_compare_function_bad_return (self):
  182. self.startTest ()
  183. def bad_comparator (l, r):
  184. if l == r:
  185. # pass the set_bt_compare test
  186. return 0
  187. return l
  188. self.createDB (bad_comparator)
  189. #print "\n*** test should print 2 errors about returning an int ***"
  190. self.addDataToDB (['a', 'b', 'c']) # this should raise, but...
  191. self.finishTest ()
  192. def test_compare_function_bad_return(self):
  193. self.verifyStderr(
  194. self._test_compare_function_bad_return,
  195. re.compile('(^TypeError:.* return an int.*){2}', re.M|re.S)
  196. )
  197. def test_cannot_assign_twice (self):
  198. def my_compare (a, b):
  199. return 0
  200. self.startTest ()
  201. self.createDB (my_compare)
  202. try:
  203. self.db.set_bt_compare (my_compare)
  204. self.assert_(0, "this set should fail")
  205. except RuntimeError, msg:
  206. pass
  207. def test_suite ():
  208. res = unittest.TestSuite ()
  209. res.addTest (unittest.makeSuite (ComparatorTests))
  210. res.addTest (unittest.makeSuite (BtreeExceptionsTestCase))
  211. res.addTest (unittest.makeSuite (BtreeKeyCompareTestCase))
  212. return res
  213. if __name__ == '__main__':
  214. unittest.main (defaultTest = 'suite')