blob: 7a49e11ef7f964aef6deb0da88035f8161e791ba [file] [log] [blame]
Dan Albert287553d2017-02-16 10:47:51 -08001"""
2TestCases for DB.associate.
3"""
4
5import sys, os, string
6import time
7from pprint import pprint
8
9import unittest
10from test_all import db, dbshelve, test_support, verbose, have_threads, \
11 get_new_environment_path
12
13
14#----------------------------------------------------------------------
15
16
17musicdata = {
181 : ("Bad English", "The Price Of Love", "Rock"),
192 : ("DNA featuring Suzanne Vega", "Tom's Diner", "Rock"),
203 : ("George Michael", "Praying For Time", "Rock"),
214 : ("Gloria Estefan", "Here We Are", "Rock"),
225 : ("Linda Ronstadt", "Don't Know Much", "Rock"),
236 : ("Michael Bolton", "How Am I Supposed To Live Without You", "Blues"),
247 : ("Paul Young", "Oh Girl", "Rock"),
258 : ("Paula Abdul", "Opposites Attract", "Rock"),
269 : ("Richard Marx", "Should've Known Better", "Rock"),
2710: ("Rod Stewart", "Forever Young", "Rock"),
2811: ("Roxette", "Dangerous", "Rock"),
2912: ("Sheena Easton", "The Lover In Me", "Rock"),
3013: ("Sinead O'Connor", "Nothing Compares 2 U", "Rock"),
3114: ("Stevie B.", "Because I Love You", "Rock"),
3215: ("Taylor Dayne", "Love Will Lead You Back", "Rock"),
3316: ("The Bangles", "Eternal Flame", "Rock"),
3417: ("Wilson Phillips", "Release Me", "Rock"),
3518: ("Billy Joel", "Blonde Over Blue", "Rock"),
3619: ("Billy Joel", "Famous Last Words", "Rock"),
3720: ("Billy Joel", "Lullabye (Goodnight, My Angel)", "Rock"),
3821: ("Billy Joel", "The River Of Dreams", "Rock"),
3922: ("Billy Joel", "Two Thousand Years", "Rock"),
4023: ("Janet Jackson", "Alright", "Rock"),
4124: ("Janet Jackson", "Black Cat", "Rock"),
4225: ("Janet Jackson", "Come Back To Me", "Rock"),
4326: ("Janet Jackson", "Escapade", "Rock"),
4427: ("Janet Jackson", "Love Will Never Do (Without You)", "Rock"),
4528: ("Janet Jackson", "Miss You Much", "Rock"),
4629: ("Janet Jackson", "Rhythm Nation", "Rock"),
4730: ("Janet Jackson", "State Of The World", "Rock"),
4831: ("Janet Jackson", "The Knowledge", "Rock"),
4932: ("Spyro Gyra", "End of Romanticism", "Jazz"),
5033: ("Spyro Gyra", "Heliopolis", "Jazz"),
5134: ("Spyro Gyra", "Jubilee", "Jazz"),
5235: ("Spyro Gyra", "Little Linda", "Jazz"),
5336: ("Spyro Gyra", "Morning Dance", "Jazz"),
5437: ("Spyro Gyra", "Song for Lorraine", "Jazz"),
5538: ("Yes", "Owner Of A Lonely Heart", "Rock"),
5639: ("Yes", "Rhythm Of Love", "Rock"),
5740: ("Cusco", "Dream Catcher", "New Age"),
5841: ("Cusco", "Geronimos Laughter", "New Age"),
5942: ("Cusco", "Ghost Dance", "New Age"),
6043: ("Blue Man Group", "Drumbone", "New Age"),
6144: ("Blue Man Group", "Endless Column", "New Age"),
6245: ("Blue Man Group", "Klein Mandelbrot", "New Age"),
6346: ("Kenny G", "Silhouette", "Jazz"),
6447: ("Sade", "Smooth Operator", "Jazz"),
6548: ("David Arkenstone", "Papillon (On The Wings Of The Butterfly)",
66 "New Age"),
6749: ("David Arkenstone", "Stepping Stars", "New Age"),
6850: ("David Arkenstone", "Carnation Lily Lily Rose", "New Age"),
6951: ("David Lanz", "Behind The Waterfall", "New Age"),
7052: ("David Lanz", "Cristofori's Dream", "New Age"),
7153: ("David Lanz", "Heartsounds", "New Age"),
7254: ("David Lanz", "Leaves on the Seine", "New Age"),
7399: ("unknown artist", "Unnamed song", "Unknown"),
74}
75
76#----------------------------------------------------------------------
77
78class 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
122class 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
308class AssociateHashTestCase(AssociateTestCase):
309 dbtype = db.DB_HASH
310
311class AssociateBTreeTestCase(AssociateTestCase):
312 dbtype = db.DB_BTREE
313
314class AssociateRecnoTestCase(AssociateTestCase):
315 dbtype = db.DB_RECNO
316 keytype = 0
317
318#----------------------------------------------------------------------
319
320class 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
361class 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
387class ShelveAssociateHashTestCase(ShelveAssociateTestCase):
388 dbtype = db.DB_HASH
389
390class ShelveAssociateBTreeTestCase(ShelveAssociateTestCase):
391 dbtype = db.DB_BTREE
392
393class ShelveAssociateRecnoTestCase(ShelveAssociateTestCase):
394 dbtype = db.DB_RECNO
395 keytype = 0
396
397
398#----------------------------------------------------------------------
399
400class 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
428class ThreadedAssociateHashTestCase(ShelveAssociateTestCase):
429 dbtype = db.DB_HASH
430
431class ThreadedAssociateBTreeTestCase(ShelveAssociateTestCase):
432 dbtype = db.DB_BTREE
433
434class ThreadedAssociateRecnoTestCase(ShelveAssociateTestCase):
435 dbtype = db.DB_RECNO
436 keytype = 0
437
438
439#----------------------------------------------------------------------
440
441def 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
464if __name__ == '__main__':
465 unittest.main(defaultTest='test_suite')