Add tests for locks with byte ranges.

This commit is contained in:
Todd Gamblin 2016-10-06 00:34:56 -07:00
parent 3d8d8d3644
commit 080a78664e

View File

@ -25,6 +25,7 @@
""" """
These tests ensure that our lock works correctly. These tests ensure that our lock works correctly.
""" """
import os
import shutil import shutil
import tempfile import tempfile
import unittest import unittest
@ -63,98 +64,185 @@ def multiproc_test(self, *functions):
# #
# Process snippets below can be composed into tests. # Process snippets below can be composed into tests.
# #
def acquire_write(self, barrier): def acquire_write(self, start=0, length=0):
lock = Lock(self.lock_path) def fn(barrier):
lock.acquire_write() # grab exclusive lock lock = Lock(self.lock_path, start, length)
barrier.wait() lock.acquire_write() # grab exclusive lock
barrier.wait() # hold the lock until exception raises in other procs. barrier.wait()
barrier.wait() # hold the lock until timeout in other procs.
return fn
def acquire_read(self, barrier): def acquire_read(self, start=0, length=0):
lock = Lock(self.lock_path) def fn(barrier):
lock.acquire_read() # grab shared lock lock = Lock(self.lock_path, start, length)
barrier.wait() lock.acquire_read() # grab shared lock
barrier.wait() # hold the lock until exception raises in other procs. barrier.wait()
barrier.wait() # hold the lock until timeout in other procs.
return fn
def timeout_write(self, barrier): def timeout_write(self, start=0, length=0):
lock = Lock(self.lock_path) def fn(barrier):
barrier.wait() # wait for lock acquire in first process lock = Lock(self.lock_path, start, length)
self.assertRaises(LockError, lock.acquire_write, 0.1) barrier.wait() # wait for lock acquire in first process
barrier.wait() self.assertRaises(LockError, lock.acquire_write, 0.1)
barrier.wait()
return fn
def timeout_read(self, barrier): def timeout_read(self, start=0, length=0):
lock = Lock(self.lock_path) def fn(barrier):
barrier.wait() # wait for lock acquire in first process lock = Lock(self.lock_path, start, length)
self.assertRaises(LockError, lock.acquire_read, 0.1) barrier.wait() # wait for lock acquire in first process
barrier.wait() self.assertRaises(LockError, lock.acquire_read, 0.1)
barrier.wait()
return fn
# #
# Test that exclusive locks on other processes time out when an # Test that exclusive locks on other processes time out when an
# exclusive lock is held. # exclusive lock is held.
# #
def test_write_lock_timeout_on_write(self): def test_write_lock_timeout_on_write(self):
self.multiproc_test(self.acquire_write, self.timeout_write) self.multiproc_test(self.acquire_write(), self.timeout_write())
def test_write_lock_timeout_on_write_2(self): def test_write_lock_timeout_on_write_2(self):
self.multiproc_test( self.multiproc_test(
self.acquire_write, self.timeout_write, self.timeout_write) self.acquire_write(), self.timeout_write(), self.timeout_write())
def test_write_lock_timeout_on_write_3(self): def test_write_lock_timeout_on_write_3(self):
self.multiproc_test( self.multiproc_test(
self.acquire_write, self.timeout_write, self.timeout_write, self.acquire_write(), self.timeout_write(), self.timeout_write(),
self.timeout_write) self.timeout_write())
def test_write_lock_timeout_on_write_ranges(self):
self.multiproc_test(
self.acquire_write(0, 1), self.timeout_write(0, 1))
def test_write_lock_timeout_on_write_ranges_2(self):
self.multiproc_test(
self.acquire_write(0, 64), self.acquire_write(65, 1),
self.timeout_write(0, 1), self.timeout_write(63, 1))
def test_write_lock_timeout_on_write_ranges_3(self):
self.multiproc_test(
self.acquire_write(0, 1), self.acquire_write(1, 1),
self.timeout_write(), self.timeout_write(), self.timeout_write())
def test_write_lock_timeout_on_write_ranges_4(self):
self.multiproc_test(
self.acquire_write(0, 1), self.acquire_write(1, 1),
self.acquire_write(2, 456), self.acquire_write(500, 64),
self.timeout_write(), self.timeout_write(), self.timeout_write())
# #
# Test that shared locks on other processes time out when an # Test that shared locks on other processes time out when an
# exclusive lock is held. # exclusive lock is held.
# #
def test_read_lock_timeout_on_write(self): def test_read_lock_timeout_on_write(self):
self.multiproc_test(self.acquire_write, self.timeout_read) self.multiproc_test(self.acquire_write(), self.timeout_read())
def test_read_lock_timeout_on_write_2(self): def test_read_lock_timeout_on_write_2(self):
self.multiproc_test( self.multiproc_test(
self.acquire_write, self.timeout_read, self.timeout_read) self.acquire_write(), self.timeout_read(), self.timeout_read())
def test_read_lock_timeout_on_write_3(self): def test_read_lock_timeout_on_write_3(self):
self.multiproc_test( self.multiproc_test(
self.acquire_write, self.timeout_read, self.timeout_read, self.acquire_write(), self.timeout_read(), self.timeout_read(),
self.timeout_read) self.timeout_read())
def test_read_lock_timeout_on_write_ranges(self):
"""small write lock, read whole file."""
self.multiproc_test(self.acquire_write(0, 1), self.timeout_read())
def test_read_lock_timeout_on_write_ranges_2(self):
"""small write lock, small read lock"""
self.multiproc_test(self.acquire_write(0, 1), self.timeout_read(0, 1))
def test_read_lock_timeout_on_write_ranges_3(self):
"""two write locks, overlapping read locks"""
self.multiproc_test(
self.acquire_write(0, 1), self.acquire_write(64, 128),
self.timeout_read(0, 1), self.timeout_read(128, 256))
# #
# Test that exclusive locks time out when shared locks are held. # Test that exclusive locks time out when shared locks are held.
# #
def test_write_lock_timeout_on_read(self): def test_write_lock_timeout_on_read(self):
self.multiproc_test(self.acquire_read, self.timeout_write) self.multiproc_test(self.acquire_read(), self.timeout_write())
def test_write_lock_timeout_on_read_2(self): def test_write_lock_timeout_on_read_2(self):
self.multiproc_test( self.multiproc_test(
self.acquire_read, self.timeout_write, self.timeout_write) self.acquire_read(), self.timeout_write(), self.timeout_write())
def test_write_lock_timeout_on_read_3(self): def test_write_lock_timeout_on_read_3(self):
self.multiproc_test( self.multiproc_test(
self.acquire_read, self.timeout_write, self.timeout_write, self.acquire_read(), self.timeout_write(), self.timeout_write(),
self.timeout_write) self.timeout_write())
def test_write_lock_timeout_on_read_ranges(self):
self.multiproc_test(self.acquire_read(0, 1), self.timeout_write())
def test_write_lock_timeout_on_read_ranges_2(self):
self.multiproc_test(self.acquire_read(0, 1), self.timeout_write(0, 1))
def test_write_lock_timeout_on_read_ranges_3(self):
self.multiproc_test(
self.acquire_read(0, 1), self.acquire_read(10, 1),
self.timeout_write(0, 1), self.timeout_write(10, 1))
def test_write_lock_timeout_on_read_ranges_4(self):
self.multiproc_test(
self.acquire_read(0, 64),
self.timeout_write(10, 1), self.timeout_write(32, 1))
def test_write_lock_timeout_on_read_ranges_5(self):
self.multiproc_test(
self.acquire_read(64, 128),
self.timeout_write(65, 1), self.timeout_write(127, 1),
self.timeout_write(90, 10))
# #
# Test that exclusive locks time while lots of shared locks are held. # Test that exclusive locks time while lots of shared locks are held.
# #
def test_write_lock_timeout_with_multiple_readers_2_1(self): def test_write_lock_timeout_with_multiple_readers_2_1(self):
self.multiproc_test( self.multiproc_test(
self.acquire_read, self.acquire_read, self.timeout_write) self.acquire_read(), self.acquire_read(), self.timeout_write())
def test_write_lock_timeout_with_multiple_readers_2_2(self): def test_write_lock_timeout_with_multiple_readers_2_2(self):
self.multiproc_test( self.multiproc_test(
self.acquire_read, self.acquire_read, self.timeout_write, self.acquire_read(), self.acquire_read(), self.timeout_write(),
self.timeout_write) self.timeout_write())
def test_write_lock_timeout_with_multiple_readers_3_1(self): def test_write_lock_timeout_with_multiple_readers_3_1(self):
self.multiproc_test( self.multiproc_test(
self.acquire_read, self.acquire_read, self.acquire_read, self.acquire_read(), self.acquire_read(), self.acquire_read(),
self.timeout_write) self.timeout_write())
def test_write_lock_timeout_with_multiple_readers_3_2(self): def test_write_lock_timeout_with_multiple_readers_3_2(self):
self.multiproc_test( self.multiproc_test(
self.acquire_read, self.acquire_read, self.acquire_read, self.acquire_read(), self.acquire_read(), self.acquire_read(),
self.timeout_write, self.timeout_write) self.timeout_write(), self.timeout_write())
def test_write_lock_timeout_with_multiple_readers_2_1_ranges(self):
self.multiproc_test(
self.acquire_read(0, 10), self.acquire_read(5, 10),
self.timeout_write(5, 5))
def test_write_lock_timeout_with_multiple_readers_2_3_ranges(self):
self.multiproc_test(
self.acquire_read(0, 10), self.acquire_read(5, 15),
self.timeout_write(0, 1), self.timeout_write(11, 3),
self.timeout_write(7, 1))
def test_write_lock_timeout_with_multiple_readers_3_1_ranges(self):
self.multiproc_test(
self.acquire_read(0, 5), self.acquire_read(5, 5),
self.acquire_read(10, 5),
self.timeout_write(0, 15))
def test_write_lock_timeout_with_multiple_readers_3_2_ranges(self):
self.multiproc_test(
self.acquire_read(0, 5), self.acquire_read(5, 5),
self.acquire_read(10, 5),
self.timeout_write(3, 10), self.timeout_write(5, 1))
# #
# Test that read can be upgraded to write. # Test that read can be upgraded to write.
@ -171,7 +259,7 @@ def test_upgrade_read_to_write(self):
lock.acquire_read() lock.acquire_read()
self.assertTrue(lock._reads == 1) self.assertTrue(lock._reads == 1)
self.assertTrue(lock._writes == 0) self.assertTrue(lock._writes == 0)
self.assertTrue(lock._file.mode == 'r') self.assertTrue(lock._file.mode == 'r+')
lock.acquire_write() lock.acquire_write()
self.assertTrue(lock._reads == 1) self.assertTrue(lock._reads == 1)
@ -188,6 +276,26 @@ def test_upgrade_read_to_write(self):
self.assertTrue(lock._writes == 0) self.assertTrue(lock._writes == 0)
self.assertTrue(lock._file is None) self.assertTrue(lock._file is None)
#
# Test that read-only file can be read-locked but not write-locked.
#
def test_upgrade_read_to_write_fails_with_readonly_file(self):
# ensure lock file exists the first time, so we open it read-only
# to begin wtih.
touch(self.lock_path)
os.chmod(self.lock_path, 0444)
lock = Lock(self.lock_path)
self.assertTrue(lock._reads == 0)
self.assertTrue(lock._writes == 0)
lock.acquire_read()
self.assertTrue(lock._reads == 1)
self.assertTrue(lock._writes == 0)
self.assertTrue(lock._file.mode == 'r')
self.assertRaises(LockError, lock.acquire_write)
# #
# Longer test case that ensures locks are reusable. Ordering is # Longer test case that ensures locks are reusable. Ordering is
# enforced by barriers throughout -- steps are shown with numbers. # enforced by barriers throughout -- steps are shown with numbers.