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')
|
---|