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