|
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 |
|
13 from test_all import db, test_support, verbose, get_new_environment_path, \ |
|
14 get_new_database_path |
|
15 |
|
16 DASH = '-' |
|
17 |
|
18 |
|
19 #---------------------------------------------------------------------- |
|
20 |
|
21 class 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 |
|
34 class 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-existant 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 |
|
589 class BasicBTreeTestCase(BasicTestCase): |
|
590 dbtype = db.DB_BTREE |
|
591 |
|
592 |
|
593 class BasicHashTestCase(BasicTestCase): |
|
594 dbtype = db.DB_HASH |
|
595 |
|
596 |
|
597 class BasicBTreeWithThreadFlagTestCase(BasicTestCase): |
|
598 dbtype = db.DB_BTREE |
|
599 dbopenflags = db.DB_THREAD |
|
600 |
|
601 |
|
602 class BasicHashWithThreadFlagTestCase(BasicTestCase): |
|
603 dbtype = db.DB_HASH |
|
604 dbopenflags = db.DB_THREAD |
|
605 |
|
606 |
|
607 class 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 |
|
635 class BasicBTreeWithEnvTestCase(BasicWithEnvTestCase): |
|
636 dbtype = db.DB_BTREE |
|
637 |
|
638 |
|
639 class BasicHashWithEnvTestCase(BasicWithEnvTestCase): |
|
640 dbtype = db.DB_HASH |
|
641 |
|
642 |
|
643 #---------------------------------------------------------------------- |
|
644 |
|
645 class 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 |
|
769 class BTreeTransactionTestCase(BasicTransactionTestCase): |
|
770 dbtype = db.DB_BTREE |
|
771 |
|
772 class HashTransactionTestCase(BasicTransactionTestCase): |
|
773 dbtype = db.DB_HASH |
|
774 |
|
775 |
|
776 |
|
777 #---------------------------------------------------------------------- |
|
778 |
|
779 class 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 |
|
809 class BTreeRecnoWithThreadFlagTestCase(BTreeRecnoTestCase): |
|
810 dbopenflags = db.DB_THREAD |
|
811 |
|
812 #---------------------------------------------------------------------- |
|
813 |
|
814 class 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 |
|
864 class BTreeDUPTestCase(BasicDUPTestCase): |
|
865 dbtype = db.DB_BTREE |
|
866 |
|
867 class HashDUPTestCase(BasicDUPTestCase): |
|
868 dbtype = db.DB_HASH |
|
869 |
|
870 class BTreeDUPWithThreadTestCase(BasicDUPTestCase): |
|
871 dbtype = db.DB_BTREE |
|
872 dbopenflags = db.DB_THREAD |
|
873 |
|
874 class HashDUPWithThreadTestCase(BasicDUPTestCase): |
|
875 dbtype = db.DB_HASH |
|
876 dbopenflags = db.DB_THREAD |
|
877 |
|
878 |
|
879 #---------------------------------------------------------------------- |
|
880 |
|
881 class 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 |
|
969 class 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 |
|
975 class 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 |
|
982 class 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 |
|
1017 class DBEnvPrivateObject(PrivateObject) : |
|
1018 def setUp(self) : |
|
1019 self.obj = db.DBEnv() |
|
1020 |
|
1021 class DBPrivateObject(PrivateObject) : |
|
1022 def setUp(self) : |
|
1023 self.obj = db.DB() |
|
1024 |
|
1025 class 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 |
|
1045 def 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 |
|
1072 if __name__ == '__main__': |
|
1073 unittest.main(defaultTest='test_suite') |