PageRenderTime 2071ms CodeModel.GetById 245ms app.highlight 1298ms RepoModel.GetById 213ms app.codeStats 1ms

/Lib/bsddb/test/test_basics.py

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