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