/Lib/bsddb/test/test_associate.py

http://unladen-swallow.googlecode.com/ · Python · 445 lines · 331 code · 96 blank · 18 comment · 45 complexity · 1de9fcd8c80ad4bcc767e3dee26f03ad MD5 · raw file

  1. """
  2. TestCases for DB.associate.
  3. """
  4. import sys, os, string
  5. import time
  6. from pprint import pprint
  7. import unittest
  8. from test_all import db, dbshelve, test_support, verbose, have_threads, \
  9. get_new_environment_path
  10. #----------------------------------------------------------------------
  11. musicdata = {
  12. 1 : ("Bad English", "The Price Of Love", "Rock"),
  13. 2 : ("DNA featuring Suzanne Vega", "Tom's Diner", "Rock"),
  14. 3 : ("George Michael", "Praying For Time", "Rock"),
  15. 4 : ("Gloria Estefan", "Here We Are", "Rock"),
  16. 5 : ("Linda Ronstadt", "Don't Know Much", "Rock"),
  17. 6 : ("Michael Bolton", "How Am I Supposed To Live Without You", "Blues"),
  18. 7 : ("Paul Young", "Oh Girl", "Rock"),
  19. 8 : ("Paula Abdul", "Opposites Attract", "Rock"),
  20. 9 : ("Richard Marx", "Should've Known Better", "Rock"),
  21. 10: ("Rod Stewart", "Forever Young", "Rock"),
  22. 11: ("Roxette", "Dangerous", "Rock"),
  23. 12: ("Sheena Easton", "The Lover In Me", "Rock"),
  24. 13: ("Sinead O'Connor", "Nothing Compares 2 U", "Rock"),
  25. 14: ("Stevie B.", "Because I Love You", "Rock"),
  26. 15: ("Taylor Dayne", "Love Will Lead You Back", "Rock"),
  27. 16: ("The Bangles", "Eternal Flame", "Rock"),
  28. 17: ("Wilson Phillips", "Release Me", "Rock"),
  29. 18: ("Billy Joel", "Blonde Over Blue", "Rock"),
  30. 19: ("Billy Joel", "Famous Last Words", "Rock"),
  31. 20: ("Billy Joel", "Lullabye (Goodnight, My Angel)", "Rock"),
  32. 21: ("Billy Joel", "The River Of Dreams", "Rock"),
  33. 22: ("Billy Joel", "Two Thousand Years", "Rock"),
  34. 23: ("Janet Jackson", "Alright", "Rock"),
  35. 24: ("Janet Jackson", "Black Cat", "Rock"),
  36. 25: ("Janet Jackson", "Come Back To Me", "Rock"),
  37. 26: ("Janet Jackson", "Escapade", "Rock"),
  38. 27: ("Janet Jackson", "Love Will Never Do (Without You)", "Rock"),
  39. 28: ("Janet Jackson", "Miss You Much", "Rock"),
  40. 29: ("Janet Jackson", "Rhythm Nation", "Rock"),
  41. 30: ("Janet Jackson", "State Of The World", "Rock"),
  42. 31: ("Janet Jackson", "The Knowledge", "Rock"),
  43. 32: ("Spyro Gyra", "End of Romanticism", "Jazz"),
  44. 33: ("Spyro Gyra", "Heliopolis", "Jazz"),
  45. 34: ("Spyro Gyra", "Jubilee", "Jazz"),
  46. 35: ("Spyro Gyra", "Little Linda", "Jazz"),
  47. 36: ("Spyro Gyra", "Morning Dance", "Jazz"),
  48. 37: ("Spyro Gyra", "Song for Lorraine", "Jazz"),
  49. 38: ("Yes", "Owner Of A Lonely Heart", "Rock"),
  50. 39: ("Yes", "Rhythm Of Love", "Rock"),
  51. 40: ("Cusco", "Dream Catcher", "New Age"),
  52. 41: ("Cusco", "Geronimos Laughter", "New Age"),
  53. 42: ("Cusco", "Ghost Dance", "New Age"),
  54. 43: ("Blue Man Group", "Drumbone", "New Age"),
  55. 44: ("Blue Man Group", "Endless Column", "New Age"),
  56. 45: ("Blue Man Group", "Klein Mandelbrot", "New Age"),
  57. 46: ("Kenny G", "Silhouette", "Jazz"),
  58. 47: ("Sade", "Smooth Operator", "Jazz"),
  59. 48: ("David Arkenstone", "Papillon (On The Wings Of The Butterfly)",
  60. "New Age"),
  61. 49: ("David Arkenstone", "Stepping Stars", "New Age"),
  62. 50: ("David Arkenstone", "Carnation Lily Lily Rose", "New Age"),
  63. 51: ("David Lanz", "Behind The Waterfall", "New Age"),
  64. 52: ("David Lanz", "Cristofori's Dream", "New Age"),
  65. 53: ("David Lanz", "Heartsounds", "New Age"),
  66. 54: ("David Lanz", "Leaves on the Seine", "New Age"),
  67. 99: ("unknown artist", "Unnamed song", "Unknown"),
  68. }
  69. #----------------------------------------------------------------------
  70. class AssociateErrorTestCase(unittest.TestCase):
  71. def setUp(self):
  72. self.filename = self.__class__.__name__ + '.db'
  73. self.homeDir = get_new_environment_path()
  74. self.env = db.DBEnv()
  75. self.env.open(self.homeDir, db.DB_CREATE | db.DB_INIT_MPOOL)
  76. def tearDown(self):
  77. self.env.close()
  78. self.env = None
  79. test_support.rmtree(self.homeDir)
  80. def test00_associateDBError(self):
  81. if verbose:
  82. print '\n', '-=' * 30
  83. print "Running %s.test00_associateDBError..." % \
  84. self.__class__.__name__
  85. dupDB = db.DB(self.env)
  86. dupDB.set_flags(db.DB_DUP)
  87. dupDB.open(self.filename, "primary", db.DB_BTREE, db.DB_CREATE)
  88. secDB = db.DB(self.env)
  89. secDB.open(self.filename, "secondary", db.DB_BTREE, db.DB_CREATE)
  90. # dupDB has been configured to allow duplicates, it can't
  91. # associate with a secondary. Berkeley DB will return an error.
  92. try:
  93. def f(a,b): return a+b
  94. dupDB.associate(secDB, f)
  95. except db.DBError:
  96. # good
  97. secDB.close()
  98. dupDB.close()
  99. else:
  100. secDB.close()
  101. dupDB.close()
  102. self.fail("DBError exception was expected")
  103. #----------------------------------------------------------------------
  104. class AssociateTestCase(unittest.TestCase):
  105. keytype = ''
  106. envFlags = 0
  107. dbFlags = 0
  108. def setUp(self):
  109. self.filename = self.__class__.__name__ + '.db'
  110. self.homeDir = get_new_environment_path()
  111. self.env = db.DBEnv()
  112. self.env.open(self.homeDir, db.DB_CREATE | db.DB_INIT_MPOOL |
  113. db.DB_INIT_LOCK | db.DB_THREAD | self.envFlags)
  114. def tearDown(self):
  115. self.closeDB()
  116. self.env.close()
  117. self.env = None
  118. test_support.rmtree(self.homeDir)
  119. def addDataToDB(self, d, txn=None):
  120. for key, value in musicdata.items():
  121. if type(self.keytype) == type(''):
  122. key = "%02d" % key
  123. d.put(key, '|'.join(value), txn=txn)
  124. def createDB(self, txn=None):
  125. self.cur = None
  126. self.secDB = None
  127. self.primary = db.DB(self.env)
  128. self.primary.set_get_returns_none(2)
  129. if db.version() >= (4, 1):
  130. self.primary.open(self.filename, "primary", self.dbtype,
  131. db.DB_CREATE | db.DB_THREAD | self.dbFlags, txn=txn)
  132. else:
  133. self.primary.open(self.filename, "primary", self.dbtype,
  134. db.DB_CREATE | db.DB_THREAD | self.dbFlags)
  135. def closeDB(self):
  136. if self.cur:
  137. self.cur.close()
  138. self.cur = None
  139. if self.secDB:
  140. self.secDB.close()
  141. self.secDB = None
  142. self.primary.close()
  143. self.primary = None
  144. def getDB(self):
  145. return self.primary
  146. def test01_associateWithDB(self):
  147. if verbose:
  148. print '\n', '-=' * 30
  149. print "Running %s.test01_associateWithDB..." % \
  150. self.__class__.__name__
  151. self.createDB()
  152. self.secDB = db.DB(self.env)
  153. self.secDB.set_flags(db.DB_DUP)
  154. self.secDB.set_get_returns_none(2)
  155. self.secDB.open(self.filename, "secondary", db.DB_BTREE,
  156. db.DB_CREATE | db.DB_THREAD | self.dbFlags)
  157. self.getDB().associate(self.secDB, self.getGenre)
  158. self.addDataToDB(self.getDB())
  159. self.finish_test(self.secDB)
  160. def test02_associateAfterDB(self):
  161. if verbose:
  162. print '\n', '-=' * 30
  163. print "Running %s.test02_associateAfterDB..." % \
  164. self.__class__.__name__
  165. self.createDB()
  166. self.addDataToDB(self.getDB())
  167. self.secDB = db.DB(self.env)
  168. self.secDB.set_flags(db.DB_DUP)
  169. self.secDB.open(self.filename, "secondary", db.DB_BTREE,
  170. db.DB_CREATE | db.DB_THREAD | self.dbFlags)
  171. # adding the DB_CREATE flag will cause it to index existing records
  172. self.getDB().associate(self.secDB, self.getGenre, db.DB_CREATE)
  173. self.finish_test(self.secDB)
  174. def finish_test(self, secDB, txn=None):
  175. # 'Blues' should not be in the secondary database
  176. vals = secDB.pget('Blues', txn=txn)
  177. self.assertEqual(vals, None, vals)
  178. vals = secDB.pget('Unknown', txn=txn)
  179. self.assert_(vals[0] == 99 or vals[0] == '99', vals)
  180. vals[1].index('Unknown')
  181. vals[1].index('Unnamed')
  182. vals[1].index('unknown')
  183. if verbose:
  184. print "Primary key traversal:"
  185. self.cur = self.getDB().cursor(txn)
  186. count = 0
  187. rec = self.cur.first()
  188. while rec is not None:
  189. if type(self.keytype) == type(''):
  190. self.assert_(int(rec[0])) # for primary db, key is a number
  191. else:
  192. self.assert_(rec[0] and type(rec[0]) == type(0))
  193. count = count + 1
  194. if verbose:
  195. print rec
  196. rec = getattr(self.cur, "next")()
  197. self.assertEqual(count, len(musicdata)) # all items accounted for
  198. if verbose:
  199. print "Secondary key traversal:"
  200. self.cur = secDB.cursor(txn)
  201. count = 0
  202. # test cursor pget
  203. vals = self.cur.pget('Unknown', flags=db.DB_LAST)
  204. self.assert_(vals[1] == 99 or vals[1] == '99', vals)
  205. self.assertEqual(vals[0], 'Unknown')
  206. vals[2].index('Unknown')
  207. vals[2].index('Unnamed')
  208. vals[2].index('unknown')
  209. vals = self.cur.pget('Unknown', data='wrong value', flags=db.DB_GET_BOTH)
  210. self.assertEqual(vals, None, vals)
  211. rec = self.cur.first()
  212. self.assertEqual(rec[0], "Jazz")
  213. while rec is not None:
  214. count = count + 1
  215. if verbose:
  216. print rec
  217. rec = getattr(self.cur, "next")()
  218. # all items accounted for EXCEPT for 1 with "Blues" genre
  219. self.assertEqual(count, len(musicdata)-1)
  220. self.cur = None
  221. def getGenre(self, priKey, priData):
  222. self.assertEqual(type(priData), type(""))
  223. genre = priData.split('|')[2]
  224. if verbose:
  225. print 'getGenre key: %r data: %r' % (priKey, priData)
  226. if genre == 'Blues':
  227. return db.DB_DONOTINDEX
  228. else:
  229. return genre
  230. #----------------------------------------------------------------------
  231. class AssociateHashTestCase(AssociateTestCase):
  232. dbtype = db.DB_HASH
  233. class AssociateBTreeTestCase(AssociateTestCase):
  234. dbtype = db.DB_BTREE
  235. class AssociateRecnoTestCase(AssociateTestCase):
  236. dbtype = db.DB_RECNO
  237. keytype = 0
  238. #----------------------------------------------------------------------
  239. class AssociateBTreeTxnTestCase(AssociateBTreeTestCase):
  240. envFlags = db.DB_INIT_TXN
  241. dbFlags = 0
  242. def txn_finish_test(self, sDB, txn):
  243. try:
  244. self.finish_test(sDB, txn=txn)
  245. finally:
  246. if self.cur:
  247. self.cur.close()
  248. self.cur = None
  249. if txn:
  250. txn.commit()
  251. def test13_associate_in_transaction(self):
  252. if verbose:
  253. print '\n', '-=' * 30
  254. print "Running %s.test13_associateAutoCommit..." % \
  255. self.__class__.__name__
  256. txn = self.env.txn_begin()
  257. try:
  258. self.createDB(txn=txn)
  259. self.secDB = db.DB(self.env)
  260. self.secDB.set_flags(db.DB_DUP)
  261. self.secDB.set_get_returns_none(2)
  262. self.secDB.open(self.filename, "secondary", db.DB_BTREE,
  263. db.DB_CREATE | db.DB_THREAD, txn=txn)
  264. if db.version() >= (4,1):
  265. self.getDB().associate(self.secDB, self.getGenre, txn=txn)
  266. else:
  267. self.getDB().associate(self.secDB, self.getGenre)
  268. self.addDataToDB(self.getDB(), txn=txn)
  269. except:
  270. txn.abort()
  271. raise
  272. self.txn_finish_test(self.secDB, txn=txn)
  273. #----------------------------------------------------------------------
  274. class ShelveAssociateTestCase(AssociateTestCase):
  275. def createDB(self):
  276. self.primary = dbshelve.open(self.filename,
  277. dbname="primary",
  278. dbenv=self.env,
  279. filetype=self.dbtype)
  280. def addDataToDB(self, d):
  281. for key, value in musicdata.items():
  282. if type(self.keytype) == type(''):
  283. key = "%02d" % key
  284. d.put(key, value) # save the value as is this time
  285. def getGenre(self, priKey, priData):
  286. self.assertEqual(type(priData), type(()))
  287. if verbose:
  288. print 'getGenre key: %r data: %r' % (priKey, priData)
  289. genre = priData[2]
  290. if genre == 'Blues':
  291. return db.DB_DONOTINDEX
  292. else:
  293. return genre
  294. class ShelveAssociateHashTestCase(ShelveAssociateTestCase):
  295. dbtype = db.DB_HASH
  296. class ShelveAssociateBTreeTestCase(ShelveAssociateTestCase):
  297. dbtype = db.DB_BTREE
  298. class ShelveAssociateRecnoTestCase(ShelveAssociateTestCase):
  299. dbtype = db.DB_RECNO
  300. keytype = 0
  301. #----------------------------------------------------------------------
  302. class ThreadedAssociateTestCase(AssociateTestCase):
  303. def addDataToDB(self, d):
  304. t1 = Thread(target = self.writer1,
  305. args = (d, ))
  306. t2 = Thread(target = self.writer2,
  307. args = (d, ))
  308. t1.setDaemon(True)
  309. t2.setDaemon(True)
  310. t1.start()
  311. t2.start()
  312. t1.join()
  313. t2.join()
  314. def writer1(self, d):
  315. for key, value in musicdata.items():
  316. if type(self.keytype) == type(''):
  317. key = "%02d" % key
  318. d.put(key, '|'.join(value))
  319. def writer2(self, d):
  320. for x in range(100, 600):
  321. key = 'z%2d' % x
  322. value = [key] * 4
  323. d.put(key, '|'.join(value))
  324. class ThreadedAssociateHashTestCase(ShelveAssociateTestCase):
  325. dbtype = db.DB_HASH
  326. class ThreadedAssociateBTreeTestCase(ShelveAssociateTestCase):
  327. dbtype = db.DB_BTREE
  328. class ThreadedAssociateRecnoTestCase(ShelveAssociateTestCase):
  329. dbtype = db.DB_RECNO
  330. keytype = 0
  331. #----------------------------------------------------------------------
  332. def test_suite():
  333. suite = unittest.TestSuite()
  334. suite.addTest(unittest.makeSuite(AssociateErrorTestCase))
  335. suite.addTest(unittest.makeSuite(AssociateHashTestCase))
  336. suite.addTest(unittest.makeSuite(AssociateBTreeTestCase))
  337. suite.addTest(unittest.makeSuite(AssociateRecnoTestCase))
  338. if db.version() >= (4, 1):
  339. suite.addTest(unittest.makeSuite(AssociateBTreeTxnTestCase))
  340. suite.addTest(unittest.makeSuite(ShelveAssociateHashTestCase))
  341. suite.addTest(unittest.makeSuite(ShelveAssociateBTreeTestCase))
  342. suite.addTest(unittest.makeSuite(ShelveAssociateRecnoTestCase))
  343. if have_threads:
  344. suite.addTest(unittest.makeSuite(ThreadedAssociateHashTestCase))
  345. suite.addTest(unittest.makeSuite(ThreadedAssociateBTreeTestCase))
  346. suite.addTest(unittest.makeSuite(ThreadedAssociateRecnoTestCase))
  347. return suite
  348. if __name__ == '__main__':
  349. unittest.main(defaultTest='test_suite')