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