| 
					
				 | 
			
			
				@@ -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) 
			 |