blob: 2a21489374130f60f03799f31a2361a23911f37d [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):
Berker Peksag7280a4e2018-03-11 03:38:50 +030040 if sqlite.sqlite_version_info == (3, 8, 7, 1):
41 self.skipTest('skip until we debug https://bugs.python.org/issue27645#msg313562')
Emanuele Gaifasd7aed412018-03-10 23:08:31 +010042 bck = sqlite.connect(':memory:')
43 bck.execute('CREATE TABLE bar (key INTEGER)')
44 bck.executemany('INSERT INTO bar (key) VALUES (?)', [(3,), (4,)])
45 with self.assertRaises(sqlite.OperationalError) as cm:
46 self.cx.backup(bck)
47 if sqlite.sqlite_version_info < (3, 8, 7):
48 self.assertEqual(str(cm.exception), 'target is in transaction')
49
50 def test_keyword_only_args(self):
51 with self.assertRaises(TypeError):
52 with sqlite.connect(':memory:') as bck:
53 self.cx.backup(bck, 1)
54
55 def test_simple(self):
56 with sqlite.connect(':memory:') as bck:
57 self.cx.backup(bck)
58 self.verify_backup(bck)
59
60 def test_progress(self):
61 journal = []
62
63 def progress(status, remaining, total):
64 journal.append(status)
65
66 with sqlite.connect(':memory:') as bck:
67 self.cx.backup(bck, pages=1, progress=progress)
68 self.verify_backup(bck)
69
70 self.assertEqual(len(journal), 2)
71 self.assertEqual(journal[0], sqlite.SQLITE_OK)
72 self.assertEqual(journal[1], sqlite.SQLITE_DONE)
73
74 def test_progress_all_pages_at_once_1(self):
75 journal = []
76
77 def progress(status, remaining, total):
78 journal.append(remaining)
79
80 with sqlite.connect(':memory:') as bck:
81 self.cx.backup(bck, progress=progress)
82 self.verify_backup(bck)
83
84 self.assertEqual(len(journal), 1)
85 self.assertEqual(journal[0], 0)
86
87 def test_progress_all_pages_at_once_2(self):
88 journal = []
89
90 def progress(status, remaining, total):
91 journal.append(remaining)
92
93 with sqlite.connect(':memory:') as bck:
94 self.cx.backup(bck, pages=-1, progress=progress)
95 self.verify_backup(bck)
96
97 self.assertEqual(len(journal), 1)
98 self.assertEqual(journal[0], 0)
99
100 def test_non_callable_progress(self):
101 with self.assertRaises(TypeError) as cm:
102 with sqlite.connect(':memory:') as bck:
103 self.cx.backup(bck, pages=1, progress='bar')
104 self.assertEqual(str(cm.exception), 'progress argument must be a callable')
105
106 def test_modifying_progress(self):
107 journal = []
108
109 def progress(status, remaining, total):
110 if not journal:
111 self.cx.execute('INSERT INTO foo (key) VALUES (?)', (remaining+1000,))
112 self.cx.commit()
113 journal.append(remaining)
114
115 with sqlite.connect(':memory:') as bck:
116 self.cx.backup(bck, pages=1, progress=progress)
117 self.verify_backup(bck)
118
119 result = bck.execute("SELECT key FROM foo"
120 " WHERE key >= 1000"
121 " ORDER BY key").fetchall()
122 self.assertEqual(result[0][0], 1001)
123
124 self.assertEqual(len(journal), 3)
125 self.assertEqual(journal[0], 1)
126 self.assertEqual(journal[1], 1)
127 self.assertEqual(journal[2], 0)
128
129 def test_failing_progress(self):
130 def progress(status, remaining, total):
131 raise SystemError('nearly out of space')
132
133 with self.assertRaises(SystemError) as err:
134 with sqlite.connect(':memory:') as bck:
135 self.cx.backup(bck, progress=progress)
136 self.assertEqual(str(err.exception), 'nearly out of space')
137
138 def test_database_source_name(self):
139 with sqlite.connect(':memory:') as bck:
140 self.cx.backup(bck, name='main')
141 with sqlite.connect(':memory:') as bck:
142 self.cx.backup(bck, name='temp')
143 with self.assertRaises(sqlite.OperationalError) as cm:
144 with sqlite.connect(':memory:') as bck:
145 self.cx.backup(bck, name='non-existing')
146 self.assertIn(
147 str(cm.exception),
148 ['SQL logic error', 'SQL logic error or missing database']
149 )
150
151 self.cx.execute("ATTACH DATABASE ':memory:' AS attached_db")
152 self.cx.execute('CREATE TABLE attached_db.foo (key INTEGER)')
153 self.cx.executemany('INSERT INTO attached_db.foo (key) VALUES (?)', [(3,), (4,)])
154 self.cx.commit()
155 with sqlite.connect(':memory:') as bck:
156 self.cx.backup(bck, name='attached_db')
157 self.verify_backup(bck)
158
159
160def suite():
161 return unittest.makeSuite(BackupTests)
162
163if __name__ == "__main__":
164 unittest.main()