1from test import support 2gdbm = support.import_module("dbm.gnu") #skip if not supported 3import unittest 4import os 5from test.support import TESTFN, TESTFN_NONASCII, unlink 6 7 8filename = TESTFN 9 10class TestGdbm(unittest.TestCase): 11 @staticmethod 12 def setUpClass(): 13 if support.verbose: 14 try: 15 from _gdbm import _GDBM_VERSION as version 16 except ImportError: 17 pass 18 else: 19 print(f"gdbm version: {version}") 20 21 def setUp(self): 22 self.g = None 23 24 def tearDown(self): 25 if self.g is not None: 26 self.g.close() 27 unlink(filename) 28 29 def test_key_methods(self): 30 self.g = gdbm.open(filename, 'c') 31 self.assertEqual(self.g.keys(), []) 32 self.g['a'] = 'b' 33 self.g['12345678910'] = '019237410982340912840198242' 34 self.g[b'bytes'] = b'data' 35 key_set = set(self.g.keys()) 36 self.assertEqual(key_set, set([b'a', b'bytes', b'12345678910'])) 37 self.assertIn('a', self.g) 38 self.assertIn(b'a', self.g) 39 self.assertEqual(self.g[b'bytes'], b'data') 40 key = self.g.firstkey() 41 while key: 42 self.assertIn(key, key_set) 43 key_set.remove(key) 44 key = self.g.nextkey(key) 45 # get() and setdefault() work as in the dict interface 46 self.assertEqual(self.g.get(b'a'), b'b') 47 self.assertIsNone(self.g.get(b'xxx')) 48 self.assertEqual(self.g.get(b'xxx', b'foo'), b'foo') 49 with self.assertRaises(KeyError): 50 self.g['xxx'] 51 self.assertEqual(self.g.setdefault(b'xxx', b'foo'), b'foo') 52 self.assertEqual(self.g[b'xxx'], b'foo') 53 54 def test_error_conditions(self): 55 # Try to open a non-existent database. 56 unlink(filename) 57 self.assertRaises(gdbm.error, gdbm.open, filename, 'r') 58 # Try to access a closed database. 59 self.g = gdbm.open(filename, 'c') 60 self.g.close() 61 self.assertRaises(gdbm.error, lambda: self.g['a']) 62 # try pass an invalid open flag 63 self.assertRaises(gdbm.error, lambda: gdbm.open(filename, 'rx').close()) 64 65 def test_flags(self): 66 # Test the flag parameter open() by trying all supported flag modes. 67 all = set(gdbm.open_flags) 68 # Test standard flags (presumably "crwn"). 69 modes = all - set('fsu') 70 for mode in sorted(modes): # put "c" mode first 71 self.g = gdbm.open(filename, mode) 72 self.g.close() 73 74 # Test additional flags (presumably "fsu"). 75 flags = all - set('crwn') 76 for mode in modes: 77 for flag in flags: 78 self.g = gdbm.open(filename, mode + flag) 79 self.g.close() 80 81 def test_reorganize(self): 82 self.g = gdbm.open(filename, 'c') 83 size0 = os.path.getsize(filename) 84 85 # bpo-33901: on macOS with gdbm 1.15, an empty database uses 16 MiB 86 # and adding an entry of 10,000 B has no effect on the file size. 87 # Add size0 bytes to make sure that the file size changes. 88 value_size = max(size0, 10000) 89 self.g['x'] = 'x' * value_size 90 size1 = os.path.getsize(filename) 91 self.assertGreater(size1, size0) 92 93 del self.g['x'] 94 # 'size' is supposed to be the same even after deleting an entry. 95 self.assertEqual(os.path.getsize(filename), size1) 96 97 self.g.reorganize() 98 size2 = os.path.getsize(filename) 99 self.assertLess(size2, size1) 100 self.assertGreaterEqual(size2, size0) 101 102 def test_context_manager(self): 103 with gdbm.open(filename, 'c') as db: 104 db["gdbm context manager"] = "context manager" 105 106 with gdbm.open(filename, 'r') as db: 107 self.assertEqual(list(db.keys()), [b"gdbm context manager"]) 108 109 with self.assertRaises(gdbm.error) as cm: 110 db.keys() 111 self.assertEqual(str(cm.exception), 112 "GDBM object has already been closed") 113 114 def test_bytes(self): 115 with gdbm.open(filename, 'c') as db: 116 db[b'bytes key \xbd'] = b'bytes value \xbd' 117 with gdbm.open(filename, 'r') as db: 118 self.assertEqual(list(db.keys()), [b'bytes key \xbd']) 119 self.assertTrue(b'bytes key \xbd' in db) 120 self.assertEqual(db[b'bytes key \xbd'], b'bytes value \xbd') 121 122 def test_unicode(self): 123 with gdbm.open(filename, 'c') as db: 124 db['Unicode key \U0001f40d'] = 'Unicode value \U0001f40d' 125 with gdbm.open(filename, 'r') as db: 126 self.assertEqual(list(db.keys()), ['Unicode key \U0001f40d'.encode()]) 127 self.assertTrue('Unicode key \U0001f40d'.encode() in db) 128 self.assertTrue('Unicode key \U0001f40d' in db) 129 self.assertEqual(db['Unicode key \U0001f40d'.encode()], 130 'Unicode value \U0001f40d'.encode()) 131 self.assertEqual(db['Unicode key \U0001f40d'], 132 'Unicode value \U0001f40d'.encode()) 133 134 def test_write_readonly_file(self): 135 with gdbm.open(filename, 'c') as db: 136 db[b'bytes key'] = b'bytes value' 137 with gdbm.open(filename, 'r') as db: 138 with self.assertRaises(gdbm.error): 139 del db[b'not exist key'] 140 with self.assertRaises(gdbm.error): 141 del db[b'bytes key'] 142 with self.assertRaises(gdbm.error): 143 db[b'not exist key'] = b'not exist value' 144 145 @unittest.skipUnless(TESTFN_NONASCII, 146 'requires OS support of non-ASCII encodings') 147 def test_nonascii_filename(self): 148 filename = TESTFN_NONASCII 149 self.addCleanup(unlink, filename) 150 with gdbm.open(filename, 'c') as db: 151 db[b'key'] = b'value' 152 self.assertTrue(os.path.exists(filename)) 153 with gdbm.open(filename, 'r') as db: 154 self.assertEqual(list(db.keys()), [b'key']) 155 self.assertTrue(b'key' in db) 156 self.assertEqual(db[b'key'], b'value') 157 158 def test_nonexisting_file(self): 159 nonexisting_file = 'nonexisting-file' 160 with self.assertRaises(gdbm.error) as cm: 161 gdbm.open(nonexisting_file) 162 self.assertIn(nonexisting_file, str(cm.exception)) 163 self.assertEqual(cm.exception.filename, nonexisting_file) 164 165 166if __name__ == '__main__': 167 unittest.main() 168