• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1"""
2Basic TestCases for BTree and hash DBs, with and without a DBEnv, with
3various DB flags, etc.
4"""
5
6import os
7import errno
8import string
9from pprint import pprint
10import unittest
11import time
12import sys
13
14from test_all import db, test_support, verbose, get_new_environment_path, \
15        get_new_database_path
16
17DASH = '-'
18
19
20#----------------------------------------------------------------------
21
22class VersionTestCase(unittest.TestCase):
23    def test00_version(self):
24        info = db.version()
25        if verbose:
26            print '\n', '-=' * 20
27            print 'bsddb.db.version(): %s' % (info, )
28            print db.DB_VERSION_STRING
29            print '-=' * 20
30        self.assertEqual(info, (db.DB_VERSION_MAJOR, db.DB_VERSION_MINOR,
31                        db.DB_VERSION_PATCH))
32
33#----------------------------------------------------------------------
34
35class BasicTestCase(unittest.TestCase):
36    dbtype       = db.DB_UNKNOWN  # must be set in derived class
37    cachesize    = (0, 1024*1024, 1)
38    dbopenflags  = 0
39    dbsetflags   = 0
40    dbmode       = 0660
41    dbname       = None
42    useEnv       = 0
43    envflags     = 0
44    envsetflags  = 0
45
46    _numKeys      = 1002    # PRIVATE.  NOTE: must be an even value
47
48    def setUp(self):
49        if self.useEnv:
50            self.homeDir=get_new_environment_path()
51            try:
52                self.env = db.DBEnv()
53                self.env.set_lg_max(1024*1024)
54                self.env.set_tx_max(30)
55                self._t = int(time.time())
56                self.env.set_tx_timestamp(self._t)
57                self.env.set_flags(self.envsetflags, 1)
58                self.env.open(self.homeDir, self.envflags | db.DB_CREATE)
59                self.filename = "test"
60            # Yes, a bare except is intended, since we're re-raising the exc.
61            except:
62                test_support.rmtree(self.homeDir)
63                raise
64        else:
65            self.env = None
66            self.filename = get_new_database_path()
67
68        # create and open the DB
69        self.d = db.DB(self.env)
70        if not self.useEnv :
71            self.d.set_cachesize(*self.cachesize)
72            cachesize = self.d.get_cachesize()
73            self.assertEqual(cachesize[0], self.cachesize[0])
74            self.assertEqual(cachesize[2], self.cachesize[2])
75            # Berkeley DB expands the cache 25% accounting overhead,
76            # if the cache is small.
77            self.assertEqual(125, int(100.0*cachesize[1]/self.cachesize[1]))
78        self.d.set_flags(self.dbsetflags)
79        if self.dbname:
80            self.d.open(self.filename, self.dbname, self.dbtype,
81                        self.dbopenflags|db.DB_CREATE, self.dbmode)
82        else:
83            self.d.open(self.filename,   # try out keyword args
84                        mode = self.dbmode,
85                        dbtype = self.dbtype,
86                        flags = self.dbopenflags|db.DB_CREATE)
87
88        if not self.useEnv:
89            self.assertRaises(db.DBInvalidArgError,
90                    self.d.set_cachesize, *self.cachesize)
91
92        self.populateDB()
93
94
95    def tearDown(self):
96        self.d.close()
97        if self.env is not None:
98            self.env.close()
99            test_support.rmtree(self.homeDir)
100        else:
101            os.remove(self.filename)
102
103
104
105    def populateDB(self, _txn=None):
106        d = self.d
107
108        for x in range(self._numKeys//2):
109            key = '%04d' % (self._numKeys - x)  # insert keys in reverse order
110            data = self.makeData(key)
111            d.put(key, data, _txn)
112
113        d.put('empty value', '', _txn)
114
115        for x in range(self._numKeys//2-1):
116            key = '%04d' % x  # and now some in forward order
117            data = self.makeData(key)
118            d.put(key, data, _txn)
119
120        if _txn:
121            _txn.commit()
122
123        num = len(d)
124        if verbose:
125            print "created %d records" % num
126
127
128    def makeData(self, key):
129        return DASH.join([key] * 5)
130
131
132
133    #----------------------------------------
134
135    def test01_GetsAndPuts(self):
136        d = self.d
137
138        if verbose:
139            print '\n', '-=' * 30
140            print "Running %s.test01_GetsAndPuts..." % self.__class__.__name__
141
142        for key in ['0001', '0100', '0400', '0700', '0999']:
143            data = d.get(key)
144            if verbose:
145                print data
146
147        self.assertEqual(d.get('0321'), '0321-0321-0321-0321-0321')
148
149        # By default non-existent keys return None...
150        self.assertEqual(d.get('abcd'), None)
151
152        # ...but they raise exceptions in other situations.  Call
153        # set_get_returns_none() to change it.
154        try:
155            d.delete('abcd')
156        except db.DBNotFoundError, val:
157            if sys.version_info < (2, 6) :
158                self.assertEqual(val[0], db.DB_NOTFOUND)
159            else :
160                self.assertEqual(val.args[0], db.DB_NOTFOUND)
161            if verbose: print val
162        else:
163            self.fail("expected exception")
164
165
166        d.put('abcd', 'a new record')
167        self.assertEqual(d.get('abcd'), 'a new record')
168
169        d.put('abcd', 'same key')
170        if self.dbsetflags & db.DB_DUP:
171            self.assertEqual(d.get('abcd'), 'a new record')
172        else:
173            self.assertEqual(d.get('abcd'), 'same key')
174
175
176        try:
177            d.put('abcd', 'this should fail', flags=db.DB_NOOVERWRITE)
178        except db.DBKeyExistError, val:
179            if sys.version_info < (2, 6) :
180                self.assertEqual(val[0], db.DB_KEYEXIST)
181            else :
182                self.assertEqual(val.args[0], db.DB_KEYEXIST)
183            if verbose: print val
184        else:
185            self.fail("expected exception")
186
187        if self.dbsetflags & db.DB_DUP:
188            self.assertEqual(d.get('abcd'), 'a new record')
189        else:
190            self.assertEqual(d.get('abcd'), 'same key')
191
192
193        d.sync()
194        d.close()
195        del d
196
197        self.d = db.DB(self.env)
198        if self.dbname:
199            self.d.open(self.filename, self.dbname)
200        else:
201            self.d.open(self.filename)
202        d = self.d
203
204        self.assertEqual(d.get('0321'), '0321-0321-0321-0321-0321')
205        if self.dbsetflags & db.DB_DUP:
206            self.assertEqual(d.get('abcd'), 'a new record')
207        else:
208            self.assertEqual(d.get('abcd'), 'same key')
209
210        rec = d.get_both('0555', '0555-0555-0555-0555-0555')
211        if verbose:
212            print rec
213
214        self.assertEqual(d.get_both('0555', 'bad data'), None)
215
216        # test default value
217        data = d.get('bad key', 'bad data')
218        self.assertEqual(data, 'bad data')
219
220        # any object can pass through
221        data = d.get('bad key', self)
222        self.assertEqual(data, self)
223
224        s = d.stat()
225        self.assertEqual(type(s), type({}))
226        if verbose:
227            print 'd.stat() returned this dictionary:'
228            pprint(s)
229
230
231    #----------------------------------------
232
233    def test02_DictionaryMethods(self):
234        d = self.d
235
236        if verbose:
237            print '\n', '-=' * 30
238            print "Running %s.test02_DictionaryMethods..." % \
239                  self.__class__.__name__
240
241        for key in ['0002', '0101', '0401', '0701', '0998']:
242            data = d[key]
243            self.assertEqual(data, self.makeData(key))
244            if verbose:
245                print data
246
247        self.assertEqual(len(d), self._numKeys)
248        keys = d.keys()
249        self.assertEqual(len(keys), self._numKeys)
250        self.assertEqual(type(keys), type([]))
251
252        d['new record'] = 'a new record'
253        self.assertEqual(len(d), self._numKeys+1)
254        keys = d.keys()
255        self.assertEqual(len(keys), self._numKeys+1)
256
257        d['new record'] = 'a replacement record'
258        self.assertEqual(len(d), self._numKeys+1)
259        keys = d.keys()
260        self.assertEqual(len(keys), self._numKeys+1)
261
262        if verbose:
263            print "the first 10 keys are:"
264            pprint(keys[:10])
265
266        self.assertEqual(d['new record'], 'a replacement record')
267
268# We check also the positional parameter
269        self.assertEqual(d.has_key('0001', None), 1)
270# We check also the keyword parameter
271        self.assertEqual(d.has_key('spam', txn=None), 0)
272
273        items = d.items()
274        self.assertEqual(len(items), self._numKeys+1)
275        self.assertEqual(type(items), type([]))
276        self.assertEqual(type(items[0]), type(()))
277        self.assertEqual(len(items[0]), 2)
278
279        if verbose:
280            print "the first 10 items are:"
281            pprint(items[:10])
282
283        values = d.values()
284        self.assertEqual(len(values), self._numKeys+1)
285        self.assertEqual(type(values), type([]))
286
287        if verbose:
288            print "the first 10 values are:"
289            pprint(values[:10])
290
291
292    #----------------------------------------
293
294    def test02b_SequenceMethods(self):
295        d = self.d
296
297        for key in ['0002', '0101', '0401', '0701', '0998']:
298            data = d[key]
299            self.assertEqual(data, self.makeData(key))
300            if verbose:
301                print data
302
303        self.assertTrue(hasattr(d, "__contains__"))
304        self.assertTrue("0401" in d)
305        self.assertFalse("1234" in d)
306
307
308    #----------------------------------------
309
310    def test03_SimpleCursorStuff(self, get_raises_error=0, set_raises_error=0):
311        if verbose:
312            print '\n', '-=' * 30
313            print "Running %s.test03_SimpleCursorStuff (get_error %s, set_error %s)..." % \
314                  (self.__class__.__name__, get_raises_error, set_raises_error)
315
316        if self.env and self.dbopenflags & db.DB_AUTO_COMMIT:
317            txn = self.env.txn_begin()
318        else:
319            txn = None
320        c = self.d.cursor(txn=txn)
321
322        rec = c.first()
323        count = 0
324        while rec is not None:
325            count = count + 1
326            if verbose and count % 100 == 0:
327                print rec
328            try:
329                rec = c.next()
330            except db.DBNotFoundError, val:
331                if get_raises_error:
332                    if sys.version_info < (2, 6) :
333                        self.assertEqual(val[0], db.DB_NOTFOUND)
334                    else :
335                        self.assertEqual(val.args[0], db.DB_NOTFOUND)
336                    if verbose: print val
337                    rec = None
338                else:
339                    self.fail("unexpected DBNotFoundError")
340            self.assertEqual(c.get_current_size(), len(c.current()[1]),
341                    "%s != len(%r)" % (c.get_current_size(), c.current()[1]))
342
343        self.assertEqual(count, self._numKeys)
344
345
346        rec = c.last()
347        count = 0
348        while rec is not None:
349            count = count + 1
350            if verbose and count % 100 == 0:
351                print rec
352            try:
353                rec = c.prev()
354            except db.DBNotFoundError, val:
355                if get_raises_error:
356                    if sys.version_info < (2, 6) :
357                        self.assertEqual(val[0], db.DB_NOTFOUND)
358                    else :
359                        self.assertEqual(val.args[0], db.DB_NOTFOUND)
360                    if verbose: print val
361                    rec = None
362                else:
363                    self.fail("unexpected DBNotFoundError")
364
365        self.assertEqual(count, self._numKeys)
366
367        rec = c.set('0505')
368        rec2 = c.current()
369        self.assertEqual(rec, rec2)
370        self.assertEqual(rec[0], '0505')
371        self.assertEqual(rec[1], self.makeData('0505'))
372        self.assertEqual(c.get_current_size(), len(rec[1]))
373
374        # make sure we get empty values properly
375        rec = c.set('empty value')
376        self.assertEqual(rec[1], '')
377        self.assertEqual(c.get_current_size(), 0)
378
379        try:
380            n = c.set('bad key')
381        except db.DBNotFoundError, val:
382            if sys.version_info < (2, 6) :
383                self.assertEqual(val[0], db.DB_NOTFOUND)
384            else :
385                self.assertEqual(val.args[0], db.DB_NOTFOUND)
386            if verbose: print val
387        else:
388            if set_raises_error:
389                self.fail("expected exception")
390            if n is not None:
391                self.fail("expected None: %r" % (n,))
392
393        rec = c.get_both('0404', self.makeData('0404'))
394        self.assertEqual(rec, ('0404', self.makeData('0404')))
395
396        try:
397            n = c.get_both('0404', 'bad data')
398        except db.DBNotFoundError, val:
399            if sys.version_info < (2, 6) :
400                self.assertEqual(val[0], db.DB_NOTFOUND)
401            else :
402                self.assertEqual(val.args[0], db.DB_NOTFOUND)
403            if verbose: print val
404        else:
405            if get_raises_error:
406                self.fail("expected exception")
407            if n is not None:
408                self.fail("expected None: %r" % (n,))
409
410        if self.d.get_type() == db.DB_BTREE:
411            rec = c.set_range('011')
412            if verbose:
413                print "searched for '011', found: ", rec
414
415            rec = c.set_range('011',dlen=0,doff=0)
416            if verbose:
417                print "searched (partial) for '011', found: ", rec
418            if rec[1] != '': self.fail('expected empty data portion')
419
420            ev = c.set_range('empty value')
421            if verbose:
422                print "search for 'empty value' returned", ev
423            if ev[1] != '': self.fail('empty value lookup failed')
424
425        c.set('0499')
426        c.delete()
427        try:
428            rec = c.current()
429        except db.DBKeyEmptyError, val:
430            if get_raises_error:
431                if sys.version_info < (2, 6) :
432                    self.assertEqual(val[0], db.DB_KEYEMPTY)
433                else :
434                    self.assertEqual(val.args[0], db.DB_KEYEMPTY)
435                if verbose: print val
436            else:
437                self.fail("unexpected DBKeyEmptyError")
438        else:
439            if get_raises_error:
440                self.fail('DBKeyEmptyError exception expected')
441
442        c.next()
443        c2 = c.dup(db.DB_POSITION)
444        self.assertEqual(c.current(), c2.current())
445
446        c2.put('', 'a new value', db.DB_CURRENT)
447        self.assertEqual(c.current(), c2.current())
448        self.assertEqual(c.current()[1], 'a new value')
449
450        c2.put('', 'er', db.DB_CURRENT, dlen=0, doff=5)
451        self.assertEqual(c2.current()[1], 'a newer value')
452
453        c.close()
454        c2.close()
455        if txn:
456            txn.commit()
457
458        # time to abuse the closed cursors and hope we don't crash
459        methods_to_test = {
460            'current': (),
461            'delete': (),
462            'dup': (db.DB_POSITION,),
463            'first': (),
464            'get': (0,),
465            'next': (),
466            'prev': (),
467            'last': (),
468            'put':('', 'spam', db.DB_CURRENT),
469            'set': ("0505",),
470        }
471        for method, args in methods_to_test.items():
472            try:
473                if verbose:
474                    print "attempting to use a closed cursor's %s method" % \
475                          method
476                # a bug may cause a NULL pointer dereference...
477                getattr(c, method)(*args)
478            except db.DBError, val:
479                if sys.version_info < (2, 6) :
480                    self.assertEqual(val[0], 0)
481                else :
482                    self.assertEqual(val.args[0], 0)
483                if verbose: print val
484            else:
485                self.fail("no exception raised when using a buggy cursor's"
486                          "%s method" % method)
487
488        #
489        # free cursor referencing a closed database, it should not barf:
490        #
491        oldcursor = self.d.cursor(txn=txn)
492        self.d.close()
493
494        # this would originally cause a segfault when the cursor for a
495        # closed database was cleaned up.  it should not anymore.
496        # SF pybsddb bug id 667343
497        del oldcursor
498
499    def test03b_SimpleCursorWithoutGetReturnsNone0(self):
500        # same test but raise exceptions instead of returning None
501        if verbose:
502            print '\n', '-=' * 30
503            print "Running %s.test03b_SimpleCursorStuffWithoutGetReturnsNone..." % \
504                  self.__class__.__name__
505
506        old = self.d.set_get_returns_none(0)
507        self.assertEqual(old, 2)
508        self.test03_SimpleCursorStuff(get_raises_error=1, set_raises_error=1)
509
510    def test03b_SimpleCursorWithGetReturnsNone1(self):
511        # same test but raise exceptions instead of returning None
512        if verbose:
513            print '\n', '-=' * 30
514            print "Running %s.test03b_SimpleCursorStuffWithoutGetReturnsNone..." % \
515                  self.__class__.__name__
516
517        old = self.d.set_get_returns_none(1)
518        self.test03_SimpleCursorStuff(get_raises_error=0, set_raises_error=1)
519
520
521    def test03c_SimpleCursorGetReturnsNone2(self):
522        # same test but raise exceptions instead of returning None
523        if verbose:
524            print '\n', '-=' * 30
525            print "Running %s.test03c_SimpleCursorStuffWithoutSetReturnsNone..." % \
526                  self.__class__.__name__
527
528        old = self.d.set_get_returns_none(1)
529        self.assertEqual(old, 2)
530        old = self.d.set_get_returns_none(2)
531        self.assertEqual(old, 1)
532        self.test03_SimpleCursorStuff(get_raises_error=0, set_raises_error=0)
533
534    if db.version() >= (4, 6):
535        def test03d_SimpleCursorPriority(self) :
536            c = self.d.cursor()
537            c.set_priority(db.DB_PRIORITY_VERY_LOW)  # Positional
538            self.assertEqual(db.DB_PRIORITY_VERY_LOW, c.get_priority())
539            c.set_priority(priority=db.DB_PRIORITY_HIGH)  # Keyword
540            self.assertEqual(db.DB_PRIORITY_HIGH, c.get_priority())
541            c.close()
542
543    #----------------------------------------
544
545    def test04_PartialGetAndPut(self):
546        d = self.d
547        if verbose:
548            print '\n', '-=' * 30
549            print "Running %s.test04_PartialGetAndPut..." % \
550                  self.__class__.__name__
551
552        key = "partialTest"
553        data = "1" * 1000 + "2" * 1000
554        d.put(key, data)
555        self.assertEqual(d.get(key), data)
556        self.assertEqual(d.get(key, dlen=20, doff=990),
557                ("1" * 10) + ("2" * 10))
558
559        d.put("partialtest2", ("1" * 30000) + "robin" )
560        self.assertEqual(d.get("partialtest2", dlen=5, doff=30000), "robin")
561
562        # There seems to be a bug in DB here...  Commented out the test for
563        # now.
564        ##self.assertEqual(d.get("partialtest2", dlen=5, doff=30010), "")
565
566        if self.dbsetflags != db.DB_DUP:
567            # Partial put with duplicate records requires a cursor
568            d.put(key, "0000", dlen=2000, doff=0)
569            self.assertEqual(d.get(key), "0000")
570
571            d.put(key, "1111", dlen=1, doff=2)
572            self.assertEqual(d.get(key), "0011110")
573
574    #----------------------------------------
575
576    def test05_GetSize(self):
577        d = self.d
578        if verbose:
579            print '\n', '-=' * 30
580            print "Running %s.test05_GetSize..." % self.__class__.__name__
581
582        for i in range(1, 50000, 500):
583            key = "size%s" % i
584            #print "before ", i,
585            d.put(key, "1" * i)
586            #print "after",
587            self.assertEqual(d.get_size(key), i)
588            #print "done"
589
590    #----------------------------------------
591
592    def test06_Truncate(self):
593        d = self.d
594        if verbose:
595            print '\n', '-=' * 30
596            print "Running %s.test06_Truncate..." % self.__class__.__name__
597
598        d.put("abcde", "ABCDE");
599        num = d.truncate()
600        self.assertTrue(num >= 1, "truncate returned <= 0 on non-empty database")
601        num = d.truncate()
602        self.assertEqual(num, 0,
603                "truncate on empty DB returned nonzero (%r)" % (num,))
604
605    #----------------------------------------
606
607    def test07_verify(self):
608        # Verify bug solved in 4.7.3pre8
609        self.d.close()
610        d = db.DB(self.env)
611        d.verify(self.filename)
612
613
614    #----------------------------------------
615
616    if db.version() >= (4, 6):
617        def test08_exists(self) :
618            self.d.put("abcde", "ABCDE")
619            self.assertTrue(self.d.exists("abcde") == True,
620                    "DB->exists() returns wrong value")
621            self.assertTrue(self.d.exists("x") == False,
622                    "DB->exists() returns wrong value")
623
624    #----------------------------------------
625
626    if db.version() >= (4, 7):
627        def test_compact(self) :
628            d = self.d
629            self.assertEqual(0, d.compact(flags=db.DB_FREELIST_ONLY))
630            self.assertEqual(0, d.compact(flags=db.DB_FREELIST_ONLY))
631            d.put("abcde", "ABCDE");
632            d.put("bcde", "BCDE");
633            d.put("abc", "ABC");
634            d.put("monty", "python");
635            d.delete("abc")
636            d.delete("bcde")
637            d.compact(start='abcde', stop='monty', txn=None,
638                    compact_fillpercent=42, compact_pages=1,
639                    compact_timeout=50000000,
640                    flags=db.DB_FREELIST_ONLY|db.DB_FREE_SPACE)
641
642    #----------------------------------------
643
644#----------------------------------------------------------------------
645
646
647class BasicBTreeTestCase(BasicTestCase):
648    dbtype = db.DB_BTREE
649
650
651class BasicHashTestCase(BasicTestCase):
652    dbtype = db.DB_HASH
653
654
655class BasicBTreeWithThreadFlagTestCase(BasicTestCase):
656    dbtype = db.DB_BTREE
657    dbopenflags = db.DB_THREAD
658
659
660class BasicHashWithThreadFlagTestCase(BasicTestCase):
661    dbtype = db.DB_HASH
662    dbopenflags = db.DB_THREAD
663
664
665class BasicWithEnvTestCase(BasicTestCase):
666    dbopenflags = db.DB_THREAD
667    useEnv = 1
668    envflags = db.DB_THREAD | db.DB_INIT_MPOOL | db.DB_INIT_LOCK
669
670    #----------------------------------------
671
672    def test09_EnvRemoveAndRename(self):
673        if not self.env:
674            return
675
676        if verbose:
677            print '\n', '-=' * 30
678            print "Running %s.test09_EnvRemoveAndRename..." % self.__class__.__name__
679
680        # can't rename or remove an open DB
681        self.d.close()
682
683        newname = self.filename + '.renamed'
684        self.env.dbrename(self.filename, None, newname)
685        self.env.dbremove(newname)
686
687    #----------------------------------------
688
689class BasicBTreeWithEnvTestCase(BasicWithEnvTestCase):
690    dbtype = db.DB_BTREE
691
692
693class BasicHashWithEnvTestCase(BasicWithEnvTestCase):
694    dbtype = db.DB_HASH
695
696
697#----------------------------------------------------------------------
698
699class BasicTransactionTestCase(BasicTestCase):
700    if (sys.version_info < (2, 7)) or ((sys.version_info >= (3, 0)) and
701            (sys.version_info < (3, 2))) :
702        def assertIn(self, a, b, msg=None) :
703            return self.assertTrue(a in b, msg=msg)
704
705    dbopenflags = db.DB_THREAD | db.DB_AUTO_COMMIT
706    useEnv = 1
707    envflags = (db.DB_THREAD | db.DB_INIT_MPOOL | db.DB_INIT_LOCK |
708                db.DB_INIT_TXN)
709    envsetflags = db.DB_AUTO_COMMIT
710
711
712    def tearDown(self):
713        self.txn.commit()
714        BasicTestCase.tearDown(self)
715
716
717    def populateDB(self):
718        txn = self.env.txn_begin()
719        BasicTestCase.populateDB(self, _txn=txn)
720
721        self.txn = self.env.txn_begin()
722
723
724    def test06_Transactions(self):
725        d = self.d
726        if verbose:
727            print '\n', '-=' * 30
728            print "Running %s.test06_Transactions..." % self.__class__.__name__
729
730        self.assertEqual(d.get('new rec', txn=self.txn), None)
731        d.put('new rec', 'this is a new record', self.txn)
732        self.assertEqual(d.get('new rec', txn=self.txn),
733                'this is a new record')
734        self.txn.abort()
735        self.assertEqual(d.get('new rec'), None)
736
737        self.txn = self.env.txn_begin()
738
739        self.assertEqual(d.get('new rec', txn=self.txn), None)
740        d.put('new rec', 'this is a new record', self.txn)
741        self.assertEqual(d.get('new rec', txn=self.txn),
742                'this is a new record')
743        self.txn.commit()
744        self.assertEqual(d.get('new rec'), 'this is a new record')
745
746        self.txn = self.env.txn_begin()
747        c = d.cursor(self.txn)
748        rec = c.first()
749        count = 0
750        while rec is not None:
751            count = count + 1
752            if verbose and count % 100 == 0:
753                print rec
754            rec = c.next()
755        self.assertEqual(count, self._numKeys+1)
756
757        c.close()                # Cursors *MUST* be closed before commit!
758        self.txn.commit()
759
760        # flush pending updates
761        self.env.txn_checkpoint (0, 0, 0)
762
763        statDict = self.env.log_stat(0);
764        self.assertIn('magic', statDict)
765        self.assertIn('version', statDict)
766        self.assertIn('cur_file', statDict)
767        self.assertIn('region_nowait', statDict)
768
769        # must have at least one log file present:
770        logs = self.env.log_archive(db.DB_ARCH_ABS | db.DB_ARCH_LOG)
771        self.assertNotEqual(logs, None)
772        for log in logs:
773            if verbose:
774                print 'log file: ' + log
775            logs = self.env.log_archive(db.DB_ARCH_REMOVE)
776            self.assertTrue(not logs)
777
778        self.txn = self.env.txn_begin()
779
780    #----------------------------------------
781
782    if db.version() >= (4, 6):
783        def test08_exists(self) :
784            txn = self.env.txn_begin()
785            self.d.put("abcde", "ABCDE", txn=txn)
786            txn.commit()
787            txn = self.env.txn_begin()
788            self.assertTrue(self.d.exists("abcde", txn=txn) == True,
789                    "DB->exists() returns wrong value")
790            self.assertTrue(self.d.exists("x", txn=txn) == False,
791                    "DB->exists() returns wrong value")
792            txn.abort()
793
794    #----------------------------------------
795
796    def test09_TxnTruncate(self):
797        d = self.d
798        if verbose:
799            print '\n', '-=' * 30
800            print "Running %s.test09_TxnTruncate..." % self.__class__.__name__
801
802        d.put("abcde", "ABCDE");
803        txn = self.env.txn_begin()
804        num = d.truncate(txn)
805        self.assertTrue(num >= 1, "truncate returned <= 0 on non-empty database")
806        num = d.truncate(txn)
807        self.assertEqual(num, 0,
808                "truncate on empty DB returned nonzero (%r)" % (num,))
809        txn.commit()
810
811    #----------------------------------------
812
813    def test10_TxnLateUse(self):
814        txn = self.env.txn_begin()
815        txn.abort()
816        try:
817            txn.abort()
818        except db.DBError, e:
819            pass
820        else:
821            raise RuntimeError, "DBTxn.abort() called after DB_TXN no longer valid w/o an exception"
822
823        txn = self.env.txn_begin()
824        txn.commit()
825        try:
826            txn.commit()
827        except db.DBError, e:
828            pass
829        else:
830            raise RuntimeError, "DBTxn.commit() called after DB_TXN no longer valid w/o an exception"
831
832
833    #----------------------------------------
834
835
836    if db.version() >= (4, 4):
837        def test_txn_name(self) :
838            txn=self.env.txn_begin()
839            self.assertEqual(txn.get_name(), "")
840            txn.set_name("XXYY")
841            self.assertEqual(txn.get_name(), "XXYY")
842            txn.set_name("")
843            self.assertEqual(txn.get_name(), "")
844            txn.abort()
845
846    #----------------------------------------
847
848
849        def test_txn_set_timeout(self) :
850            txn=self.env.txn_begin()
851            txn.set_timeout(1234567, db.DB_SET_LOCK_TIMEOUT)
852            txn.set_timeout(2345678, flags=db.DB_SET_TXN_TIMEOUT)
853            txn.abort()
854
855    #----------------------------------------
856
857        def test_get_tx_max(self) :
858            self.assertEqual(self.env.get_tx_max(), 30)
859
860        def test_get_tx_timestamp(self) :
861            self.assertEqual(self.env.get_tx_timestamp(), self._t)
862
863
864
865class BTreeTransactionTestCase(BasicTransactionTestCase):
866    dbtype = db.DB_BTREE
867
868class HashTransactionTestCase(BasicTransactionTestCase):
869    dbtype = db.DB_HASH
870
871
872
873#----------------------------------------------------------------------
874
875class BTreeRecnoTestCase(BasicTestCase):
876    dbtype     = db.DB_BTREE
877    dbsetflags = db.DB_RECNUM
878
879    def test09_RecnoInBTree(self):
880        d = self.d
881        if verbose:
882            print '\n', '-=' * 30
883            print "Running %s.test09_RecnoInBTree..." % self.__class__.__name__
884
885        rec = d.get(200)
886        self.assertEqual(type(rec), type(()))
887        self.assertEqual(len(rec), 2)
888        if verbose:
889            print "Record #200 is ", rec
890
891        c = d.cursor()
892        c.set('0200')
893        num = c.get_recno()
894        self.assertEqual(type(num), type(1))
895        if verbose:
896            print "recno of d['0200'] is ", num
897
898        rec = c.current()
899        self.assertEqual(c.set_recno(num), rec)
900
901        c.close()
902
903
904
905class BTreeRecnoWithThreadFlagTestCase(BTreeRecnoTestCase):
906    dbopenflags = db.DB_THREAD
907
908#----------------------------------------------------------------------
909
910class BasicDUPTestCase(BasicTestCase):
911    dbsetflags = db.DB_DUP
912
913    def test10_DuplicateKeys(self):
914        d = self.d
915        if verbose:
916            print '\n', '-=' * 30
917            print "Running %s.test10_DuplicateKeys..." % \
918                  self.__class__.__name__
919
920        d.put("dup0", "before")
921        for x in "The quick brown fox jumped over the lazy dog.".split():
922            d.put("dup1", x)
923        d.put("dup2", "after")
924
925        data = d.get("dup1")
926        self.assertEqual(data, "The")
927        if verbose:
928            print data
929
930        c = d.cursor()
931        rec = c.set("dup1")
932        self.assertEqual(rec, ('dup1', 'The'))
933
934        next_reg = c.next()
935        self.assertEqual(next_reg, ('dup1', 'quick'))
936
937        rec = c.set("dup1")
938        count = c.count()
939        self.assertEqual(count, 9)
940
941        next_dup = c.next_dup()
942        self.assertEqual(next_dup, ('dup1', 'quick'))
943
944        rec = c.set('dup1')
945        while rec is not None:
946            if verbose:
947                print rec
948            rec = c.next_dup()
949
950        c.set('dup1')
951        rec = c.next_nodup()
952        self.assertNotEqual(rec[0], 'dup1')
953        if verbose:
954            print rec
955
956        c.close()
957
958
959
960class BTreeDUPTestCase(BasicDUPTestCase):
961    dbtype = db.DB_BTREE
962
963class HashDUPTestCase(BasicDUPTestCase):
964    dbtype = db.DB_HASH
965
966class BTreeDUPWithThreadTestCase(BasicDUPTestCase):
967    dbtype = db.DB_BTREE
968    dbopenflags = db.DB_THREAD
969
970class HashDUPWithThreadTestCase(BasicDUPTestCase):
971    dbtype = db.DB_HASH
972    dbopenflags = db.DB_THREAD
973
974
975#----------------------------------------------------------------------
976
977class BasicMultiDBTestCase(BasicTestCase):
978    dbname = 'first'
979
980    def otherType(self):
981        if self.dbtype == db.DB_BTREE:
982            return db.DB_HASH
983        else:
984            return db.DB_BTREE
985
986    def test11_MultiDB(self):
987        d1 = self.d
988        if verbose:
989            print '\n', '-=' * 30
990            print "Running %s.test11_MultiDB..." % self.__class__.__name__
991
992        d2 = db.DB(self.env)
993        d2.open(self.filename, "second", self.dbtype,
994                self.dbopenflags|db.DB_CREATE)
995        d3 = db.DB(self.env)
996        d3.open(self.filename, "third", self.otherType(),
997                self.dbopenflags|db.DB_CREATE)
998
999        for x in "The quick brown fox jumped over the lazy dog".split():
1000            d2.put(x, self.makeData(x))
1001
1002        for x in string.ascii_letters:
1003            d3.put(x, x*70)
1004
1005        d1.sync()
1006        d2.sync()
1007        d3.sync()
1008        d1.close()
1009        d2.close()
1010        d3.close()
1011
1012        self.d = d1 = d2 = d3 = None
1013
1014        self.d = d1 = db.DB(self.env)
1015        d1.open(self.filename, self.dbname, flags = self.dbopenflags)
1016        d2 = db.DB(self.env)
1017        d2.open(self.filename, "second",  flags = self.dbopenflags)
1018        d3 = db.DB(self.env)
1019        d3.open(self.filename, "third", flags = self.dbopenflags)
1020
1021        c1 = d1.cursor()
1022        c2 = d2.cursor()
1023        c3 = d3.cursor()
1024
1025        count = 0
1026        rec = c1.first()
1027        while rec is not None:
1028            count = count + 1
1029            if verbose and (count % 50) == 0:
1030                print rec
1031            rec = c1.next()
1032        self.assertEqual(count, self._numKeys)
1033
1034        count = 0
1035        rec = c2.first()
1036        while rec is not None:
1037            count = count + 1
1038            if verbose:
1039                print rec
1040            rec = c2.next()
1041        self.assertEqual(count, 9)
1042
1043        count = 0
1044        rec = c3.first()
1045        while rec is not None:
1046            count = count + 1
1047            if verbose:
1048                print rec
1049            rec = c3.next()
1050        self.assertEqual(count, len(string.ascii_letters))
1051
1052
1053        c1.close()
1054        c2.close()
1055        c3.close()
1056
1057        d2.close()
1058        d3.close()
1059
1060
1061
1062# Strange things happen if you try to use Multiple DBs per file without a
1063# DBEnv with MPOOL and LOCKing...
1064
1065class BTreeMultiDBTestCase(BasicMultiDBTestCase):
1066    dbtype = db.DB_BTREE
1067    dbopenflags = db.DB_THREAD
1068    useEnv = 1
1069    envflags = db.DB_THREAD | db.DB_INIT_MPOOL | db.DB_INIT_LOCK
1070
1071class HashMultiDBTestCase(BasicMultiDBTestCase):
1072    dbtype = db.DB_HASH
1073    dbopenflags = db.DB_THREAD
1074    useEnv = 1
1075    envflags = db.DB_THREAD | db.DB_INIT_MPOOL | db.DB_INIT_LOCK
1076
1077
1078class PrivateObject(unittest.TestCase) :
1079    def tearDown(self) :
1080        del self.obj
1081
1082    def test01_DefaultIsNone(self) :
1083        self.assertEqual(self.obj.get_private(), None)
1084
1085    def test02_assignment(self) :
1086        a = "example of private object"
1087        self.obj.set_private(a)
1088        b = self.obj.get_private()
1089        self.assertTrue(a is b)  # Object identity
1090
1091    def test03_leak_assignment(self) :
1092        a = "example of private object"
1093        refcount = sys.getrefcount(a)
1094        self.obj.set_private(a)
1095        self.assertEqual(refcount+1, sys.getrefcount(a))
1096        self.obj.set_private(None)
1097        self.assertEqual(refcount, sys.getrefcount(a))
1098
1099    def test04_leak_GC(self) :
1100        a = "example of private object"
1101        refcount = sys.getrefcount(a)
1102        self.obj.set_private(a)
1103        self.obj = None
1104        self.assertEqual(refcount, sys.getrefcount(a))
1105
1106class DBEnvPrivateObject(PrivateObject) :
1107    def setUp(self) :
1108        self.obj = db.DBEnv()
1109
1110class DBPrivateObject(PrivateObject) :
1111    def setUp(self) :
1112        self.obj = db.DB()
1113
1114class CrashAndBurn(unittest.TestCase) :
1115    #def test01_OpenCrash(self) :
1116    #    # See http://bugs.python.org/issue3307
1117    #    self.assertRaises(db.DBInvalidArgError, db.DB, None, 65535)
1118
1119    if db.version() < (4, 8) :
1120        def test02_DBEnv_dealloc(self):
1121            # http://bugs.python.org/issue3885
1122            import gc
1123            self.assertRaises(db.DBInvalidArgError, db.DBEnv, ~db.DB_RPCCLIENT)
1124            gc.collect()
1125
1126
1127#----------------------------------------------------------------------
1128#----------------------------------------------------------------------
1129
1130def test_suite():
1131    suite = unittest.TestSuite()
1132
1133    suite.addTest(unittest.makeSuite(VersionTestCase))
1134    suite.addTest(unittest.makeSuite(BasicBTreeTestCase))
1135    suite.addTest(unittest.makeSuite(BasicHashTestCase))
1136    suite.addTest(unittest.makeSuite(BasicBTreeWithThreadFlagTestCase))
1137    suite.addTest(unittest.makeSuite(BasicHashWithThreadFlagTestCase))
1138    suite.addTest(unittest.makeSuite(BasicBTreeWithEnvTestCase))
1139    suite.addTest(unittest.makeSuite(BasicHashWithEnvTestCase))
1140    suite.addTest(unittest.makeSuite(BTreeTransactionTestCase))
1141    suite.addTest(unittest.makeSuite(HashTransactionTestCase))
1142    suite.addTest(unittest.makeSuite(BTreeRecnoTestCase))
1143    suite.addTest(unittest.makeSuite(BTreeRecnoWithThreadFlagTestCase))
1144    suite.addTest(unittest.makeSuite(BTreeDUPTestCase))
1145    suite.addTest(unittest.makeSuite(HashDUPTestCase))
1146    suite.addTest(unittest.makeSuite(BTreeDUPWithThreadTestCase))
1147    suite.addTest(unittest.makeSuite(HashDUPWithThreadTestCase))
1148    suite.addTest(unittest.makeSuite(BTreeMultiDBTestCase))
1149    suite.addTest(unittest.makeSuite(HashMultiDBTestCase))
1150    suite.addTest(unittest.makeSuite(DBEnvPrivateObject))
1151    suite.addTest(unittest.makeSuite(DBPrivateObject))
1152    suite.addTest(unittest.makeSuite(CrashAndBurn))
1153
1154    return suite
1155
1156
1157if __name__ == '__main__':
1158    unittest.main(defaultTest='test_suite')
1159