def test_full(self): # Test resource recovery after a premature termination lock_dir = get_lock_dir('full') resource = Resource('full', ['foo'], 1) os.makedirs(lock_dir, exist_ok=True) with open(os.path.join(lock_dir, '0.0.lock'), 'w') as fid: fid.write(str(os.getpid())) with self.assertRaises(ResourceError): resource.acquire() process = Process(target=lambda: 0) process.start() dead_pid = process.pid process.join() with open(os.path.join(lock_dir, '0.0.lock'), 'w') as fid: fid.write(str(dead_pid)) lock = resource.acquire() self.assertEqual(lock, 'foo') # Test that additional calls to acquire after recovering resource, work self.assertEqual(lock, resource.acquire()) # Clean up warnings resource.release(force=True)
def test_dir_cleanup(self): # Test that empty lock dir is auto deleted resource = Resource('test', 1, 1) if filelock.FileLock == filelock.SoftFileLock: self.assertNotExist(resource.lock_dir) else: self.assertExist(resource.lock_dir, is_dir=True) resource.acquire() self.assertExist(resource.lock_dir, is_dir=True) lock_file = resource._local.lock.lock_file self.assertExist(lock_file) resource.release() self.assertNotExist(lock_file) self.assertNotExist(resource.lock_dir)
def test_release(self): # test releasing a lock in a single thread test = Resource('test', 1, 1) test.acquire() lock = test._local.lock self.assertIsNotNone(test._local.lock) self.assertIsNotNone(test._local.resource_id) self.assertIsNotNone(test._local.instance_id) test.release() # make sure file and cache is cleaned up self.assertNotExist(lock.lock_file) self.assertIsNone(test._local.lock) self.assertIsNone(test._local.resource_id) self.assertIsNone(test._local.instance_id)
def test_items(self): # Test list of objects as resources resource1 = Resource('items', ['foo', 'bar']) resource2 = Resource('items', ['foo', 'bar']) resource3 = Resource('items', ['foo', 'bar']) foo = resource1.acquire() self.assertEqual(foo, 'foo') self.assertEqual(foo, resource1.acquire()) bar = resource2.acquire() self.assertEqual(bar, 'bar') self.assertEqual(bar, resource2.acquire()) with self.assertRaises(ResourceError): resource3.acquire() # Clean up warnings resource1.release(force=True) resource2.release(force=True)
def test_repeats(self): # test repeated resources repeat1 = Resource('repeat', 2, 2) repeat2 = Resource('repeat', 2, 2) repeat3 = Resource('repeat', 2, 2) repeat4 = Resource('repeat', 2, 2) repeat5 = Resource('repeat', 2, 2) lock1 = repeat1.acquire() lock2 = repeat2.acquire() lock3 = repeat3.acquire() lock4 = repeat4.acquire() # Four unique names self.assertEqual( len({ repeat1._local.lock.lock_file, repeat2._local.lock.lock_file, repeat3._local.lock.lock_file, repeat4._local.lock.lock_file }), 4) # reacquire, cache lock1b = repeat1.acquire() lock2b = repeat2.acquire() lock3b = repeat3.acquire() lock4b = repeat4.acquire() self.assertEqual(lock1, lock1b) self.assertEqual(lock2, lock2b) self.assertEqual(lock3, lock3b) self.assertEqual(lock4, lock4b) with self.assertRaises(ResourceError): repeat5.acquire() self.assertEqual(lock1, 0) self.assertEqual(repeat1._local.instance_id, 0) self.assertEqual(lock2, 1) self.assertEqual(repeat2._local.instance_id, 0) self.assertEqual(lock3, 0) self.assertEqual(repeat3._local.instance_id, 1) self.assertEqual(lock4, 1) self.assertEqual(repeat4._local.instance_id, 1) # Clean up warnings repeat1.release(force=True) repeat2.release(force=True) repeat3.release(force=True) repeat4.release(force=True)
def test_acquire_single(self): # test acquiring a lock in a single thread test1 = Resource('single', 2, 1) test2 = Resource('single', 2, 1) test3 = Resource('single', 2, 1) lock1 = test1.acquire() lock2 = test1.acquire() # Acquiring twice, should use the same value self.assertEqual(lock1, lock2) lock3 = test2.acquire() # However, using an entirely different resource instance, won't have cache self.assertNotEqual(lock1, lock3) # At this point, I should be out of resources with self.assertRaises(ResourceError): test3.acquire() # cleanup warnings test1.release(force=True) test2.release(force=True)
def test_none(self): # Early version of the code used None in such a way it tripped up the logic # This test is to make sure that doesn't happen again. resource1 = Resource('none', [None, None, 1], 1) resource2 = Resource('none', [None, None, 1], 1) resource3 = Resource('none', [None, None], 1) n1 = resource1.acquire() self.assertIsNone(n1) # Prevent the accidental delete lock loophole, which would create a race # condition, if not caught lock1 = resource1._local.lock self.assertIsNone(resource1.acquire()) n2 = resource2.acquire() self.assertIsNone(n2) # resource2 should already be acquired, make sure it's not accidentally # unlocking and relocking again lock1.release() self.assertIsNone(resource2.acquire()) lock1.acquire(timeout=0) # two unique names self.assertEqual( len({ resource1._local.lock.lock_file, resource2._local.lock.lock_file }), 2) with self.assertRaises(ResourceError): resource3.acquire() # Clean up warnings resource1.release(force=True) resource2.release(force=True)
def test_multiple_release(self): test = Resource('test', 1, 1) self.assertFalse(test.is_locked) test.acquire() self.assertTrue(test.is_locked) self.assertEqual(test._local.lock._lock_counter, 1) test.acquire() self.assertTrue(test.is_locked) self.assertEqual(test._local.lock._lock_counter, 2) test.release() self.assertTrue(test.is_locked) self.assertEqual(test._local.lock._lock_counter, 1) test.release() self.assertFalse(test.is_locked) with self.assertRaisesRegex(ValueError, "Release called with no lock acquired"): test.release()
def test_force_release(self): test = Resource('test', 1, 1) test.acquire() test.acquire() test.release(force=True) self.assertFalse(test.is_locked)