blob: 666f92cf4926132a76eac6f57483cf94f0e3da7b [file] [log] [blame]
Roger E. Masse352e1861997-01-16 22:04:10 +00001#! /usr/bin/env python
Raymond Hettingerd55111f2003-09-13 05:51:09 +00002"""Test script for the bsddb C module by Roger E. Masse
3 Adapted to unittest format and expanded scope by Raymond Hettinger
Roger E. Masse352e1861997-01-16 22:04:10 +00004"""
Gregory P. Smithdc113a82003-11-02 09:10:16 +00005import os, sys
Gregory P. Smitha703a212003-11-03 01:04:41 +00006import copy
Roger E. Masse352e1861997-01-16 22:04:10 +00007import bsddb
Georg Brandl0a7ac7d2008-05-26 10:29:35 +00008import dbm.bsd # Just so we know it's imported
Raymond Hettingerd55111f2003-09-13 05:51:09 +00009import unittest
Benjamin Petersonee8712c2008-05-20 21:35:26 +000010from test import support
Roger E. Masse352e1861997-01-16 22:04:10 +000011
Raymond Hettingerd55111f2003-09-13 05:51:09 +000012class TestBSDDB(unittest.TestCase):
Thomas Wouters49fd7fa2006-04-21 10:40:58 +000013 openflag = 'c'
Roger E. Masse352e1861997-01-16 22:04:10 +000014
Martin v. Löwis64ce5052007-08-05 15:39:16 +000015 def do_open(self, *args, **kw):
Jesus Cea3a2bd7d2008-09-02 02:30:21 +000016 # This code will be vastly improved in future bsddb 4.7.4. Meanwhile,
17 # let's live with this ugliness. XXX - jcea@jcea.es - 20080902
18 class _ExposedProperties:
19 @property
20 def _cursor_refs(self):
21 return self.db._cursor_refs
22
23 import collections
24 class StringKeys(collections.MutableMapping, _ExposedProperties):
25 """Wrapper around DB object that automatically encodes
26 all keys as UTF-8; the keys must be strings."""
27
28 def __init__(self, db):
29 self.db = db
30
31 def __len__(self):
32 return len(self.db)
33
34 def __getitem__(self, key):
35 return self.db[key.encode("utf-8")]
36
37 def __setitem__(self, key, value):
38 self.db[key.encode("utf-8")] = value
39
40 def __delitem__(self, key):
41 del self.db[key.encode("utf-8")]
42
43 def __iter__(self):
44 for k in self.db:
45 yield k.decode("utf-8")
46
47 def close(self):
48 self.db.close()
49
50 def keys(self):
51 for k in self.db.keys():
52 yield k.decode("utf-8")
53
54 def has_key(self, key):
55 return self.db.has_key(key.encode("utf-8"))
56
57 __contains__ = has_key
58
59 def values(self):
60 return self.db.values()
61
62 def items(self):
63 for k,v in self.db.items():
64 yield k.decode("utf-8"), v
65
66 def set_location(self, key):
67 return self.db.set_location(key.encode("utf-8"))
68
69 def next(self):
70 key, value = self.db.next()
71 return key.decode("utf-8"), value
72
73 def previous(self):
74 key, value = self.db.previous()
75 return key.decode("utf-8"), value
76
77 def first(self):
78 key, value = self.db.first()
79 return key.decode("utf-8"), value
80
81 def last(self):
82 key, value = self.db.last()
83 return key.decode("utf-8"), value
84
85 def set_location(self, key):
86 key, value = self.db.set_location(key.encode("utf-8"))
87 return key.decode("utf-8"), value
88
89 def sync(self):
90 return self.db.sync()
91
92 class StringValues(collections.MutableMapping, _ExposedProperties):
93 """Wrapper around DB object that automatically encodes
94 and decodes all values as UTF-8; input values must be strings."""
95
96 def __init__(self, db):
97 self.db = db
98
99 def __len__(self):
100 return len(self.db)
101
102 def __getitem__(self, key):
103 return self.db[key].decode("utf-8")
104
105 def __setitem__(self, key, value):
106 self.db[key] = value.encode("utf-8")
107
108 def __delitem__(self, key):
109 del self.db[key]
110
111 def __iter__(self):
112 return iter(self.db)
113
114 def close(self):
115 self.db.close()
116
117 def keys(self):
118 return self.db.keys()
119
120 def has_key(self, key):
121 return self.db.has_key(key)
122
123 __contains__ = has_key
124
125 def values(self):
126 for v in self.db.values():
127 yield v.decode("utf-8")
128
129 def items(self):
130 for k,v in self.db.items():
131 yield k, v.decode("utf-8")
132
133 def set_location(self, key):
134 return self.db.set_location(key)
135
136 def next(self):
137 key, value = self.db.next()
138 return key, value.decode("utf-8")
139
140 def previous(self):
141 key, value = self.db.previous()
142 return key, value.decode("utf-8")
143
144 def first(self):
145 key, value = self.db.first()
146 return key, value.decode("utf-8")
147
148 def last(self):
149 key, value = self.db.last()
150 return key, value.decode("utf-8")
151
152 def set_location(self, key):
153 key, value = self.db.set_location(key)
154 return key, value.decode("utf-8")
155
156 def sync(self):
157 return self.db.sync()
158
Martin v. Löwis64ce5052007-08-05 15:39:16 +0000159 # openmethod is a list so that it's not mistaken as an instance method
Jesus Cea3a2bd7d2008-09-02 02:30:21 +0000160 return StringValues(StringKeys(self.openmethod[0](*args, **kw)))
Martin v. Löwis64ce5052007-08-05 15:39:16 +0000161
Raymond Hettingerd55111f2003-09-13 05:51:09 +0000162 def setUp(self):
Martin v. Löwis64ce5052007-08-05 15:39:16 +0000163 self.f = self.do_open(self.fname, self.openflag, cachesize=32768)
Raymond Hettingerd55111f2003-09-13 05:51:09 +0000164 self.d = dict(q='Guido', w='van', e='Rossum', r='invented', t='Python', y='')
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000165 for k, v in self.d.items():
Raymond Hettingerd55111f2003-09-13 05:51:09 +0000166 self.f[k] = v
Fred Drake004d5e62000-10-23 17:22:08 +0000167
Raymond Hettingerd55111f2003-09-13 05:51:09 +0000168 def tearDown(self):
169 self.f.sync()
170 self.f.close()
171 if self.fname is None:
172 return
Anthony Baxter83888952002-04-23 02:11:05 +0000173 try:
Raymond Hettingerd55111f2003-09-13 05:51:09 +0000174 os.remove(self.fname)
Anthony Baxter83888952002-04-23 02:11:05 +0000175 except os.error:
176 pass
Roger E. Masse352e1861997-01-16 22:04:10 +0000177
Raymond Hettingerd55111f2003-09-13 05:51:09 +0000178 def test_getitem(self):
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000179 for k, v in self.d.items():
Raymond Hettingerd55111f2003-09-13 05:51:09 +0000180 self.assertEqual(self.f[k], v)
Roger E. Masse352e1861997-01-16 22:04:10 +0000181
Raymond Hettingerd55111f2003-09-13 05:51:09 +0000182 def test_len(self):
183 self.assertEqual(len(self.f), len(self.d))
184
185 def test_change(self):
186 self.f['r'] = 'discovered'
187 self.assertEqual(self.f['r'], 'discovered')
188 self.assert_('r' in self.f.keys())
189 self.assert_('discovered' in self.f.values())
190
191 def test_close_and_reopen(self):
192 if self.fname is None:
193 # if we're using an in-memory only db, we can't reopen it
194 # so finish here.
195 return
196 self.f.close()
Martin v. Löwis64ce5052007-08-05 15:39:16 +0000197 self.f = self.do_open(self.fname, 'w')
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000198 for k, v in self.d.items():
Raymond Hettingerd55111f2003-09-13 05:51:09 +0000199 self.assertEqual(self.f[k], v)
200
201 def assertSetEquals(self, seqn1, seqn2):
Thomas Wouters0e3f5912006-08-11 14:57:12 +0000202 self.assertEqual(set(seqn1), set(seqn2))
Raymond Hettingerd55111f2003-09-13 05:51:09 +0000203
204 def test_mapping_iteration_methods(self):
205 f = self.f
206 d = self.d
207 self.assertSetEquals(d, f)
208 self.assertSetEquals(d.keys(), f.keys())
209 self.assertSetEquals(d.values(), f.values())
210 self.assertSetEquals(d.items(), f.items())
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000211 self.assertSetEquals(d.keys(), f.keys())
212 self.assertSetEquals(d.values(), f.values())
213 self.assertSetEquals(d.items(), f.items())
Raymond Hettingerd55111f2003-09-13 05:51:09 +0000214
Gregory P. Smitha703a212003-11-03 01:04:41 +0000215 def test_iter_while_modifying_values(self):
216 if not hasattr(self.f, '__iter__'):
217 return
218
219 di = iter(self.d)
220 while 1:
221 try:
Georg Brandla18af4e2007-04-21 15:47:16 +0000222 key = next(di)
Gregory P. Smitha703a212003-11-03 01:04:41 +0000223 self.d[key] = 'modified '+key
224 except StopIteration:
225 break
226
227 # it should behave the same as a dict. modifying values
228 # of existing keys should not break iteration. (adding
229 # or removing keys should)
230 fi = iter(self.f)
231 while 1:
232 try:
Georg Brandla18af4e2007-04-21 15:47:16 +0000233 key = next(fi)
Gregory P. Smitha703a212003-11-03 01:04:41 +0000234 self.f[key] = 'modified '+key
235 except StopIteration:
236 break
237
238 self.test_mapping_iteration_methods()
239
240 def test_iteritems_while_modifying_values(self):
241 if not hasattr(self.f, 'iteritems'):
242 return
243
Brett Cannon5b8d24a2007-02-22 06:12:19 +0000244 di = iter(self.d.items())
Gregory P. Smitha703a212003-11-03 01:04:41 +0000245 while 1:
246 try:
Georg Brandla18af4e2007-04-21 15:47:16 +0000247 k, v = next(di)
Gregory P. Smitha703a212003-11-03 01:04:41 +0000248 self.d[k] = 'modified '+v
249 except StopIteration:
250 break
251
252 # it should behave the same as a dict. modifying values
253 # of existing keys should not break iteration. (adding
254 # or removing keys should)
Brett Cannon5b8d24a2007-02-22 06:12:19 +0000255 fi = iter(self.f.items())
Gregory P. Smitha703a212003-11-03 01:04:41 +0000256 while 1:
257 try:
Georg Brandla18af4e2007-04-21 15:47:16 +0000258 k, v = next(fi)
Gregory P. Smitha703a212003-11-03 01:04:41 +0000259 self.f[k] = 'modified '+v
260 except StopIteration:
261 break
262
263 self.test_mapping_iteration_methods()
264
Raymond Hettingerd55111f2003-09-13 05:51:09 +0000265 def test_first_next_looping(self):
266 items = [self.f.first()]
Guido van Rossum805365e2007-05-07 22:24:25 +0000267 for i in range(1, len(self.f)):
Raymond Hettingerd55111f2003-09-13 05:51:09 +0000268 items.append(self.f.next())
269 self.assertSetEquals(items, self.d.items())
270
271 def test_previous_last_looping(self):
272 items = [self.f.last()]
Guido van Rossum805365e2007-05-07 22:24:25 +0000273 for i in range(1, len(self.f)):
Raymond Hettingerd55111f2003-09-13 05:51:09 +0000274 items.append(self.f.previous())
275 self.assertSetEquals(items, self.d.items())
276
Gregory P. Smith07aec082007-08-24 05:32:10 +0000277 def test_first_while_deleting(self):
278 # Test for bug 1725856
279 self.assert_(len(self.d) >= 2, "test requires >=2 items")
280 for _ in self.d:
281 key = self.f.first()[0]
282 del self.f[key]
283 self.assertEqual(0, len(self.f), "expected empty db after test")
284
285 def test_last_while_deleting(self):
286 # Test for bug 1725856's evil twin
287 self.assert_(len(self.d) >= 2, "test requires >=2 items")
288 for _ in self.d:
289 key = self.f.last()[0]
290 del self.f[key]
291 self.assertEqual(0, len(self.f), "expected empty db after test")
292
Raymond Hettingerd55111f2003-09-13 05:51:09 +0000293 def test_set_location(self):
294 self.assertEqual(self.f.set_location('e'), ('e', self.d['e']))
295
296 def test_contains(self):
297 for k in self.d:
298 self.assert_(k in self.f)
299 self.assert_('not here' not in self.f)
300
Raymond Hettingerd55111f2003-09-13 05:51:09 +0000301 def test_clear(self):
302 self.f.clear()
303 self.assertEqual(len(self.f), 0)
304
Gregory P. Smithdc113a82003-11-02 09:10:16 +0000305 def test__no_deadlock_first(self, debug=0):
306 # do this so that testers can see what function we're in in
307 # verbose mode when we deadlock.
308 sys.stdout.flush()
309
310 # in pybsddb's _DBWithCursor this causes an internal DBCursor
311 # object is created. Other test_ methods in this class could
312 # inadvertently cause the deadlock but an explicit test is needed.
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000313 if debug: print("A")
Gregory P. Smithdc113a82003-11-02 09:10:16 +0000314 k,v = self.f.first()
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000315 if debug: print("B", k)
Gregory P. Smithdc113a82003-11-02 09:10:16 +0000316 self.f[k] = "deadlock. do not pass go. do not collect $200."
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000317 if debug: print("C")
Gregory P. Smithdc113a82003-11-02 09:10:16 +0000318 # if the bsddb implementation leaves the DBCursor open during
319 # the database write and locking+threading support is enabled
320 # the cursor's read lock will deadlock the write lock request..
321
322 # test the iterator interface (if present)
Gregory P. Smitha703a212003-11-03 01:04:41 +0000323 if hasattr(self.f, 'iteritems'):
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000324 if debug: print("D")
Brett Cannon5b8d24a2007-02-22 06:12:19 +0000325 i = iter(self.f.items())
Georg Brandla18af4e2007-04-21 15:47:16 +0000326 k,v = next(i)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000327 if debug: print("E")
Gregory P. Smithdc113a82003-11-02 09:10:16 +0000328 self.f[k] = "please don't deadlock"
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000329 if debug: print("F")
Gregory P. Smithdc113a82003-11-02 09:10:16 +0000330 while 1:
331 try:
Georg Brandla18af4e2007-04-21 15:47:16 +0000332 k,v = next(i)
Gregory P. Smithdc113a82003-11-02 09:10:16 +0000333 except StopIteration:
334 break
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000335 if debug: print("F2")
Gregory P. Smithdc113a82003-11-02 09:10:16 +0000336
337 i = iter(self.f)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000338 if debug: print("G")
Gregory P. Smithdc113a82003-11-02 09:10:16 +0000339 while i:
340 try:
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000341 if debug: print("H")
Georg Brandla18af4e2007-04-21 15:47:16 +0000342 k = next(i)
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000343 if debug: print("I")
Gregory P. Smithdc113a82003-11-02 09:10:16 +0000344 self.f[k] = "deadlocks-r-us"
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000345 if debug: print("J")
Gregory P. Smithdc113a82003-11-02 09:10:16 +0000346 except StopIteration:
347 i = None
Guido van Rossumbe19ed72007-02-09 05:37:30 +0000348 if debug: print("K")
Gregory P. Smithdc113a82003-11-02 09:10:16 +0000349
350 # test the legacy cursor interface mixed with writes
351 self.assert_(self.f.first()[0] in self.d)
352 k = self.f.next()[0]
353 self.assert_(k in self.d)
354 self.f[k] = "be gone with ye deadlocks"
355 self.assert_(self.f[k], "be gone with ye deadlocks")
356
Gregory P. Smitha703a212003-11-03 01:04:41 +0000357 def test_for_cursor_memleak(self):
358 if not hasattr(self.f, 'iteritems'):
359 return
360
361 # do the bsddb._DBWithCursor _iter_mixin internals leak cursors?
362 nc1 = len(self.f._cursor_refs)
363 # create iterator
Guido van Rossum63eecc72007-02-22 23:55:25 +0000364 i = iter(self.f.iteritems())
Gregory P. Smitha703a212003-11-03 01:04:41 +0000365 nc2 = len(self.f._cursor_refs)
Guido van Rossum63eecc72007-02-22 23:55:25 +0000366 # use the iterator (should run to the first yield, creating the cursor)
Georg Brandla18af4e2007-04-21 15:47:16 +0000367 k, v = next(i)
Gregory P. Smitha703a212003-11-03 01:04:41 +0000368 nc3 = len(self.f._cursor_refs)
369 # destroy the iterator; this should cause the weakref callback
370 # to remove the cursor object from self.f._cursor_refs
371 del i
372 nc4 = len(self.f._cursor_refs)
373
374 self.assertEqual(nc1, nc2)
375 self.assertEqual(nc1, nc4)
Guido van Rossum63eecc72007-02-22 23:55:25 +0000376 self.assertEqual(nc3, nc1+1)
Gregory P. Smitha703a212003-11-03 01:04:41 +0000377
Raymond Hettingerd55111f2003-09-13 05:51:09 +0000378 def test_popitem(self):
379 k, v = self.f.popitem()
380 self.assert_(k in self.d)
381 self.assert_(v in self.d.values())
382 self.assert_(k not in self.f)
383 self.assertEqual(len(self.d)-1, len(self.f))
384
385 def test_pop(self):
386 k = 'w'
387 v = self.f.pop(k)
388 self.assertEqual(v, self.d[k])
389 self.assert_(k not in self.f)
390 self.assert_(v not in self.f.values())
391 self.assertEqual(len(self.d)-1, len(self.f))
392
393 def test_get(self):
394 self.assertEqual(self.f.get('NotHere'), None)
395 self.assertEqual(self.f.get('NotHere', 'Default'), 'Default')
396 self.assertEqual(self.f.get('q', 'Default'), self.d['q'])
397
398 def test_setdefault(self):
399 self.assertEqual(self.f.setdefault('new', 'dog'), 'dog')
400 self.assertEqual(self.f.setdefault('r', 'cat'), self.d['r'])
401
402 def test_update(self):
403 new = dict(y='life', u='of', i='brian')
404 self.f.update(new)
405 self.d.update(new)
Guido van Rossumcc2b0162007-02-11 06:12:03 +0000406 for k, v in self.d.items():
Raymond Hettingerd55111f2003-09-13 05:51:09 +0000407 self.assertEqual(self.f[k], v)
408
409 def test_keyordering(self):
410 if self.openmethod[0] is not bsddb.btopen:
411 return
Brett Cannon5b8d24a2007-02-22 06:12:19 +0000412 keys = sorted(self.d.keys())
Raymond Hettingerd55111f2003-09-13 05:51:09 +0000413 self.assertEqual(self.f.first()[0], keys[0])
414 self.assertEqual(self.f.next()[0], keys[1])
415 self.assertEqual(self.f.last()[0], keys[-1])
416 self.assertEqual(self.f.previous()[0], keys[-2])
417 self.assertEqual(list(self.f), keys)
418
419class TestBTree(TestBSDDB):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000420 fname = support.TESTFN
Raymond Hettingerd55111f2003-09-13 05:51:09 +0000421 openmethod = [bsddb.btopen]
422
423class TestBTree_InMemory(TestBSDDB):
424 fname = None
425 openmethod = [bsddb.btopen]
426
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000427class TestBTree_InMemory_Truncate(TestBSDDB):
428 fname = None
429 openflag = 'n'
430 openmethod = [bsddb.btopen]
431
Raymond Hettingerd55111f2003-09-13 05:51:09 +0000432class TestHashTable(TestBSDDB):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000433 fname = support.TESTFN
Raymond Hettingerd55111f2003-09-13 05:51:09 +0000434 openmethod = [bsddb.hashopen]
435
436class TestHashTable_InMemory(TestBSDDB):
437 fname = None
438 openmethod = [bsddb.hashopen]
439
440## # (bsddb.rnopen,'Record Numbers'), 'put' for RECNO for bsddb 1.85
441## # appears broken... at least on
442## # Solaris Intel - rmasse 1/97
443
444def test_main(verbose=None):
Benjamin Petersonee8712c2008-05-20 21:35:26 +0000445 support.run_unittest(
Raymond Hettingerd55111f2003-09-13 05:51:09 +0000446 TestBTree,
447 TestHashTable,
448 TestBTree_InMemory,
449 TestHashTable_InMemory,
Thomas Wouters49fd7fa2006-04-21 10:40:58 +0000450 TestBTree_InMemory_Truncate,
Raymond Hettingerd55111f2003-09-13 05:51:09 +0000451 )
452
453if __name__ == "__main__":
454 test_main(verbose=True)