From 1e739fd52dbe417db225027baa41c8d60679d8d8 Mon Sep 17 00:00:00 2001 From: Thomas Waldmann Date: Sat, 23 Jul 2016 16:16:56 +0200 Subject: [PATCH] fix local repo / upgrader tests --- borg/archiver.py | 10 +++---- borg/testsuite/archiver.py | 4 +-- borg/testsuite/repository.py | 53 +++++++++++++++++++++++++----------- borg/testsuite/upgrader.py | 2 +- 4 files changed, 45 insertions(+), 24 deletions(-) diff --git a/borg/archiver.py b/borg/archiver.py index 4c3bfca0a..3c921e962 100644 --- a/borg/archiver.py +++ b/borg/archiver.py @@ -134,7 +134,7 @@ class Archiver: pass return self.exit_code - @with_repository(exclusive='repair', manifest=False) + @with_repository(exclusive=True, manifest=False) def do_check(self, args, repository): """Check repository consistency""" if args.repair: @@ -174,7 +174,7 @@ class Archiver: key_new.change_passphrase() # option to change key protection passphrase, save return EXIT_SUCCESS - @with_repository(fake='dry_run') + @with_repository(fake='dry_run', exclusive=True) def do_create(self, args, repository, manifest=None, key=None): """Create new archive""" matcher = PatternMatcher(fallback=True) @@ -595,7 +595,7 @@ class Archiver: print(str(cache)) return self.exit_code - @with_repository() + @with_repository(exclusive=True) def do_prune(self, args, repository, manifest, key): """Prune repository archives according to specified rules""" if not any((args.hourly, args.daily, @@ -722,7 +722,7 @@ class Archiver: print("object %s fetched." % hex_id) return EXIT_SUCCESS - @with_repository(manifest=False) + @with_repository(manifest=False, exclusive=True) def do_debug_put_obj(self, args, repository): """put file(s) contents into the repository""" for path in args.paths: @@ -734,7 +734,7 @@ class Archiver: repository.commit() return EXIT_SUCCESS - @with_repository(manifest=False) + @with_repository(manifest=False, exclusive=True) def do_debug_delete_obj(self, args, repository): """delete the objects with the given IDs from the repo""" modified = False diff --git a/borg/testsuite/archiver.py b/borg/testsuite/archiver.py index ff83a225a..f1f70fcdc 100644 --- a/borg/testsuite/archiver.py +++ b/borg/testsuite/archiver.py @@ -236,7 +236,7 @@ class ArchiverTestCaseBase(BaseTestCase): self.cmd('create', self.repository_location + '::' + name, src_dir) def open_archive(self, name): - repository = Repository(self.repository_path) + repository = Repository(self.repository_path, exclusive=True) with repository: manifest, key = Manifest.load(repository) archive = Archive(repository, key, manifest, name) @@ -1288,7 +1288,7 @@ class ArchiverCheckTestCase(ArchiverTestCaseBase): def test_extra_chunks(self): self.cmd('check', self.repository_location, exit_code=0) - with Repository(self.repository_location) as repository: + with Repository(self.repository_location, exclusive=True) as repository: repository.put(b'01234567890123456789012345678901', b'xxxx') repository.commit() self.cmd('check', self.repository_location, exit_code=1) diff --git a/borg/testsuite/repository.py b/borg/testsuite/repository.py index e034cf9e1..a012f514a 100644 --- a/borg/testsuite/repository.py +++ b/borg/testsuite/repository.py @@ -12,11 +12,17 @@ from ..repository import Repository, LoggedIO, TAG_COMMIT from . import BaseTestCase +UNSPECIFIED = object() # for default values where we can't use None + + class RepositoryTestCaseBase(BaseTestCase): key_size = 32 + exclusive = True - def open(self, create=False): - return Repository(os.path.join(self.tmppath, 'repository'), create=create) + def open(self, create=False, exclusive=UNSPECIFIED): + if exclusive is UNSPECIFIED: + exclusive = self.exclusive + return Repository(os.path.join(self.tmppath, 'repository'), exclusive=exclusive, create=create) def setUp(self): self.tmppath = tempfile.mkdtemp() @@ -27,10 +33,10 @@ class RepositoryTestCaseBase(BaseTestCase): self.repository.close() shutil.rmtree(self.tmppath) - def reopen(self): + def reopen(self, exclusive=UNSPECIFIED): if self.repository: self.repository.close() - self.repository = self.open() + self.repository = self.open(exclusive=exclusive) class RepositoryTestCase(RepositoryTestCaseBase): @@ -156,17 +162,6 @@ class RepositoryCommitTestCase(RepositoryTestCaseBase): self.assert_equal(len(self.repository), 3) self.assert_equal(self.repository.check(), True) - def test_replay_of_readonly_repository(self): - self.add_keys() - for name in os.listdir(self.repository.path): - if name.startswith('index.'): - os.unlink(os.path.join(self.repository.path, name)) - with patch.object(UpgradableLock, 'upgrade', side_effect=LockFailed) as upgrade: - self.reopen() - with self.repository: - self.assert_raises(LockFailed, lambda: len(self.repository)) - upgrade.assert_called_once_with() - def test_crash_before_write_index(self): self.add_keys() self.repository.write_index = None @@ -179,6 +174,32 @@ class RepositoryCommitTestCase(RepositoryTestCaseBase): self.assert_equal(len(self.repository), 3) self.assert_equal(self.repository.check(), True) + def test_replay_lock_upgrade_old(self): + self.add_keys() + for name in os.listdir(self.repository.path): + if name.startswith('index.'): + os.unlink(os.path.join(self.repository.path, name)) + with patch.object(Lock, 'upgrade', side_effect=LockFailed) as upgrade: + self.reopen(exclusive=None) # simulate old client that always does lock upgrades + with self.repository: + # the repo is only locked by a shared read lock, but to replay segments, + # we need an exclusive write lock - check if the lock gets upgraded. + self.assert_raises(LockFailed, lambda: len(self.repository)) + upgrade.assert_called_once_with() + + def test_replay_lock_upgrade(self): + self.add_keys() + for name in os.listdir(self.repository.path): + if name.startswith('index.'): + os.unlink(os.path.join(self.repository.path, name)) + with patch.object(Lock, 'upgrade', side_effect=LockFailed) as upgrade: + self.reopen(exclusive=False) # current client usually does not do lock upgrade, except for replay + with self.repository: + # the repo is only locked by a shared read lock, but to replay segments, + # we need an exclusive write lock - check if the lock gets upgraded. + self.assert_raises(LockFailed, lambda: len(self.repository)) + upgrade.assert_called_once_with() + def test_crash_before_deleting_compacted_segments(self): self.add_keys() self.repository.io.delete_segment = None @@ -202,7 +223,7 @@ class RepositoryCommitTestCase(RepositoryTestCaseBase): class RepositoryAppendOnlyTestCase(RepositoryTestCaseBase): def open(self, create=False): - return Repository(os.path.join(self.tmppath, 'repository'), create=create, append_only=True) + return Repository(os.path.join(self.tmppath, 'repository'), exclusive=True, create=create, append_only=True) def test_destroy_append_only(self): # Can't destroy append only repo (via the API) diff --git a/borg/testsuite/upgrader.py b/borg/testsuite/upgrader.py index 26c34a3c6..013a8d002 100644 --- a/borg/testsuite/upgrader.py +++ b/borg/testsuite/upgrader.py @@ -23,7 +23,7 @@ def repo_valid(path): :param path: the path to the repository :returns: if borg can check the repository """ - with Repository(str(path), create=False) as repository: + with Repository(str(path), exclusive=True, create=False) as repository: # can't check raises() because check() handles the error return repository.check()