|
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 if db.version() >= (4, 1): |
|
152 self.primary.open(self.filename, "primary", self.dbtype, |
|
153 db.DB_CREATE | db.DB_THREAD | self.dbFlags, txn=txn) |
|
154 else: |
|
155 self.primary.open(self.filename, "primary", self.dbtype, |
|
156 db.DB_CREATE | db.DB_THREAD | self.dbFlags) |
|
157 |
|
158 def closeDB(self): |
|
159 if self.cur: |
|
160 self.cur.close() |
|
161 self.cur = None |
|
162 if self.secDB: |
|
163 self.secDB.close() |
|
164 self.secDB = None |
|
165 self.primary.close() |
|
166 self.primary = None |
|
167 |
|
168 def getDB(self): |
|
169 return self.primary |
|
170 |
|
171 |
|
172 def test01_associateWithDB(self): |
|
173 if verbose: |
|
174 print '\n', '-=' * 30 |
|
175 print "Running %s.test01_associateWithDB..." % \ |
|
176 self.__class__.__name__ |
|
177 |
|
178 self.createDB() |
|
179 |
|
180 self.secDB = db.DB(self.env) |
|
181 self.secDB.set_flags(db.DB_DUP) |
|
182 self.secDB.set_get_returns_none(2) |
|
183 self.secDB.open(self.filename, "secondary", db.DB_BTREE, |
|
184 db.DB_CREATE | db.DB_THREAD | self.dbFlags) |
|
185 self.getDB().associate(self.secDB, self.getGenre) |
|
186 |
|
187 self.addDataToDB(self.getDB()) |
|
188 |
|
189 self.finish_test(self.secDB) |
|
190 |
|
191 |
|
192 def test02_associateAfterDB(self): |
|
193 if verbose: |
|
194 print '\n', '-=' * 30 |
|
195 print "Running %s.test02_associateAfterDB..." % \ |
|
196 self.__class__.__name__ |
|
197 |
|
198 self.createDB() |
|
199 self.addDataToDB(self.getDB()) |
|
200 |
|
201 self.secDB = db.DB(self.env) |
|
202 self.secDB.set_flags(db.DB_DUP) |
|
203 self.secDB.open(self.filename, "secondary", db.DB_BTREE, |
|
204 db.DB_CREATE | db.DB_THREAD | self.dbFlags) |
|
205 |
|
206 # adding the DB_CREATE flag will cause it to index existing records |
|
207 self.getDB().associate(self.secDB, self.getGenre, db.DB_CREATE) |
|
208 |
|
209 self.finish_test(self.secDB) |
|
210 |
|
211 |
|
212 def finish_test(self, secDB, txn=None): |
|
213 # 'Blues' should not be in the secondary database |
|
214 vals = secDB.pget('Blues', txn=txn) |
|
215 self.assertEqual(vals, None, vals) |
|
216 |
|
217 vals = secDB.pget('Unknown', txn=txn) |
|
218 self.assert_(vals[0] == 99 or vals[0] == '99', vals) |
|
219 vals[1].index('Unknown') |
|
220 vals[1].index('Unnamed') |
|
221 vals[1].index('unknown') |
|
222 |
|
223 if verbose: |
|
224 print "Primary key traversal:" |
|
225 self.cur = self.getDB().cursor(txn) |
|
226 count = 0 |
|
227 rec = self.cur.first() |
|
228 while rec is not None: |
|
229 if type(self.keytype) == type(''): |
|
230 self.assert_(int(rec[0])) # for primary db, key is a number |
|
231 else: |
|
232 self.assert_(rec[0] and type(rec[0]) == type(0)) |
|
233 count = count + 1 |
|
234 if verbose: |
|
235 print rec |
|
236 rec = getattr(self.cur, "next")() |
|
237 self.assertEqual(count, len(musicdata)) # all items accounted for |
|
238 |
|
239 |
|
240 if verbose: |
|
241 print "Secondary key traversal:" |
|
242 self.cur = secDB.cursor(txn) |
|
243 count = 0 |
|
244 |
|
245 # test cursor pget |
|
246 vals = self.cur.pget('Unknown', flags=db.DB_LAST) |
|
247 self.assert_(vals[1] == 99 or vals[1] == '99', vals) |
|
248 self.assertEqual(vals[0], 'Unknown') |
|
249 vals[2].index('Unknown') |
|
250 vals[2].index('Unnamed') |
|
251 vals[2].index('unknown') |
|
252 |
|
253 vals = self.cur.pget('Unknown', data='wrong value', flags=db.DB_GET_BOTH) |
|
254 self.assertEqual(vals, None, vals) |
|
255 |
|
256 rec = self.cur.first() |
|
257 self.assertEqual(rec[0], "Jazz") |
|
258 while rec is not None: |
|
259 count = count + 1 |
|
260 if verbose: |
|
261 print rec |
|
262 rec = getattr(self.cur, "next")() |
|
263 # all items accounted for EXCEPT for 1 with "Blues" genre |
|
264 self.assertEqual(count, len(musicdata)-1) |
|
265 |
|
266 self.cur = None |
|
267 |
|
268 def getGenre(self, priKey, priData): |
|
269 self.assertEqual(type(priData), type("")) |
|
270 genre = priData.split('|')[2] |
|
271 |
|
272 if verbose: |
|
273 print 'getGenre key: %r data: %r' % (priKey, priData) |
|
274 |
|
275 if genre == 'Blues': |
|
276 return db.DB_DONOTINDEX |
|
277 else: |
|
278 return genre |
|
279 |
|
280 |
|
281 #---------------------------------------------------------------------- |
|
282 |
|
283 |
|
284 class AssociateHashTestCase(AssociateTestCase): |
|
285 dbtype = db.DB_HASH |
|
286 |
|
287 class AssociateBTreeTestCase(AssociateTestCase): |
|
288 dbtype = db.DB_BTREE |
|
289 |
|
290 class AssociateRecnoTestCase(AssociateTestCase): |
|
291 dbtype = db.DB_RECNO |
|
292 keytype = 0 |
|
293 |
|
294 #---------------------------------------------------------------------- |
|
295 |
|
296 class AssociateBTreeTxnTestCase(AssociateBTreeTestCase): |
|
297 envFlags = db.DB_INIT_TXN |
|
298 dbFlags = 0 |
|
299 |
|
300 def txn_finish_test(self, sDB, txn): |
|
301 try: |
|
302 self.finish_test(sDB, txn=txn) |
|
303 finally: |
|
304 if self.cur: |
|
305 self.cur.close() |
|
306 self.cur = None |
|
307 if txn: |
|
308 txn.commit() |
|
309 |
|
310 def test13_associate_in_transaction(self): |
|
311 if verbose: |
|
312 print '\n', '-=' * 30 |
|
313 print "Running %s.test13_associateAutoCommit..." % \ |
|
314 self.__class__.__name__ |
|
315 |
|
316 txn = self.env.txn_begin() |
|
317 try: |
|
318 self.createDB(txn=txn) |
|
319 |
|
320 self.secDB = db.DB(self.env) |
|
321 self.secDB.set_flags(db.DB_DUP) |
|
322 self.secDB.set_get_returns_none(2) |
|
323 self.secDB.open(self.filename, "secondary", db.DB_BTREE, |
|
324 db.DB_CREATE | db.DB_THREAD, txn=txn) |
|
325 if db.version() >= (4,1): |
|
326 self.getDB().associate(self.secDB, self.getGenre, txn=txn) |
|
327 else: |
|
328 self.getDB().associate(self.secDB, self.getGenre) |
|
329 |
|
330 self.addDataToDB(self.getDB(), txn=txn) |
|
331 except: |
|
332 txn.abort() |
|
333 raise |
|
334 |
|
335 self.txn_finish_test(self.secDB, txn=txn) |
|
336 |
|
337 |
|
338 #---------------------------------------------------------------------- |
|
339 |
|
340 class ShelveAssociateTestCase(AssociateTestCase): |
|
341 |
|
342 def createDB(self): |
|
343 self.primary = dbshelve.open(self.filename, |
|
344 dbname="primary", |
|
345 dbenv=self.env, |
|
346 filetype=self.dbtype) |
|
347 |
|
348 def addDataToDB(self, d): |
|
349 for key, value in musicdata.items(): |
|
350 if type(self.keytype) == type(''): |
|
351 key = "%02d" % key |
|
352 d.put(key, value) # save the value as is this time |
|
353 |
|
354 |
|
355 def getGenre(self, priKey, priData): |
|
356 self.assertEqual(type(priData), type(())) |
|
357 if verbose: |
|
358 print 'getGenre key: %r data: %r' % (priKey, priData) |
|
359 genre = priData[2] |
|
360 if genre == 'Blues': |
|
361 return db.DB_DONOTINDEX |
|
362 else: |
|
363 return genre |
|
364 |
|
365 |
|
366 class ShelveAssociateHashTestCase(ShelveAssociateTestCase): |
|
367 dbtype = db.DB_HASH |
|
368 |
|
369 class ShelveAssociateBTreeTestCase(ShelveAssociateTestCase): |
|
370 dbtype = db.DB_BTREE |
|
371 |
|
372 class ShelveAssociateRecnoTestCase(ShelveAssociateTestCase): |
|
373 dbtype = db.DB_RECNO |
|
374 keytype = 0 |
|
375 |
|
376 |
|
377 #---------------------------------------------------------------------- |
|
378 |
|
379 class ThreadedAssociateTestCase(AssociateTestCase): |
|
380 |
|
381 def addDataToDB(self, d): |
|
382 t1 = Thread(target = self.writer1, |
|
383 args = (d, )) |
|
384 t2 = Thread(target = self.writer2, |
|
385 args = (d, )) |
|
386 |
|
387 t1.setDaemon(True) |
|
388 t2.setDaemon(True) |
|
389 t1.start() |
|
390 t2.start() |
|
391 t1.join() |
|
392 t2.join() |
|
393 |
|
394 def writer1(self, d): |
|
395 for key, value in musicdata.items(): |
|
396 if type(self.keytype) == type(''): |
|
397 key = "%02d" % key |
|
398 d.put(key, '|'.join(value)) |
|
399 |
|
400 def writer2(self, d): |
|
401 for x in range(100, 600): |
|
402 key = 'z%2d' % x |
|
403 value = [key] * 4 |
|
404 d.put(key, '|'.join(value)) |
|
405 |
|
406 |
|
407 class ThreadedAssociateHashTestCase(ShelveAssociateTestCase): |
|
408 dbtype = db.DB_HASH |
|
409 |
|
410 class ThreadedAssociateBTreeTestCase(ShelveAssociateTestCase): |
|
411 dbtype = db.DB_BTREE |
|
412 |
|
413 class ThreadedAssociateRecnoTestCase(ShelveAssociateTestCase): |
|
414 dbtype = db.DB_RECNO |
|
415 keytype = 0 |
|
416 |
|
417 |
|
418 #---------------------------------------------------------------------- |
|
419 |
|
420 def test_suite(): |
|
421 suite = unittest.TestSuite() |
|
422 |
|
423 suite.addTest(unittest.makeSuite(AssociateErrorTestCase)) |
|
424 |
|
425 suite.addTest(unittest.makeSuite(AssociateHashTestCase)) |
|
426 suite.addTest(unittest.makeSuite(AssociateBTreeTestCase)) |
|
427 suite.addTest(unittest.makeSuite(AssociateRecnoTestCase)) |
|
428 |
|
429 if db.version() >= (4, 1): |
|
430 suite.addTest(unittest.makeSuite(AssociateBTreeTxnTestCase)) |
|
431 |
|
432 suite.addTest(unittest.makeSuite(ShelveAssociateHashTestCase)) |
|
433 suite.addTest(unittest.makeSuite(ShelveAssociateBTreeTestCase)) |
|
434 suite.addTest(unittest.makeSuite(ShelveAssociateRecnoTestCase)) |
|
435 |
|
436 if have_threads: |
|
437 suite.addTest(unittest.makeSuite(ThreadedAssociateHashTestCase)) |
|
438 suite.addTest(unittest.makeSuite(ThreadedAssociateBTreeTestCase)) |
|
439 suite.addTest(unittest.makeSuite(ThreadedAssociateRecnoTestCase)) |
|
440 |
|
441 return suite |
|
442 |
|
443 |
|
444 if __name__ == '__main__': |
|
445 unittest.main(defaultTest='test_suite') |