Home | History | Annotate | Download | only in test
      1 """
      2 Basic TestCases for BTree and hash DBs, with and without a DBEnv, with
      3 various DB flags, etc.
      4 """
      5 
      6 import os
      7 import errno
      8 import string
      9 from pprint import pprint
     10 import unittest
     11 import time
     12 import sys
     13 
     14 from test_all import db, test_support, verbose, get_new_environment_path, \
     15         get_new_database_path
     16 
     17 DASH = '-'
     18 
     19 
     20 #----------------------------------------------------------------------
     21 
     22 class 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 
     35 class 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 
    647 class BasicBTreeTestCase(BasicTestCase):
    648     dbtype = db.DB_BTREE
    649 
    650 
    651 class BasicHashTestCase(BasicTestCase):
    652     dbtype = db.DB_HASH
    653 
    654 
    655 class BasicBTreeWithThreadFlagTestCase(BasicTestCase):
    656     dbtype = db.DB_BTREE
    657     dbopenflags = db.DB_THREAD
    658 
    659 
    660 class BasicHashWithThreadFlagTestCase(BasicTestCase):
    661     dbtype = db.DB_HASH
    662     dbopenflags = db.DB_THREAD
    663 
    664 
    665 class 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 
    689 class BasicBTreeWithEnvTestCase(BasicWithEnvTestCase):
    690     dbtype = db.DB_BTREE
    691 
    692 
    693 class BasicHashWithEnvTestCase(BasicWithEnvTestCase):
    694     dbtype = db.DB_HASH
    695 
    696 
    697 #----------------------------------------------------------------------
    698 
    699 class 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 
    865 class BTreeTransactionTestCase(BasicTransactionTestCase):
    866     dbtype = db.DB_BTREE
    867 
    868 class HashTransactionTestCase(BasicTransactionTestCase):
    869     dbtype = db.DB_HASH
    870 
    871 
    872 
    873 #----------------------------------------------------------------------
    874 
    875 class 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 
    905 class BTreeRecnoWithThreadFlagTestCase(BTreeRecnoTestCase):
    906     dbopenflags = db.DB_THREAD
    907 
    908 #----------------------------------------------------------------------
    909 
    910 class 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 
    960 class BTreeDUPTestCase(BasicDUPTestCase):
    961     dbtype = db.DB_BTREE
    962 
    963 class HashDUPTestCase(BasicDUPTestCase):
    964     dbtype = db.DB_HASH
    965 
    966 class BTreeDUPWithThreadTestCase(BasicDUPTestCase):
    967     dbtype = db.DB_BTREE
    968     dbopenflags = db.DB_THREAD
    969 
    970 class HashDUPWithThreadTestCase(BasicDUPTestCase):
    971     dbtype = db.DB_HASH
    972     dbopenflags = db.DB_THREAD
    973 
    974 
    975 #----------------------------------------------------------------------
    976 
    977 class 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.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.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 
   1065 class 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 
   1071 class 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 
   1078 class 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 
   1106 class DBEnvPrivateObject(PrivateObject) :
   1107     def setUp(self) :
   1108         self.obj = db.DBEnv()
   1109 
   1110 class DBPrivateObject(PrivateObject) :
   1111     def setUp(self) :
   1112         self.obj = db.DB()
   1113 
   1114 class 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 
   1130 def 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 
   1157 if __name__ == '__main__':
   1158     unittest.main(defaultTest='test_suite')
   1159