2 # Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
3 # Use of this source code is governed by a BSD-style license that can be
4 # found in the LICENSE file.
6 """Test flock library."""
8 from __future__ import print_function
11 import multiprocessing
17 fixup_path.FixupPath()
19 from chromite.lib import cros_test_lib
20 from chromite.lib import osutils
21 from chromite.lib.paygen import flock
28 class FLockTest(mox.MoxTestBase):
29 """Test FLock lock class."""
31 def __init__(self, testCaseNames):
33 mox.MoxTestBase.__init__(self, testCaseNames)
36 """Prepare for each test."""
39 # To make certain we don't self update while running tests.
40 os.environ['CROSTOOLS_NO_SOURCE_UPDATE'] = '1'
43 """Cleanup after each test."""
46 @osutils.TempDirDecorator
47 def _HelperSingleLockTest(self, blocking, shared):
48 """Helper method that runs a basic test with/without blocking/sharing."""
49 lock = flock.Lock('SingleLock',
50 lock_dir=self.tempdir,
54 expected_lock_file = os.path.join(self.tempdir, 'SingleLock')
56 self.assertFalse(os.path.exists(expected_lock_file))
57 self.assertFalse(lock.IsLocked())
59 self.assertTrue(os.path.exists(expected_lock_file))
60 self.assertTrue(lock.IsLocked())
62 # Acquiring the lock again should be safe.
64 self.assertTrue(lock.IsLocked())
66 # Ensure the lock file contains our pid, and nothing else.
67 fd = open(expected_lock_file, 'r')
68 self.assertEquals(['%d\n' % os.getpid()], fd.readlines())
72 self.assertFalse(lock.IsLocked())
74 @osutils.TempDirDecorator
75 def _HelperDoubleLockTest(self, blocking1, shared1, blocking2, shared2):
76 """Helper method that runs a two-lock test with/without blocking/sharing."""
77 lock1 = flock.Lock('DoubleLock',
78 lock_dir=self.tempdir,
81 lock2 = flock.Lock('DoubleLock',
82 lock_dir=self.tempdir,
87 self.assertTrue(lock1.IsLocked())
88 self.assertFalse(lock2.IsLocked())
90 # The second lock should fail to acquire.
91 self.assertRaises(flock.LockNotAcquired, lock2.Acquire)
92 self.assertTrue(lock1.IsLocked())
93 self.assertFalse(lock2.IsLocked())
96 self.assertFalse(lock1.IsLocked())
97 self.assertFalse(lock2.IsLocked())
99 # Releasing second lock should be harmless.
101 self.assertFalse(lock1.IsLocked())
102 self.assertFalse(lock2.IsLocked())
104 def _HelperInsideProcess(self, name, lock_dir, blocking, shared):
105 """Helper method that runs a basic test with/without blocking."""
108 with flock.Lock(name,
113 sys.exit(LOCK_ACQUIRED)
114 except flock.LockNotAcquired:
115 sys.exit(LOCK_NOT_ACQUIRED)
117 def _HelperStartProcess(self, name, blocking=False, shared=False):
118 """Create a process and invoke _HelperInsideProcess in it."""
119 p = multiprocessing.Process(target=self._HelperInsideProcess,
120 args=(name, self.tempdir, blocking, shared))
123 # It's highly probably that p will have tried to grab the lock before the
124 # timer expired, but not certain.
129 def _HelperWithProcess(self, name, expected, blocking=False, shared=False):
130 """Create a process and invoke _HelperInsideProcess in it."""
131 p = multiprocessing.Process(target=self._HelperInsideProcess,
132 args=(name, self.tempdir, blocking, shared))
135 self.assertEquals(p.exitcode, expected)
137 def testLockName(self):
138 """Make sure that we get the expected lock file name."""
139 lock = flock.Lock(lock_name='/tmp/foo')
140 self.assertEqual(lock.lock_file, '/tmp/foo')
142 lock = flock.Lock(lock_name='foo')
143 self.assertEqual(lock.lock_file, '/tmp/run_once/foo')
145 lock = flock.Lock(lock_name='foo', lock_dir='/bar')
146 self.assertEqual(lock.lock_file, '/bar/foo')
148 def testSingleLock(self):
149 """Just test getting releasing a lock with options."""
150 self._HelperSingleLockTest(blocking=False, shared=False)
151 self._HelperSingleLockTest(blocking=True, shared=False)
152 self._HelperSingleLockTest(blocking=False, shared=True)
153 self._HelperSingleLockTest(blocking=True, shared=True)
155 def testDoubleLock(self):
156 """Test two lock objects for the same lock file."""
157 self._HelperDoubleLockTest(blocking1=False, shared1=False,
158 blocking2=False, shared2=False)
160 def testContextMgr(self):
161 """Make sure we behave properly with 'with'."""
165 # Create an instance, and use it in a with
166 prelock = flock.Lock(name, lock_dir=self.tempdir)
167 self._HelperWithProcess(name, expected=LOCK_ACQUIRED)
169 with prelock as lock:
170 # Assert the instance didn't change.
171 self.assertIs(prelock, lock)
172 self._HelperWithProcess(name, expected=LOCK_NOT_ACQUIRED)
174 self._HelperWithProcess(name, expected=LOCK_ACQUIRED)
176 # Construct the instance in the with expression.
177 with flock.Lock(name, lock_dir=self.tempdir) as lock:
178 self.assertIsInstance(lock, flock.Lock)
179 self._HelperWithProcess(name, expected=LOCK_NOT_ACQUIRED)
181 self._HelperWithProcess(name, expected=LOCK_ACQUIRED)
183 def testAcquireBeforeWith(self):
184 """Sometimes you want to Acquire a lock and then return it into 'with'."""
187 lock = flock.Lock(name, lock_dir=self.tempdir)
190 self._HelperWithProcess(name, expected=LOCK_NOT_ACQUIRED)
193 self._HelperWithProcess(name, expected=LOCK_NOT_ACQUIRED)
195 self._HelperWithProcess(name, expected=LOCK_ACQUIRED)
197 @osutils.TempDirDecorator
198 def testSingleProcessLock(self):
199 """Test grabbing the same lock in processes with no conflicts."""
200 self._HelperWithProcess('ProcessLock', expected=LOCK_ACQUIRED)
201 self._HelperWithProcess('ProcessLock', expected=LOCK_ACQUIRED)
202 self._HelperWithProcess('ProcessLock', expected=LOCK_ACQUIRED,
204 self._HelperWithProcess('ProcessLock', expected=LOCK_ACQUIRED,
206 self._HelperWithProcess('ProcessLock', expected=LOCK_ACQUIRED,
207 blocking=True, shared=True)
209 @osutils.TempDirDecorator
210 def testNonBlockingConflicts(self):
211 """Test that we get a lock conflict for non-blocking locks."""
213 with flock.Lock(name, lock_dir=self.tempdir):
214 self._HelperWithProcess(name,
215 expected=LOCK_NOT_ACQUIRED)
217 self._HelperWithProcess(name,
218 expected=LOCK_NOT_ACQUIRED,
221 # Can grab it after it's released
222 self._HelperWithProcess(name, expected=LOCK_ACQUIRED)
224 @osutils.TempDirDecorator
225 def testSharedLocks(self):
226 """Test lock conflict for blocking locks."""
229 # Intial lock is NOT shared
230 with flock.Lock(name, lock_dir=self.tempdir, shared=False):
231 self._HelperWithProcess(name, expected=LOCK_NOT_ACQUIRED, shared=True)
233 # Intial lock IS shared
234 with flock.Lock(name, lock_dir=self.tempdir, shared=True):
235 self._HelperWithProcess(name, expected=LOCK_ACQUIRED, shared=True)
236 self._HelperWithProcess(name, expected=LOCK_NOT_ACQUIRED, shared=False)
238 @osutils.TempDirDecorator
239 def testBlockingConflicts(self):
240 """Test lock conflict for blocking locks."""
243 # Intial lock is blocking, exclusive
244 with flock.Lock(name, lock_dir=self.tempdir, blocking=True):
245 self._HelperWithProcess(name,
246 expected=LOCK_NOT_ACQUIRED,
249 p = self._HelperStartProcess(name, blocking=True, shared=False)
251 # when the with clause exits, p should unblock and get the lock, setting
252 # its exit code to sucess now.
254 self.assertEquals(p.exitcode, LOCK_ACQUIRED)
256 # Intial lock is NON blocking
257 with flock.Lock(name, lock_dir=self.tempdir):
258 self._HelperWithProcess(name, expected=LOCK_NOT_ACQUIRED)
260 p = self._HelperStartProcess(name, blocking=True, shared=False)
262 # when the with clause exits, p should unblock and get the lock, setting
263 # it's exit code to sucess now.
265 self.assertEquals(p.exitcode, LOCK_ACQUIRED)
267 # Intial lock is shared, blocking lock is exclusive
268 with flock.Lock(name, lock_dir=self.tempdir, shared=True):
269 self._HelperWithProcess(name, expected=LOCK_NOT_ACQUIRED)
270 self._HelperWithProcess(name, expected=LOCK_ACQUIRED, shared=True)
272 p = self._HelperStartProcess(name, blocking=True, shared=False)
273 q = self._HelperStartProcess(name, blocking=True, shared=False)
275 # when the with clause exits, p should unblock and get the lock, setting
276 # it's exit code to sucess now.
278 self.assertEquals(p.exitcode, LOCK_ACQUIRED)
280 self.assertEquals(p.exitcode, LOCK_ACQUIRED)
283 if __name__ == '__main__':