From 00a0e70fe68bf21c60bf53ed54a936c518ef29a2 Mon Sep 17 00:00:00 2001 From: Brad Warren Date: Fri, 17 Mar 2017 12:16:34 -0700 Subject: [PATCH] test lock contention --- certbot/tests/main_test.py | 40 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/certbot/tests/main_test.py b/certbot/tests/main_test.py index 7d5f065e2..d0fde766b 100644 --- a/certbot/tests/main_test.py +++ b/certbot/tests/main_test.py @@ -4,6 +4,7 @@ from __future__ import print_function import itertools import mock +import multiprocessing import os import shutil import tempfile @@ -1311,12 +1312,51 @@ class TestHandleException(unittest.TestCase): class TestAcquireFileLock(unittest.TestCase): """Test main.acquire_file_lock.""" + def setUp(self): + self.tempdir = tempfile.mkdtemp() + self.lock_path = os.path.join(self.tempdir, 'certbot.lock') + + def tearDown(self): + shutil.rmtree(self.tempdir) + @mock.patch('certbot.main.logger') def test_bad_path(self, mock_logger): lock = main.acquire_file_lock(os.getcwd()) self.assertTrue(mock_logger.warning.called) self.assertFalse(lock.acquired) + def test_held_lock(self): + # start child and wait for it to grab the lock + cv = multiprocessing.Condition() + cv.acquire() + child_args = (cv, self.lock_path,) + child = multiprocessing.Process(target=_hold_lock, args=child_args) + child.start() + cv.wait() + + # assert we can't grab lock and terminate the child + self.assertRaises(errors.Error, main.acquire_file_lock, self.lock_path) + cv.notify() + cv.release() + child.join() + self.assertEqual(child.exitcode, 0) + + +def _hold_lock(cv, lock_path): + """Acquire a file lock at lock_path and wait to release it. + + :param multiprocessing.Condition cv: condition for syncronization + :param str lock_path: path to the file lock + + """ + import fasteners + lock = fasteners.InterProcessLock(lock_path) + lock.acquire() + cv.acquire() + cv.notify() + cv.wait() + lock.release() + if __name__ == '__main__': unittest.main() # pragma: no cover