• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""
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')
466