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