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