1from test import support
2support.import_module("dbm.ndbm") #skip if not supported
3import os
4import unittest
5import dbm.ndbm
6from dbm.ndbm import error
7
8class DbmTestCase(unittest.TestCase):
9
10    def setUp(self):
11        self.filename = support.TESTFN
12        self.d = dbm.ndbm.open(self.filename, 'c')
13        self.d.close()
14
15    def tearDown(self):
16        for suffix in ['', '.pag', '.dir', '.db']:
17            support.unlink(self.filename + suffix)
18
19    def test_keys(self):
20        self.d = dbm.ndbm.open(self.filename, 'c')
21        self.assertEqual(self.d.keys(), [])
22        self.d['a'] = 'b'
23        self.d[b'bytes'] = b'data'
24        self.d['12345678910'] = '019237410982340912840198242'
25        self.d.keys()
26        self.assertIn('a', self.d)
27        self.assertIn(b'a', self.d)
28        self.assertEqual(self.d[b'bytes'], b'data')
29        # get() and setdefault() work as in the dict interface
30        self.assertEqual(self.d.get(b'a'), b'b')
31        self.assertIsNone(self.d.get(b'xxx'))
32        self.assertEqual(self.d.get(b'xxx', b'foo'), b'foo')
33        with self.assertRaises(KeyError):
34            self.d['xxx']
35        self.assertEqual(self.d.setdefault(b'xxx', b'foo'), b'foo')
36        self.assertEqual(self.d[b'xxx'], b'foo')
37        self.d.close()
38
39    def test_empty_value(self):
40        if dbm.ndbm.library == 'Berkeley DB':
41            self.skipTest("Berkeley DB doesn't distinguish the empty value "
42                          "from the absent one")
43        self.d = dbm.ndbm.open(self.filename, 'c')
44        self.assertEqual(self.d.keys(), [])
45        self.d['empty'] = ''
46        self.assertEqual(self.d.keys(), [b'empty'])
47        self.assertIn(b'empty', self.d)
48        self.assertEqual(self.d[b'empty'], b'')
49        self.assertEqual(self.d.get(b'empty'), b'')
50        self.assertEqual(self.d.setdefault(b'empty'), b'')
51        self.d.close()
52
53    def test_modes(self):
54        for mode in ['r', 'rw', 'w', 'n']:
55            try:
56                self.d = dbm.ndbm.open(self.filename, mode)
57                self.d.close()
58            except error:
59                self.fail()
60
61    def test_context_manager(self):
62        with dbm.ndbm.open(self.filename, 'c') as db:
63            db["ndbm context manager"] = "context manager"
64
65        with dbm.ndbm.open(self.filename, 'r') as db:
66            self.assertEqual(list(db.keys()), [b"ndbm context manager"])
67
68        with self.assertRaises(dbm.ndbm.error) as cm:
69            db.keys()
70        self.assertEqual(str(cm.exception),
71                         "DBM object has already been closed")
72
73    def test_bytes(self):
74        with dbm.ndbm.open(self.filename, 'c') as db:
75            db[b'bytes key \xbd'] = b'bytes value \xbd'
76        with dbm.ndbm.open(self.filename, 'r') as db:
77            self.assertEqual(list(db.keys()), [b'bytes key \xbd'])
78            self.assertTrue(b'bytes key \xbd' in db)
79            self.assertEqual(db[b'bytes key \xbd'], b'bytes value \xbd')
80
81    def test_unicode(self):
82        with dbm.ndbm.open(self.filename, 'c') as db:
83            db['Unicode key \U0001f40d'] = 'Unicode value \U0001f40d'
84        with dbm.ndbm.open(self.filename, 'r') as db:
85            self.assertEqual(list(db.keys()), ['Unicode key \U0001f40d'.encode()])
86            self.assertTrue('Unicode key \U0001f40d'.encode() in db)
87            self.assertTrue('Unicode key \U0001f40d' in db)
88            self.assertEqual(db['Unicode key \U0001f40d'.encode()],
89                             'Unicode value \U0001f40d'.encode())
90            self.assertEqual(db['Unicode key \U0001f40d'],
91                             'Unicode value \U0001f40d'.encode())
92
93    def test_write_readonly_file(self):
94        with dbm.ndbm.open(self.filename, 'c') as db:
95            db[b'bytes key'] = b'bytes value'
96        with dbm.ndbm.open(self.filename, 'r') as db:
97            with self.assertRaises(error):
98                del db[b'not exist key']
99            with self.assertRaises(error):
100                del db[b'bytes key']
101            with self.assertRaises(error):
102                db[b'not exist key'] = b'not exist value'
103
104    @unittest.skipUnless(support.TESTFN_NONASCII,
105                         'requires OS support of non-ASCII encodings')
106    def test_nonascii_filename(self):
107        filename = support.TESTFN_NONASCII
108        for suffix in ['', '.pag', '.dir', '.db']:
109            self.addCleanup(support.unlink, filename + suffix)
110        with dbm.ndbm.open(filename, 'c') as db:
111            db[b'key'] = b'value'
112        self.assertTrue(any(os.path.exists(filename + suffix)
113                            for suffix in ['', '.pag', '.dir', '.db']))
114        with dbm.ndbm.open(filename, 'r') as db:
115            self.assertEqual(list(db.keys()), [b'key'])
116            self.assertTrue(b'key' in db)
117            self.assertEqual(db[b'key'], b'value')
118
119    def test_nonexisting_file(self):
120        nonexisting_file = 'nonexisting-file'
121        with self.assertRaises(dbm.ndbm.error) as cm:
122            dbm.ndbm.open(nonexisting_file)
123        self.assertIn(nonexisting_file, str(cm.exception))
124        self.assertEqual(cm.exception.filename, nonexisting_file)
125
126
127if __name__ == '__main__':
128    unittest.main()
129