3 from StringIO import StringIO
4 from twisted.trial import unittest
6 from allmydata.util import fileutil
7 from allmydata.util.encodingutil import listdir_unicode, get_filesystem_encoding, unicode_platform
8 from allmydata.util.assertutil import precondition
9 from allmydata.scripts import backupdb
11 class BackupDB(unittest.TestCase):
12 def create(self, dbfile):
14 bdb = backupdb.get_backupdb(dbfile, stderr=stderr)
15 self.failUnless(bdb, "unable to create backupdb from %r" % (dbfile,))
18 def skip_if_cannot_represent_filename(self, u):
19 precondition(isinstance(u, unicode))
21 enc = get_filesystem_encoding()
22 if not unicode_platform():
25 except UnicodeEncodeError:
26 raise unittest.SkipTest("A non-ASCII filename could not be encoded on this platform.")
29 self.basedir = basedir = os.path.join("backupdb", "create")
30 fileutil.make_dirs(basedir)
31 dbfile = os.path.join(basedir, "dbfile")
32 bdb = self.create(dbfile)
33 self.failUnlessEqual(bdb.VERSION, 2)
35 def test_upgrade_v1_v2(self):
36 self.basedir = basedir = os.path.join("backupdb", "upgrade_v1_v2")
37 fileutil.make_dirs(basedir)
38 dbfile = os.path.join(basedir, "dbfile")
40 created = backupdb.get_backupdb(dbfile, stderr=stderr,
41 create_version=(backupdb.SCHEMA_v1, 1),
43 self.failUnless(created, "unable to create v1 backupdb")
44 # now we should have a v1 database on disk
45 bdb = self.create(dbfile)
46 self.failUnlessEqual(bdb.VERSION, 2)
49 self.basedir = basedir = os.path.join("backupdb", "fail")
50 fileutil.make_dirs(basedir)
52 # put a non-DB file in the way
53 not_a_db = ("I do not look like a sqlite database\n" +
54 "I'M NOT" * 1000) # OS-X sqlite-2.3.2 takes some convincing
55 self.writeto("not-a-database", not_a_db)
57 bdb = backupdb.get_backupdb(os.path.join(basedir, "not-a-database"),
59 self.failUnlessEqual(bdb, None)
60 stderr = stderr_f.getvalue()
61 self.failUnlessIn("backupdb file is unusable", stderr)
62 self.failUnlessIn("file is encrypted or is not a database", stderr)
64 # put a directory in the way, to exercise a different error path
65 where = os.path.join(basedir, "roadblock-dir")
66 fileutil.make_dirs(where)
68 bdb = backupdb.get_backupdb(where, stderr_f)
69 self.failUnlessEqual(bdb, None)
70 stderr = stderr_f.getvalue()
71 self.failUnlessIn("Unable to create/open backupdb file %s" % (where,), stderr)
72 self.failUnlessIn("unable to open database file", stderr)
75 def writeto(self, filename, data):
76 fn = os.path.join(self.basedir, unicode(filename))
77 parentdir = os.path.dirname(fn)
78 fileutil.make_dirs(parentdir)
79 fileutil.write(fn, data)
83 self.basedir = basedir = os.path.join("backupdb", "check")
84 fileutil.make_dirs(basedir)
85 dbfile = os.path.join(basedir, "dbfile")
86 bdb = self.create(dbfile)
88 foo_fn = self.writeto("foo.txt", "foo.txt")
89 blah_fn = self.writeto("bar/blah.txt", "blah.txt")
91 r = bdb.check_file(foo_fn)
92 self.failUnlessEqual(r.was_uploaded(), False)
93 r.did_upload("foo-cap")
95 r = bdb.check_file(blah_fn)
96 self.failUnlessEqual(r.was_uploaded(), False)
97 r.did_upload("blah-cap")
99 r = bdb.check_file(foo_fn)
100 self.failUnlessEqual(r.was_uploaded(), "foo-cap")
101 self.failUnlessEqual(type(r.was_uploaded()), str)
102 self.failUnlessEqual(r.should_check(), False)
104 time.sleep(1.0) # make sure the timestamp changes
105 self.writeto("foo.txt", "NEW")
107 r = bdb.check_file(foo_fn)
108 self.failUnlessEqual(r.was_uploaded(), False)
109 r.did_upload("new-cap")
111 r = bdb.check_file(foo_fn)
112 self.failUnlessEqual(r.was_uploaded(), "new-cap")
113 self.failUnlessEqual(r.should_check(), False)
114 # if we spontaneously decide to upload it anyways, nothing should
116 r.did_upload("new-cap")
118 r = bdb.check_file(foo_fn, use_timestamps=False)
119 self.failUnlessEqual(r.was_uploaded(), False)
120 r.did_upload("new-cap")
122 r = bdb.check_file(foo_fn)
123 self.failUnlessEqual(r.was_uploaded(), "new-cap")
124 self.failUnlessEqual(r.should_check(), False)
126 bdb.NO_CHECK_BEFORE = 0
127 bdb.ALWAYS_CHECK_AFTER = 0.1
129 r = bdb.check_file(blah_fn)
130 self.failUnlessEqual(r.was_uploaded(), "blah-cap")
131 self.failUnlessEqual(r.should_check(), True)
132 r.did_check_healthy("results") # we know they're ignored for now
134 bdb.NO_CHECK_BEFORE = 200
135 bdb.ALWAYS_CHECK_AFTER = 400
137 r = bdb.check_file(blah_fn)
138 self.failUnlessEqual(r.was_uploaded(), "blah-cap")
139 self.failUnlessEqual(r.should_check(), False)
141 os.unlink(os.path.join(basedir, "foo.txt"))
142 fileutil.make_dirs(os.path.join(basedir, "foo.txt")) # file becomes dir
143 r = bdb.check_file(foo_fn)
144 self.failUnlessEqual(r.was_uploaded(), False)
146 def test_wrong_version(self):
147 self.basedir = basedir = os.path.join("backupdb", "wrong_version")
148 fileutil.make_dirs(basedir)
150 where = os.path.join(basedir, "tooold.db")
151 bdb = self.create(where)
152 # reach into the DB and make it old
153 bdb.cursor.execute("UPDATE version SET version=0")
154 bdb.connection.commit()
156 # now the next time we open the database, it should be an unusable
158 stderr_f = StringIO()
159 bdb = backupdb.get_backupdb(where, stderr_f)
160 self.failUnlessEqual(bdb, None)
161 stderr = stderr_f.getvalue()
162 self.failUnlessEqual(stderr.strip(),
163 "Unable to handle backupdb version 0")
165 def test_directory(self):
166 self.basedir = basedir = os.path.join("backupdb", "directory")
167 fileutil.make_dirs(basedir)
168 dbfile = os.path.join(basedir, "dbfile")
169 bdb = self.create(dbfile)
171 contents = {u"file1": "URI:CHK:blah1",
172 u"file2": "URI:CHK:blah2",
173 u"dir1": "URI:DIR2-CHK:baz2"}
174 r = bdb.check_directory(contents)
175 self.failUnless(isinstance(r, backupdb.DirectoryResult))
176 self.failIf(r.was_created())
177 dircap = "URI:DIR2-CHK:foo1"
180 r = bdb.check_directory(contents)
181 self.failUnless(r.was_created())
182 self.failUnlessEqual(r.was_created(), dircap)
183 self.failUnlessEqual(r.should_check(), False)
185 # if we spontaneously decide to upload it anyways, nothing should
188 r = bdb.check_directory(contents)
189 self.failUnless(r.was_created())
190 self.failUnlessEqual(r.was_created(), dircap)
191 self.failUnlessEqual(type(r.was_created()), str)
192 self.failUnlessEqual(r.should_check(), False)
194 bdb.NO_CHECK_BEFORE = 0
195 bdb.ALWAYS_CHECK_AFTER = 0.1
198 r = bdb.check_directory(contents)
199 self.failUnless(r.was_created())
200 self.failUnlessEqual(r.was_created(), dircap)
201 self.failUnlessEqual(r.should_check(), True)
202 r.did_check_healthy("results")
204 bdb.NO_CHECK_BEFORE = 200
205 bdb.ALWAYS_CHECK_AFTER = 400
207 r = bdb.check_directory(contents)
208 self.failUnless(r.was_created())
209 self.failUnlessEqual(r.was_created(), dircap)
210 self.failUnlessEqual(r.should_check(), False)
213 contents2 = {u"file1": "URI:CHK:blah1",
214 u"dir1": "URI:DIR2-CHK:baz2"}
215 r = bdb.check_directory(contents2)
216 self.failIf(r.was_created())
218 contents3 = {u"file1": "URI:CHK:blah1",
219 u"file2": "URI:CHK:blah3",
220 u"dir1": "URI:DIR2-CHK:baz2"}
221 r = bdb.check_directory(contents3)
222 self.failIf(r.was_created())
224 def test_unicode(self):
225 self.skip_if_cannot_represent_filename(u"f\u00f6\u00f6.txt")
226 self.skip_if_cannot_represent_filename(u"b\u00e5r.txt")
228 self.basedir = basedir = os.path.join("backupdb", "unicode")
229 fileutil.make_dirs(basedir)
230 dbfile = os.path.join(basedir, "dbfile")
231 bdb = self.create(dbfile)
233 self.writeto(u"f\u00f6\u00f6.txt", "foo.txt")
234 files = [fn for fn in listdir_unicode(unicode(basedir)) if fn.endswith(".txt")]
235 self.failUnlessEqual(len(files), 1)
236 foo_fn = os.path.join(basedir, files[0])
237 #print foo_fn, type(foo_fn)
239 r = bdb.check_file(foo_fn)
240 self.failUnlessEqual(r.was_uploaded(), False)
241 r.did_upload("foo-cap")
243 r = bdb.check_file(foo_fn)
244 self.failUnlessEqual(r.was_uploaded(), "foo-cap")
245 self.failUnlessEqual(r.should_check(), False)
247 bar_fn = self.writeto(u"b\u00e5r.txt", "bar.txt")
248 #print bar_fn, type(bar_fn)
250 r = bdb.check_file(bar_fn)
251 self.failUnlessEqual(r.was_uploaded(), False)
252 r.did_upload("bar-cap")
254 r = bdb.check_file(bar_fn)
255 self.failUnlessEqual(r.was_uploaded(), "bar-cap")
256 self.failUnlessEqual(r.should_check(), False)