blob: dbab2313b08c66a8490e7fe31892deef4f4508c9 [file] [log] [blame]
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +00001"""
2Basic TestCases for BTree and hash DBs, with and without a DBEnv, with
3various DB flags, etc.
4"""
5
Barry Warsaw9a0d7792002-12-30 20:53:52 +00006import os
7import sys
8import errno
9import shutil
10import string
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +000011import tempfile
12from pprint import pprint
13import unittest
14
Barry Warsawf71de3e2003-01-28 17:20:44 +000015try:
16 # For Python 2.3
17 from bsddb import db
18except ImportError:
19 # For earlier Pythons w/distutils pybsddb
20 from bsddb3 import db
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +000021
Barry Warsaw9a0d7792002-12-30 20:53:52 +000022from test_all import verbose
23
24DASH = '-'
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +000025
26
27#----------------------------------------------------------------------
28
29class VersionTestCase(unittest.TestCase):
30 def test00_version(self):
31 info = db.version()
32 if verbose:
33 print '\n', '-=' * 20
34 print 'bsddb.db.version(): %s' % (info, )
35 print db.DB_VERSION_STRING
36 print '-=' * 20
Barry Warsaw9a0d7792002-12-30 20:53:52 +000037 assert info == (db.DB_VERSION_MAJOR, db.DB_VERSION_MINOR,
38 db.DB_VERSION_PATCH)
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +000039
40#----------------------------------------------------------------------
41
42class BasicTestCase(unittest.TestCase):
43 dbtype = db.DB_UNKNOWN # must be set in derived class
44 dbopenflags = 0
45 dbsetflags = 0
46 dbmode = 0660
47 dbname = None
48 useEnv = 0
49 envflags = 0
Barry Warsaw9a0d7792002-12-30 20:53:52 +000050 envsetflags = 0
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +000051
52 def setUp(self):
53 if self.useEnv:
54 homeDir = os.path.join(os.path.dirname(sys.argv[0]), 'db_home')
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +000055 self.homeDir = homeDir
Barry Warsaw9a0d7792002-12-30 20:53:52 +000056 try:
57 shutil.rmtree(homeDir)
58 except OSError, e:
59 # unix returns ENOENT, windows returns ESRCH
60 if e.errno not in (errno.ENOENT, errno.ESRCH): raise
61 os.mkdir(homeDir)
62 try:
63 self.env = db.DBEnv()
64 self.env.set_lg_max(1024*1024)
65 self.env.set_flags(self.envsetflags, 1)
66 self.env.open(homeDir, self.envflags | db.DB_CREATE)
67 tempfile.tempdir = homeDir
68 self.filename = os.path.split(tempfile.mktemp())[1]
69 tempfile.tempdir = None
70 # Yes, a bare except is intended, since we're re-raising the exc.
71 except:
72 shutil.rmtree(homeDir)
73 raise
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +000074 else:
75 self.env = None
76 self.filename = tempfile.mktemp()
77
78 # create and open the DB
79 self.d = db.DB(self.env)
80 self.d.set_flags(self.dbsetflags)
81 if self.dbname:
82 self.d.open(self.filename, self.dbname, self.dbtype,
83 self.dbopenflags|db.DB_CREATE, self.dbmode)
84 else:
85 self.d.open(self.filename, # try out keyword args
86 mode = self.dbmode,
Barry Warsaw9a0d7792002-12-30 20:53:52 +000087 dbtype = self.dbtype,
88 flags = self.dbopenflags|db.DB_CREATE)
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +000089
90 self.populateDB()
91
92
93 def tearDown(self):
94 self.d.close()
95 if self.env is not None:
96 self.env.close()
Barry Warsaw9a0d7792002-12-30 20:53:52 +000097 shutil.rmtree(self.homeDir)
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +000098 ## Make a new DBEnv to remove the env files from the home dir.
99 ## (It can't be done while the env is open, nor after it has been
100 ## closed, so we make a new one to do it.)
101 #e = db.DBEnv()
102 #e.remove(self.homeDir)
103 #os.remove(os.path.join(self.homeDir, self.filename))
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000104 else:
105 os.remove(self.filename)
106
107
108
109 def populateDB(self):
110 d = self.d
111 for x in range(500):
112 key = '%04d' % (1000 - x) # insert keys in reverse order
113 data = self.makeData(key)
114 d.put(key, data)
115
116 for x in range(500):
117 key = '%04d' % x # and now some in forward order
118 data = self.makeData(key)
119 d.put(key, data)
120
121 num = len(d)
122 if verbose:
123 print "created %d records" % num
124
125
126 def makeData(self, key):
Barry Warsaw9a0d7792002-12-30 20:53:52 +0000127 return DASH.join([key] * 5)
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000128
129
130
131 #----------------------------------------
132
133 def test01_GetsAndPuts(self):
134 d = self.d
135
136 if verbose:
137 print '\n', '-=' * 30
138 print "Running %s.test01_GetsAndPuts..." % self.__class__.__name__
139
140 for key in ['0001', '0100', '0400', '0700', '0999']:
141 data = d.get(key)
142 if verbose:
143 print data
144
145 assert d.get('0321') == '0321-0321-0321-0321-0321'
146
147 # By default non-existant keys return None...
148 assert d.get('abcd') == None
149
150 # ...but they raise exceptions in other situations. Call
151 # set_get_returns_none() to change it.
152 try:
153 d.delete('abcd')
154 except db.DBNotFoundError, val:
155 assert val[0] == db.DB_NOTFOUND
156 if verbose: print val
157 else:
158 self.fail("expected exception")
159
160
161 d.put('abcd', 'a new record')
162 assert d.get('abcd') == 'a new record'
163
164 d.put('abcd', 'same key')
165 if self.dbsetflags & db.DB_DUP:
166 assert d.get('abcd') == 'a new record'
167 else:
168 assert d.get('abcd') == 'same key'
169
170
171 try:
172 d.put('abcd', 'this should fail', flags=db.DB_NOOVERWRITE)
173 except db.DBKeyExistError, val:
174 assert val[0] == db.DB_KEYEXIST
175 if verbose: print val
176 else:
177 self.fail("expected exception")
178
179 if self.dbsetflags & db.DB_DUP:
180 assert d.get('abcd') == 'a new record'
181 else:
182 assert d.get('abcd') == 'same key'
183
184
185 d.sync()
186 d.close()
187 del d
188
189 self.d = db.DB(self.env)
190 if self.dbname:
191 self.d.open(self.filename, self.dbname)
192 else:
193 self.d.open(self.filename)
194 d = self.d
195
196 assert d.get('0321') == '0321-0321-0321-0321-0321'
197 if self.dbsetflags & db.DB_DUP:
198 assert d.get('abcd') == 'a new record'
199 else:
200 assert d.get('abcd') == 'same key'
201
202 rec = d.get_both('0555', '0555-0555-0555-0555-0555')
203 if verbose:
204 print rec
205
206 assert d.get_both('0555', 'bad data') == None
207
208 # test default value
209 data = d.get('bad key', 'bad data')
210 assert data == 'bad data'
211
212 # any object can pass through
213 data = d.get('bad key', self)
214 assert data == self
215
216 s = d.stat()
217 assert type(s) == type({})
218 if verbose:
219 print 'd.stat() returned this dictionary:'
220 pprint(s)
221
222
223 #----------------------------------------
224
225 def test02_DictionaryMethods(self):
226 d = self.d
227
228 if verbose:
229 print '\n', '-=' * 30
Barry Warsaw9a0d7792002-12-30 20:53:52 +0000230 print "Running %s.test02_DictionaryMethods..." % \
231 self.__class__.__name__
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000232
233 for key in ['0002', '0101', '0401', '0701', '0998']:
234 data = d[key]
235 assert data == self.makeData(key)
236 if verbose:
237 print data
238
239 assert len(d) == 1000
240 keys = d.keys()
241 assert len(keys) == 1000
242 assert type(keys) == type([])
243
244 d['new record'] = 'a new record'
245 assert len(d) == 1001
246 keys = d.keys()
247 assert len(keys) == 1001
248
249 d['new record'] = 'a replacement record'
250 assert len(d) == 1001
251 keys = d.keys()
252 assert len(keys) == 1001
253
254 if verbose:
255 print "the first 10 keys are:"
256 pprint(keys[:10])
257
258 assert d['new record'] == 'a replacement record'
259
260 assert d.has_key('0001') == 1
261 assert d.has_key('spam') == 0
262
263 items = d.items()
264 assert len(items) == 1001
265 assert type(items) == type([])
266 assert type(items[0]) == type(())
267 assert len(items[0]) == 2
268
269 if verbose:
270 print "the first 10 items are:"
271 pprint(items[:10])
272
273 values = d.values()
274 assert len(values) == 1001
275 assert type(values) == type([])
276
277 if verbose:
278 print "the first 10 values are:"
279 pprint(values[:10])
280
281
282
283 #----------------------------------------
284
Gregory P. Smith455d46f2003-07-09 04:45:59 +0000285 def test03_SimpleCursorStuff(self, get_raises_error=0, set_raises_error=1):
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000286 if verbose:
287 print '\n', '-=' * 30
Gregory P. Smith455d46f2003-07-09 04:45:59 +0000288 print "Running %s.test03_SimpleCursorStuff (get_error %s, set_error %s)..." % \
289 (self.__class__.__name__, get_raises_error, set_raises_error)
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000290
Barry Warsaw9a0d7792002-12-30 20:53:52 +0000291 if self.env and self.dbopenflags & db.DB_AUTO_COMMIT:
292 txn = self.env.txn_begin()
293 else:
294 txn = None
295 c = self.d.cursor(txn=txn)
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000296
297 rec = c.first()
298 count = 0
299 while rec is not None:
300 count = count + 1
301 if verbose and count % 100 == 0:
302 print rec
Gregory P. Smith455d46f2003-07-09 04:45:59 +0000303 try:
304 rec = c.next()
305 except db.DBNotFoundError, val:
306 if get_raises_error:
307 assert val[0] == db.DB_NOTFOUND
308 if verbose: print val
309 rec = None
310 else:
311 self.fail("unexpected DBNotFoundError")
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000312
313 assert count == 1000
314
315
316 rec = c.last()
317 count = 0
318 while rec is not None:
319 count = count + 1
320 if verbose and count % 100 == 0:
321 print rec
Gregory P. Smith455d46f2003-07-09 04:45:59 +0000322 try:
323 rec = c.prev()
324 except db.DBNotFoundError, val:
325 if get_raises_error:
326 assert val[0] == db.DB_NOTFOUND
327 if verbose: print val
328 rec = None
329 else:
330 self.fail("unexpected DBNotFoundError")
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000331
332 assert count == 1000
333
334 rec = c.set('0505')
335 rec2 = c.current()
336 assert rec == rec2
337 assert rec[0] == '0505'
338 assert rec[1] == self.makeData('0505')
339
340 try:
Gregory P. Smith455d46f2003-07-09 04:45:59 +0000341 n = c.set('bad key')
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000342 except db.DBNotFoundError, val:
343 assert val[0] == db.DB_NOTFOUND
344 if verbose: print val
345 else:
Gregory P. Smith455d46f2003-07-09 04:45:59 +0000346 if set_raises_error:
347 self.fail("expected exception")
348 if n != None:
349 self.fail("expected None: "+`n`)
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000350
351 rec = c.get_both('0404', self.makeData('0404'))
352 assert rec == ('0404', self.makeData('0404'))
353
354 try:
Gregory P. Smith455d46f2003-07-09 04:45:59 +0000355 n = c.get_both('0404', 'bad data')
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000356 except db.DBNotFoundError, val:
357 assert val[0] == db.DB_NOTFOUND
358 if verbose: print val
359 else:
Gregory P. Smith455d46f2003-07-09 04:45:59 +0000360 if get_raises_error:
361 self.fail("expected exception")
362 if n != None:
363 self.fail("expected None: "+`n`)
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000364
365 if self.d.get_type() == db.DB_BTREE:
366 rec = c.set_range('011')
367 if verbose:
368 print "searched for '011', found: ", rec
369
370 rec = c.set_range('011',dlen=0,doff=0)
371 if verbose:
372 print "searched (partial) for '011', found: ", rec
373 if rec[1] != '': set.fail('expected empty data portion')
374
375 c.set('0499')
376 c.delete()
377 try:
378 rec = c.current()
379 except db.DBKeyEmptyError, val:
380 assert val[0] == db.DB_KEYEMPTY
381 if verbose: print val
382 else:
383 self.fail('exception expected')
384
385 c.next()
386 c2 = c.dup(db.DB_POSITION)
387 assert c.current() == c2.current()
388
389 c2.put('', 'a new value', db.DB_CURRENT)
390 assert c.current() == c2.current()
391 assert c.current()[1] == 'a new value'
392
393 c2.put('', 'er', db.DB_CURRENT, dlen=0, doff=5)
394 assert c2.current()[1] == 'a newer value'
395
396 c.close()
397 c2.close()
Barry Warsaw9a0d7792002-12-30 20:53:52 +0000398 if txn:
399 txn.commit()
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000400
401 # time to abuse the closed cursors and hope we don't crash
402 methods_to_test = {
403 'current': (),
404 'delete': (),
405 'dup': (db.DB_POSITION,),
406 'first': (),
407 'get': (0,),
408 'next': (),
409 'prev': (),
410 'last': (),
411 'put':('', 'spam', db.DB_CURRENT),
412 'set': ("0505",),
413 }
414 for method, args in methods_to_test.items():
415 try:
416 if verbose:
Barry Warsaw9a0d7792002-12-30 20:53:52 +0000417 print "attempting to use a closed cursor's %s method" % \
418 method
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000419 # a bug may cause a NULL pointer dereference...
420 apply(getattr(c, method), args)
421 except db.DBError, val:
422 assert val[0] == 0
423 if verbose: print val
424 else:
Barry Warsaw9a0d7792002-12-30 20:53:52 +0000425 self.fail("no exception raised when using a buggy cursor's"
426 "%s method" % method)
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000427
Gregory P. Smithb6c9f782003-01-17 08:42:50 +0000428 #
429 # free cursor referencing a closed database, it should not barf:
430 #
431 oldcursor = self.d.cursor(txn=txn)
432 self.d.close()
433
434 # this would originally cause a segfault when the cursor for a
435 # closed database was cleaned up. it should not anymore.
436 # SF pybsddb bug id 667343
437 del oldcursor
438
Gregory P. Smith455d46f2003-07-09 04:45:59 +0000439 def test03b_SimpleCursorWithoutGetReturnsNone0(self):
440 # same test but raise exceptions instead of returning None
441 if verbose:
442 print '\n', '-=' * 30
443 print "Running %s.test03b_SimpleCursorStuffWithoutGetReturnsNone..." % \
444 self.__class__.__name__
445
446 old = self.d.set_get_returns_none(0)
447 assert old == 1
448 self.test03_SimpleCursorStuff(get_raises_error=1, set_raises_error=1)
449
450 def test03c_SimpleCursorGetReturnsNone2(self):
451 # same test but raise exceptions instead of returning None
452 if verbose:
453 print '\n', '-=' * 30
454 print "Running %s.test03c_SimpleCursorStuffWithoutSetReturnsNone..." % \
455 self.__class__.__name__
456
457 old = self.d.set_get_returns_none(2)
458 assert old == 1
459 old = self.d.set_get_returns_none(2)
460 assert old == 2
461 self.test03_SimpleCursorStuff(get_raises_error=0, set_raises_error=0)
Gregory P. Smithb6c9f782003-01-17 08:42:50 +0000462
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000463 #----------------------------------------
464
465 def test04_PartialGetAndPut(self):
466 d = self.d
467 if verbose:
468 print '\n', '-=' * 30
Barry Warsaw9a0d7792002-12-30 20:53:52 +0000469 print "Running %s.test04_PartialGetAndPut..." % \
470 self.__class__.__name__
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000471
472 key = "partialTest"
473 data = "1" * 1000 + "2" * 1000
474 d.put(key, data)
475 assert d.get(key) == data
476 assert d.get(key, dlen=20, doff=990) == ("1" * 10) + ("2" * 10)
477
478 d.put("partialtest2", ("1" * 30000) + "robin" )
479 assert d.get("partialtest2", dlen=5, doff=30000) == "robin"
480
Barry Warsaw9a0d7792002-12-30 20:53:52 +0000481 # There seems to be a bug in DB here... Commented out the test for
482 # now.
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000483 ##assert d.get("partialtest2", dlen=5, doff=30010) == ""
484
485 if self.dbsetflags != db.DB_DUP:
486 # Partial put with duplicate records requires a cursor
487 d.put(key, "0000", dlen=2000, doff=0)
488 assert d.get(key) == "0000"
489
490 d.put(key, "1111", dlen=1, doff=2)
491 assert d.get(key) == "0011110"
492
493 #----------------------------------------
494
495 def test05_GetSize(self):
496 d = self.d
497 if verbose:
498 print '\n', '-=' * 30
499 print "Running %s.test05_GetSize..." % self.__class__.__name__
500
501 for i in range(1, 50000, 500):
502 key = "size%s" % i
503 #print "before ", i,
504 d.put(key, "1" * i)
505 #print "after",
506 assert d.get_size(key) == i
507 #print "done"
508
509 #----------------------------------------
510
511 def test06_Truncate(self):
Barry Warsaw9a0d7792002-12-30 20:53:52 +0000512 if db.version() < (3,3):
513 # truncate is a feature of BerkeleyDB 3.3 and above
514 return
515
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000516 d = self.d
517 if verbose:
518 print '\n', '-=' * 30
519 print "Running %s.test99_Truncate..." % self.__class__.__name__
520
521 d.put("abcde", "ABCDE");
522 num = d.truncate()
523 assert num >= 1, "truncate returned <= 0 on non-empty database"
524 num = d.truncate()
525 assert num == 0, "truncate on empty DB returned nonzero (%s)" % `num`
526
527#----------------------------------------------------------------------
528
529
530class BasicBTreeTestCase(BasicTestCase):
531 dbtype = db.DB_BTREE
532
533
534class BasicHashTestCase(BasicTestCase):
535 dbtype = db.DB_HASH
536
537
538class BasicBTreeWithThreadFlagTestCase(BasicTestCase):
539 dbtype = db.DB_BTREE
540 dbopenflags = db.DB_THREAD
541
542
543class BasicHashWithThreadFlagTestCase(BasicTestCase):
544 dbtype = db.DB_HASH
545 dbopenflags = db.DB_THREAD
546
547
548class BasicBTreeWithEnvTestCase(BasicTestCase):
549 dbtype = db.DB_BTREE
550 dbopenflags = db.DB_THREAD
551 useEnv = 1
552 envflags = db.DB_THREAD | db.DB_INIT_MPOOL | db.DB_INIT_LOCK
553
554
555class BasicHashWithEnvTestCase(BasicTestCase):
556 dbtype = db.DB_HASH
557 dbopenflags = db.DB_THREAD
558 useEnv = 1
559 envflags = db.DB_THREAD | db.DB_INIT_MPOOL | db.DB_INIT_LOCK
560
561
562#----------------------------------------------------------------------
563
564class BasicTransactionTestCase(BasicTestCase):
Barry Warsaw9a0d7792002-12-30 20:53:52 +0000565 dbopenflags = db.DB_THREAD | db.DB_AUTO_COMMIT
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000566 useEnv = 1
Barry Warsaw9a0d7792002-12-30 20:53:52 +0000567 envflags = (db.DB_THREAD | db.DB_INIT_MPOOL | db.DB_INIT_LOCK |
568 db.DB_INIT_TXN)
569 envsetflags = db.DB_AUTO_COMMIT
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000570
571
572 def tearDown(self):
573 self.txn.commit()
574 BasicTestCase.tearDown(self)
575
576
577 def populateDB(self):
578 d = self.d
579 txn = self.env.txn_begin()
580 for x in range(500):
581 key = '%04d' % (1000 - x) # insert keys in reverse order
582 data = self.makeData(key)
583 d.put(key, data, txn)
584
585 for x in range(500):
586 key = '%04d' % x # and now some in forward order
587 data = self.makeData(key)
588 d.put(key, data, txn)
589
590 txn.commit()
591
592 num = len(d)
593 if verbose:
594 print "created %d records" % num
595
596 self.txn = self.env.txn_begin()
597
598
599
600 def test06_Transactions(self):
601 d = self.d
602 if verbose:
603 print '\n', '-=' * 30
604 print "Running %s.test06_Transactions..." % self.__class__.__name__
605
606 assert d.get('new rec', txn=self.txn) == None
607 d.put('new rec', 'this is a new record', self.txn)
608 assert d.get('new rec', txn=self.txn) == 'this is a new record'
609 self.txn.abort()
610 assert d.get('new rec') == None
611
612 self.txn = self.env.txn_begin()
613
614 assert d.get('new rec', txn=self.txn) == None
615 d.put('new rec', 'this is a new record', self.txn)
616 assert d.get('new rec', txn=self.txn) == 'this is a new record'
617 self.txn.commit()
618 assert d.get('new rec') == 'this is a new record'
619
620 self.txn = self.env.txn_begin()
621 c = d.cursor(self.txn)
622 rec = c.first()
623 count = 0
624 while rec is not None:
625 count = count + 1
626 if verbose and count % 100 == 0:
627 print rec
628 rec = c.next()
629 assert count == 1001
630
631 c.close() # Cursors *MUST* be closed before commit!
632 self.txn.commit()
633
634 # flush pending updates
635 try:
636 self.env.txn_checkpoint (0, 0, 0)
637 except db.DBIncompleteError:
638 pass
639
640 # must have at least one log file present:
641 logs = self.env.log_archive(db.DB_ARCH_ABS | db.DB_ARCH_LOG)
642 assert logs != None
643 for log in logs:
644 if verbose:
645 print 'log file: ' + log
646
647 self.txn = self.env.txn_begin()
648
649 #----------------------------------------
650
651 def test07_TxnTruncate(self):
Barry Warsaw9a0d7792002-12-30 20:53:52 +0000652 if db.version() < (3,3):
653 # truncate is a feature of BerkeleyDB 3.3 and above
654 return
655
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000656 d = self.d
657 if verbose:
658 print '\n', '-=' * 30
659 print "Running %s.test07_TxnTruncate..." % self.__class__.__name__
660
661 d.put("abcde", "ABCDE");
662 txn = self.env.txn_begin()
663 num = d.truncate(txn)
664 assert num >= 1, "truncate returned <= 0 on non-empty database"
665 num = d.truncate(txn)
666 assert num == 0, "truncate on empty DB returned nonzero (%s)" % `num`
667 txn.commit()
668
Gregory P. Smithc25fd3f2003-01-17 07:52:59 +0000669 #----------------------------------------
670
671 def test08_TxnLateUse(self):
672 txn = self.env.txn_begin()
673 txn.abort()
674 try:
675 txn.abort()
676 except db.DBError, e:
677 pass
678 else:
679 raise RuntimeError, "DBTxn.abort() called after DB_TXN no longer valid w/o an exception"
680
681 txn = self.env.txn_begin()
682 txn.commit()
683 try:
684 txn.commit()
685 except db.DBError, e:
686 pass
687 else:
688 raise RuntimeError, "DBTxn.commit() called after DB_TXN no longer valid w/o an exception"
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000689
690
691class BTreeTransactionTestCase(BasicTransactionTestCase):
692 dbtype = db.DB_BTREE
693
694class HashTransactionTestCase(BasicTransactionTestCase):
695 dbtype = db.DB_HASH
696
697
698
699#----------------------------------------------------------------------
700
701class BTreeRecnoTestCase(BasicTestCase):
702 dbtype = db.DB_BTREE
703 dbsetflags = db.DB_RECNUM
704
705 def test07_RecnoInBTree(self):
706 d = self.d
707 if verbose:
708 print '\n', '-=' * 30
709 print "Running %s.test07_RecnoInBTree..." % self.__class__.__name__
710
711 rec = d.get(200)
712 assert type(rec) == type(())
713 assert len(rec) == 2
714 if verbose:
715 print "Record #200 is ", rec
716
717 c = d.cursor()
718 c.set('0200')
719 num = c.get_recno()
720 assert type(num) == type(1)
721 if verbose:
722 print "recno of d['0200'] is ", num
723
724 rec = c.current()
725 assert c.set_recno(num) == rec
726
727 c.close()
728
729
730
731class BTreeRecnoWithThreadFlagTestCase(BTreeRecnoTestCase):
732 dbopenflags = db.DB_THREAD
733
734#----------------------------------------------------------------------
735
736class BasicDUPTestCase(BasicTestCase):
737 dbsetflags = db.DB_DUP
738
739 def test08_DuplicateKeys(self):
740 d = self.d
741 if verbose:
742 print '\n', '-=' * 30
Barry Warsaw9a0d7792002-12-30 20:53:52 +0000743 print "Running %s.test08_DuplicateKeys..." % \
744 self.__class__.__name__
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000745
746 d.put("dup0", "before")
Barry Warsaw9a0d7792002-12-30 20:53:52 +0000747 for x in "The quick brown fox jumped over the lazy dog.".split():
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000748 d.put("dup1", x)
749 d.put("dup2", "after")
750
751 data = d.get("dup1")
752 assert data == "The"
753 if verbose:
754 print data
755
756 c = d.cursor()
757 rec = c.set("dup1")
758 assert rec == ('dup1', 'The')
759
760 next = c.next()
761 assert next == ('dup1', 'quick')
762
763 rec = c.set("dup1")
764 count = c.count()
765 assert count == 9
766
767 next_dup = c.next_dup()
768 assert next_dup == ('dup1', 'quick')
769
770 rec = c.set('dup1')
771 while rec is not None:
772 if verbose:
773 print rec
774 rec = c.next_dup()
775
776 c.set('dup1')
777 rec = c.next_nodup()
778 assert rec[0] != 'dup1'
779 if verbose:
780 print rec
781
782 c.close()
783
784
785
786class BTreeDUPTestCase(BasicDUPTestCase):
787 dbtype = db.DB_BTREE
788
789class HashDUPTestCase(BasicDUPTestCase):
790 dbtype = db.DB_HASH
791
792class BTreeDUPWithThreadTestCase(BasicDUPTestCase):
793 dbtype = db.DB_BTREE
794 dbopenflags = db.DB_THREAD
795
796class HashDUPWithThreadTestCase(BasicDUPTestCase):
797 dbtype = db.DB_HASH
798 dbopenflags = db.DB_THREAD
799
800
801#----------------------------------------------------------------------
802
803class BasicMultiDBTestCase(BasicTestCase):
804 dbname = 'first'
805
806 def otherType(self):
807 if self.dbtype == db.DB_BTREE:
808 return db.DB_HASH
809 else:
810 return db.DB_BTREE
811
812 def test09_MultiDB(self):
813 d1 = self.d
814 if verbose:
815 print '\n', '-=' * 30
816 print "Running %s.test09_MultiDB..." % self.__class__.__name__
817
818 d2 = db.DB(self.env)
Barry Warsaw9a0d7792002-12-30 20:53:52 +0000819 d2.open(self.filename, "second", self.dbtype,
820 self.dbopenflags|db.DB_CREATE)
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000821 d3 = db.DB(self.env)
Barry Warsaw9a0d7792002-12-30 20:53:52 +0000822 d3.open(self.filename, "third", self.otherType(),
823 self.dbopenflags|db.DB_CREATE)
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000824
Barry Warsaw9a0d7792002-12-30 20:53:52 +0000825 for x in "The quick brown fox jumped over the lazy dog".split():
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000826 d2.put(x, self.makeData(x))
827
828 for x in string.letters:
829 d3.put(x, x*70)
830
831 d1.sync()
832 d2.sync()
833 d3.sync()
834 d1.close()
835 d2.close()
836 d3.close()
837
838 self.d = d1 = d2 = d3 = None
839
840 self.d = d1 = db.DB(self.env)
841 d1.open(self.filename, self.dbname, flags = self.dbopenflags)
842 d2 = db.DB(self.env)
843 d2.open(self.filename, "second", flags = self.dbopenflags)
844 d3 = db.DB(self.env)
845 d3.open(self.filename, "third", flags = self.dbopenflags)
846
847 c1 = d1.cursor()
848 c2 = d2.cursor()
849 c3 = d3.cursor()
850
851 count = 0
852 rec = c1.first()
853 while rec is not None:
854 count = count + 1
855 if verbose and (count % 50) == 0:
856 print rec
857 rec = c1.next()
858 assert count == 1000
859
860 count = 0
861 rec = c2.first()
862 while rec is not None:
863 count = count + 1
864 if verbose:
865 print rec
866 rec = c2.next()
867 assert count == 9
868
869 count = 0
870 rec = c3.first()
871 while rec is not None:
872 count = count + 1
873 if verbose:
874 print rec
875 rec = c3.next()
876 assert count == 52
877
878
879 c1.close()
880 c2.close()
881 c3.close()
882
883 d2.close()
884 d3.close()
885
886
887
888# Strange things happen if you try to use Multiple DBs per file without a
889# DBEnv with MPOOL and LOCKing...
890
891class BTreeMultiDBTestCase(BasicMultiDBTestCase):
892 dbtype = db.DB_BTREE
893 dbopenflags = db.DB_THREAD
894 useEnv = 1
895 envflags = db.DB_THREAD | db.DB_INIT_MPOOL | db.DB_INIT_LOCK
896
897class HashMultiDBTestCase(BasicMultiDBTestCase):
898 dbtype = db.DB_HASH
899 dbopenflags = db.DB_THREAD
900 useEnv = 1
901 envflags = db.DB_THREAD | db.DB_INIT_MPOOL | db.DB_INIT_LOCK
902
903
904#----------------------------------------------------------------------
905#----------------------------------------------------------------------
906
Barry Warsaw9a0d7792002-12-30 20:53:52 +0000907def test_suite():
908 suite = unittest.TestSuite()
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000909
Barry Warsaw9a0d7792002-12-30 20:53:52 +0000910 suite.addTest(unittest.makeSuite(VersionTestCase))
911 suite.addTest(unittest.makeSuite(BasicBTreeTestCase))
912 suite.addTest(unittest.makeSuite(BasicHashTestCase))
913 suite.addTest(unittest.makeSuite(BasicBTreeWithThreadFlagTestCase))
914 suite.addTest(unittest.makeSuite(BasicHashWithThreadFlagTestCase))
915 suite.addTest(unittest.makeSuite(BasicBTreeWithEnvTestCase))
916 suite.addTest(unittest.makeSuite(BasicHashWithEnvTestCase))
917 suite.addTest(unittest.makeSuite(BTreeTransactionTestCase))
918 suite.addTest(unittest.makeSuite(HashTransactionTestCase))
919 suite.addTest(unittest.makeSuite(BTreeRecnoTestCase))
920 suite.addTest(unittest.makeSuite(BTreeRecnoWithThreadFlagTestCase))
921 suite.addTest(unittest.makeSuite(BTreeDUPTestCase))
922 suite.addTest(unittest.makeSuite(HashDUPTestCase))
923 suite.addTest(unittest.makeSuite(BTreeDUPWithThreadTestCase))
924 suite.addTest(unittest.makeSuite(HashDUPWithThreadTestCase))
925 suite.addTest(unittest.makeSuite(BTreeMultiDBTestCase))
926 suite.addTest(unittest.makeSuite(HashMultiDBTestCase))
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000927
Barry Warsaw9a0d7792002-12-30 20:53:52 +0000928 return suite
Martin v. Löwis1c6b1a22002-11-19 17:47:07 +0000929
930
931if __name__ == '__main__':
Barry Warsaw9a0d7792002-12-30 20:53:52 +0000932 unittest.main(defaultTest='test_suite')