blob: 3637c4bb21b6db61982fb770a0667ab7156daab6 [file] [log] [blame]
Emanuele Gaifasd7aed412018-03-10 23:08:31 +01001import sqlite3 as sqlite
2import unittest
3
4
Emanuele Gaifasd7aed412018-03-10 23:08:31 +01005class BackupTests(unittest.TestCase):
6 def setUp(self):
7 cx = self.cx = sqlite.connect(":memory:")
8 cx.execute('CREATE TABLE foo (key INTEGER)')
9 cx.executemany('INSERT INTO foo (key) VALUES (?)', [(3,), (4,)])
10 cx.commit()
11
12 def tearDown(self):
13 self.cx.close()
14
15 def verify_backup(self, bckcx):
16 result = bckcx.execute("SELECT key FROM foo ORDER BY key").fetchall()
17 self.assertEqual(result[0][0], 3)
18 self.assertEqual(result[1][0], 4)
19
20 def test_bad_target_none(self):
21 with self.assertRaises(TypeError):
22 self.cx.backup(None)
23
24 def test_bad_target_filename(self):
25 with self.assertRaises(TypeError):
26 self.cx.backup('some_file_name.db')
27
28 def test_bad_target_same_connection(self):
29 with self.assertRaises(ValueError):
30 self.cx.backup(self.cx)
31
32 def test_bad_target_closed_connection(self):
33 bck = sqlite.connect(':memory:')
34 bck.close()
35 with self.assertRaises(sqlite.ProgrammingError):
36 self.cx.backup(bck)
37
Peter McCormickbfee9fa2020-09-19 23:40:46 -040038 def test_bad_source_closed_connection(self):
39 bck = sqlite.connect(':memory:')
40 source = sqlite.connect(":memory:")
41 source.close()
42 with self.assertRaises(sqlite.ProgrammingError):
43 source.backup(bck)
44
Emanuele Gaifasd7aed412018-03-10 23:08:31 +010045 def test_bad_target_in_transaction(self):
Emanuele Gaifasd7aed412018-03-10 23:08:31 +010046 bck = sqlite.connect(':memory:')
47 bck.execute('CREATE TABLE bar (key INTEGER)')
48 bck.executemany('INSERT INTO bar (key) VALUES (?)', [(3,), (4,)])
49 with self.assertRaises(sqlite.OperationalError) as cm:
50 self.cx.backup(bck)
Aviv Palivodabbf7bb72018-03-18 02:48:55 +020051 if sqlite.sqlite_version_info < (3, 8, 8):
Emanuele Gaifasd7aed412018-03-10 23:08:31 +010052 self.assertEqual(str(cm.exception), 'target is in transaction')
53
54 def test_keyword_only_args(self):
55 with self.assertRaises(TypeError):
56 with sqlite.connect(':memory:') as bck:
57 self.cx.backup(bck, 1)
58
59 def test_simple(self):
60 with sqlite.connect(':memory:') as bck:
61 self.cx.backup(bck)
62 self.verify_backup(bck)
63
64 def test_progress(self):
65 journal = []
66
67 def progress(status, remaining, total):
68 journal.append(status)
69
70 with sqlite.connect(':memory:') as bck:
71 self.cx.backup(bck, pages=1, progress=progress)
72 self.verify_backup(bck)
73
74 self.assertEqual(len(journal), 2)
75 self.assertEqual(journal[0], sqlite.SQLITE_OK)
76 self.assertEqual(journal[1], sqlite.SQLITE_DONE)
77
78 def test_progress_all_pages_at_once_1(self):
79 journal = []
80
81 def progress(status, remaining, total):
82 journal.append(remaining)
83
84 with sqlite.connect(':memory:') as bck:
85 self.cx.backup(bck, progress=progress)
86 self.verify_backup(bck)
87
88 self.assertEqual(len(journal), 1)
89 self.assertEqual(journal[0], 0)
90
91 def test_progress_all_pages_at_once_2(self):
92 journal = []
93
94 def progress(status, remaining, total):
95 journal.append(remaining)
96
97 with sqlite.connect(':memory:') as bck:
98 self.cx.backup(bck, pages=-1, progress=progress)
99 self.verify_backup(bck)
100
101 self.assertEqual(len(journal), 1)
102 self.assertEqual(journal[0], 0)
103
104 def test_non_callable_progress(self):
105 with self.assertRaises(TypeError) as cm:
106 with sqlite.connect(':memory:') as bck:
107 self.cx.backup(bck, pages=1, progress='bar')
108 self.assertEqual(str(cm.exception), 'progress argument must be a callable')
109
110 def test_modifying_progress(self):
111 journal = []
112
113 def progress(status, remaining, total):
114 if not journal:
115 self.cx.execute('INSERT INTO foo (key) VALUES (?)', (remaining+1000,))
116 self.cx.commit()
117 journal.append(remaining)
118
119 with sqlite.connect(':memory:') as bck:
120 self.cx.backup(bck, pages=1, progress=progress)
121 self.verify_backup(bck)
122
123 result = bck.execute("SELECT key FROM foo"
124 " WHERE key >= 1000"
125 " ORDER BY key").fetchall()
126 self.assertEqual(result[0][0], 1001)
127
128 self.assertEqual(len(journal), 3)
129 self.assertEqual(journal[0], 1)
130 self.assertEqual(journal[1], 1)
131 self.assertEqual(journal[2], 0)
132
133 def test_failing_progress(self):
134 def progress(status, remaining, total):
135 raise SystemError('nearly out of space')
136
137 with self.assertRaises(SystemError) as err:
138 with sqlite.connect(':memory:') as bck:
139 self.cx.backup(bck, progress=progress)
140 self.assertEqual(str(err.exception), 'nearly out of space')
141
142 def test_database_source_name(self):
143 with sqlite.connect(':memory:') as bck:
144 self.cx.backup(bck, name='main')
145 with sqlite.connect(':memory:') as bck:
146 self.cx.backup(bck, name='temp')
147 with self.assertRaises(sqlite.OperationalError) as cm:
148 with sqlite.connect(':memory:') as bck:
149 self.cx.backup(bck, name='non-existing')
150 self.assertIn(
151 str(cm.exception),
152 ['SQL logic error', 'SQL logic error or missing database']
153 )
154
155 self.cx.execute("ATTACH DATABASE ':memory:' AS attached_db")
156 self.cx.execute('CREATE TABLE attached_db.foo (key INTEGER)')
157 self.cx.executemany('INSERT INTO attached_db.foo (key) VALUES (?)', [(3,), (4,)])
158 self.cx.commit()
159 with sqlite.connect(':memory:') as bck:
160 self.cx.backup(bck, name='attached_db')
161 self.verify_backup(bck)
162
163
164def suite():
165 return unittest.makeSuite(BackupTests)
166
167if __name__ == "__main__":
168 unittest.main()