1fc46f261SDan Klein# -*- coding: utf-8 -*-
2fc46f261SDan Klein#
3fc46f261SDan Klein# test/test_pidfile.py
4fc46f261SDan Klein# Part of ‘python-daemon’, an implementation of PEP 3143.
5fc46f261SDan Klein#
6fc46f261SDan Klein# Copyright © 2008–2015 Ben Finney <ben+python@benfinney.id.au>
7fc46f261SDan Klein#
8fc46f261SDan Klein# This is free software: you may copy, modify, and/or distribute this work
9fc46f261SDan Klein# under the terms of the Apache License, version 2.0 as published by the
10fc46f261SDan Klein# Apache Software Foundation.
11fc46f261SDan Klein# No warranty expressed or implied. See the file ‘LICENSE.ASF-2’ for details.
12fc46f261SDan Klein
13fc46f261SDan Klein""" Unit test for ‘pidfile’ module.
14fc46f261SDan Klein    """
15fc46f261SDan Klein
16fc46f261SDan Kleinfrom __future__ import (absolute_import, unicode_literals)
17fc46f261SDan Klein
18fc46f261SDan Kleintry:
19fc46f261SDan Klein    # Python 3 standard library.
20fc46f261SDan Klein    import builtins
21fc46f261SDan Kleinexcept ImportError:
22fc46f261SDan Klein    # Python 2 standard library.
23fc46f261SDan Klein    import __builtin__ as builtins
24fc46f261SDan Kleinimport os
25fc46f261SDan Kleinimport itertools
26fc46f261SDan Kleinimport tempfile
27fc46f261SDan Kleinimport errno
28fc46f261SDan Kleinimport functools
29fc46f261SDan Kleintry:
30fc46f261SDan Klein    # Standard library of Python 2.7 and later.
31fc46f261SDan Klein    from io import StringIO
32fc46f261SDan Kleinexcept ImportError:
33fc46f261SDan Klein    # Standard library of Python 2.6 and earlier.
34fc46f261SDan Klein    from StringIO import StringIO
35fc46f261SDan Klein
36fc46f261SDan Kleinimport mock
37fc46f261SDan Kleinimport lockfile
38fc46f261SDan Klein
39fc46f261SDan Kleinfrom . import scaffold
40fc46f261SDan Klein
41fc46f261SDan Kleinimport daemon.pidfile
42fc46f261SDan Klein
43fc46f261SDan Klein
44fc46f261SDan Kleinclass FakeFileDescriptorStringIO(StringIO, object):
45fc46f261SDan Klein    """ A StringIO class that fakes a file descriptor. """
46fc46f261SDan Klein
47fc46f261SDan Klein    _fileno_generator = itertools.count()
48fc46f261SDan Klein
49fc46f261SDan Klein    def __init__(self, *args, **kwargs):
50fc46f261SDan Klein        self._fileno = next(self._fileno_generator)
51fc46f261SDan Klein        super(FakeFileDescriptorStringIO, self).__init__(*args, **kwargs)
52fc46f261SDan Klein
53fc46f261SDan Klein    def fileno(self):
54fc46f261SDan Klein        return self._fileno
55fc46f261SDan Klein
56fc46f261SDan Klein    def __enter__(self):
57fc46f261SDan Klein        return self
58fc46f261SDan Klein
59fc46f261SDan Klein    def __exit__(self, exc_type, exc_val, exc_tb):
60fc46f261SDan Klein        pass
61fc46f261SDan Klein
62fc46f261SDan Klein
63fc46f261SDan Kleintry:
64fc46f261SDan Klein    FileNotFoundError
65fc46f261SDan Klein    PermissionError
66fc46f261SDan Kleinexcept NameError:
67fc46f261SDan Klein    # Python 2 uses IOError.
68fc46f261SDan Klein    FileNotFoundError = functools.partial(IOError, errno.ENOENT)
69fc46f261SDan Klein    PermissionError = functools.partial(IOError, errno.EPERM)
70fc46f261SDan Klein
71fc46f261SDan Klein
72fc46f261SDan Kleindef make_pidlockfile_scenarios():
73fc46f261SDan Klein    """ Make a collection of scenarios for testing `PIDLockFile` instances.
74fc46f261SDan Klein
75fc46f261SDan Klein        :return: A collection of scenarios for tests involving
76fc46f261SDan Klein            `PIDLockfFile` instances.
77fc46f261SDan Klein
78fc46f261SDan Klein        The collection is a mapping from scenario name to a dictionary of
79fc46f261SDan Klein        scenario attributes.
80fc46f261SDan Klein
81fc46f261SDan Klein        """
82fc46f261SDan Klein
83fc46f261SDan Klein    fake_current_pid = 235
84fc46f261SDan Klein    fake_other_pid = 8642
85fc46f261SDan Klein    fake_pidfile_path = tempfile.mktemp()
86fc46f261SDan Klein
87fc46f261SDan Klein    fake_pidfile_empty = FakeFileDescriptorStringIO()
88fc46f261SDan Klein    fake_pidfile_current_pid = FakeFileDescriptorStringIO(
89fc46f261SDan Klein            "{pid:d}\n".format(pid=fake_current_pid))
90fc46f261SDan Klein    fake_pidfile_other_pid = FakeFileDescriptorStringIO(
91fc46f261SDan Klein            "{pid:d}\n".format(pid=fake_other_pid))
92fc46f261SDan Klein    fake_pidfile_bogus = FakeFileDescriptorStringIO(
93fc46f261SDan Klein            "b0gUs")
94fc46f261SDan Klein
95fc46f261SDan Klein    scenarios = {
96fc46f261SDan Klein            'simple': {},
97fc46f261SDan Klein            'not-exist': {
98fc46f261SDan Klein                'open_func_name': 'fake_open_nonexist',
99fc46f261SDan Klein                'os_open_func_name': 'fake_os_open_nonexist',
100fc46f261SDan Klein                },
101fc46f261SDan Klein            'not-exist-write-denied': {
102fc46f261SDan Klein                'open_func_name': 'fake_open_nonexist',
103fc46f261SDan Klein                'os_open_func_name': 'fake_os_open_nonexist',
104fc46f261SDan Klein                },
105fc46f261SDan Klein            'not-exist-write-busy': {
106fc46f261SDan Klein                'open_func_name': 'fake_open_nonexist',
107fc46f261SDan Klein                'os_open_func_name': 'fake_os_open_nonexist',
108fc46f261SDan Klein                },
109fc46f261SDan Klein            'exist-read-denied': {
110fc46f261SDan Klein                'open_func_name': 'fake_open_read_denied',
111fc46f261SDan Klein                'os_open_func_name': 'fake_os_open_read_denied',
112fc46f261SDan Klein                },
113fc46f261SDan Klein            'exist-locked-read-denied': {
114fc46f261SDan Klein                'locking_pid': fake_other_pid,
115fc46f261SDan Klein                'open_func_name': 'fake_open_read_denied',
116fc46f261SDan Klein                'os_open_func_name': 'fake_os_open_read_denied',
117fc46f261SDan Klein                },
118fc46f261SDan Klein            'exist-empty': {},
119fc46f261SDan Klein            'exist-invalid': {
120fc46f261SDan Klein                'pidfile': fake_pidfile_bogus,
121fc46f261SDan Klein                },
122fc46f261SDan Klein            'exist-current-pid': {
123fc46f261SDan Klein                'pidfile': fake_pidfile_current_pid,
124fc46f261SDan Klein                'pidfile_pid': fake_current_pid,
125fc46f261SDan Klein                },
126fc46f261SDan Klein            'exist-current-pid-locked': {
127fc46f261SDan Klein                'pidfile': fake_pidfile_current_pid,
128fc46f261SDan Klein                'pidfile_pid': fake_current_pid,
129fc46f261SDan Klein                'locking_pid': fake_current_pid,
130fc46f261SDan Klein                },
131fc46f261SDan Klein            'exist-other-pid': {
132fc46f261SDan Klein                'pidfile': fake_pidfile_other_pid,
133fc46f261SDan Klein                'pidfile_pid': fake_other_pid,
134fc46f261SDan Klein                },
135fc46f261SDan Klein            'exist-other-pid-locked': {
136fc46f261SDan Klein                'pidfile': fake_pidfile_other_pid,
137fc46f261SDan Klein                'pidfile_pid': fake_other_pid,
138fc46f261SDan Klein                'locking_pid': fake_other_pid,
139fc46f261SDan Klein                },
140fc46f261SDan Klein            }
141fc46f261SDan Klein
142fc46f261SDan Klein    for scenario in scenarios.values():
143fc46f261SDan Klein        scenario['pid'] = fake_current_pid
144fc46f261SDan Klein        scenario['pidfile_path'] = fake_pidfile_path
145fc46f261SDan Klein        if 'pidfile' not in scenario:
146fc46f261SDan Klein            scenario['pidfile'] = fake_pidfile_empty
147fc46f261SDan Klein        if 'pidfile_pid' not in scenario:
148fc46f261SDan Klein            scenario['pidfile_pid'] = None
149fc46f261SDan Klein        if 'locking_pid' not in scenario:
150fc46f261SDan Klein            scenario['locking_pid'] = None
151fc46f261SDan Klein        if 'open_func_name' not in scenario:
152fc46f261SDan Klein            scenario['open_func_name'] = 'fake_open_okay'
153fc46f261SDan Klein        if 'os_open_func_name' not in scenario:
154fc46f261SDan Klein            scenario['os_open_func_name'] = 'fake_os_open_okay'
155fc46f261SDan Klein
156fc46f261SDan Klein    return scenarios
157fc46f261SDan Klein
158fc46f261SDan Klein
159fc46f261SDan Kleindef setup_pidfile_fixtures(testcase):
160fc46f261SDan Klein    """ Set up common fixtures for PID file test cases.
161fc46f261SDan Klein
162fc46f261SDan Klein        :param testcase: A `TestCase` instance to decorate.
163fc46f261SDan Klein
164fc46f261SDan Klein        Decorate the `testcase` with attributes to be fixtures for tests
165fc46f261SDan Klein        involving `PIDLockFile` instances.
166fc46f261SDan Klein
167fc46f261SDan Klein        """
168fc46f261SDan Klein    scenarios = make_pidlockfile_scenarios()
169fc46f261SDan Klein    testcase.pidlockfile_scenarios = scenarios
170fc46f261SDan Klein
171fc46f261SDan Klein    def get_scenario_option(testcase, key, default=None):
172fc46f261SDan Klein        value = default
173fc46f261SDan Klein        try:
174fc46f261SDan Klein            value = testcase.scenario[key]
175fc46f261SDan Klein        except (NameError, TypeError, AttributeError, KeyError):
176fc46f261SDan Klein            pass
177fc46f261SDan Klein        return value
178fc46f261SDan Klein
179fc46f261SDan Klein    func_patcher_os_getpid = mock.patch.object(
180fc46f261SDan Klein            os, "getpid",
181fc46f261SDan Klein            return_value=scenarios['simple']['pid'])
182fc46f261SDan Klein    func_patcher_os_getpid.start()
183fc46f261SDan Klein    testcase.addCleanup(func_patcher_os_getpid.stop)
184fc46f261SDan Klein
185fc46f261SDan Klein    def make_fake_open_funcs(testcase):
186fc46f261SDan Klein
187fc46f261SDan Klein        def fake_open_nonexist(filename, mode, buffering):
188fc46f261SDan Klein            if mode.startswith('r'):
189fc46f261SDan Klein                error = FileNotFoundError(
190fc46f261SDan Klein                        "No such file {filename!r}".format(
191fc46f261SDan Klein                            filename=filename))
192fc46f261SDan Klein                raise error
193fc46f261SDan Klein            else:
194fc46f261SDan Klein                result = testcase.scenario['pidfile']
195fc46f261SDan Klein            return result
196fc46f261SDan Klein
197fc46f261SDan Klein        def fake_open_read_denied(filename, mode, buffering):
198fc46f261SDan Klein            if mode.startswith('r'):
199fc46f261SDan Klein                error = PermissionError(
200fc46f261SDan Klein                        "Read denied on {filename!r}".format(
201fc46f261SDan Klein                            filename=filename))
202fc46f261SDan Klein                raise error
203fc46f261SDan Klein            else:
204fc46f261SDan Klein                result = testcase.scenario['pidfile']
205fc46f261SDan Klein            return result
206fc46f261SDan Klein
207fc46f261SDan Klein        def fake_open_okay(filename, mode, buffering):
208fc46f261SDan Klein            result = testcase.scenario['pidfile']
209fc46f261SDan Klein            return result
210fc46f261SDan Klein
211fc46f261SDan Klein        def fake_os_open_nonexist(filename, flags, mode):
212fc46f261SDan Klein            if (flags & os.O_CREAT):
213fc46f261SDan Klein                result = testcase.scenario['pidfile'].fileno()
214fc46f261SDan Klein            else:
215fc46f261SDan Klein                error = FileNotFoundError(
216fc46f261SDan Klein                        "No such file {filename!r}".format(
217fc46f261SDan Klein                            filename=filename))
218fc46f261SDan Klein                raise error
219fc46f261SDan Klein            return result
220fc46f261SDan Klein
221fc46f261SDan Klein        def fake_os_open_read_denied(filename, flags, mode):
222fc46f261SDan Klein            if (flags & os.O_CREAT):
223fc46f261SDan Klein                result = testcase.scenario['pidfile'].fileno()
224fc46f261SDan Klein            else:
225fc46f261SDan Klein                error = PermissionError(
226fc46f261SDan Klein                        "Read denied on {filename!r}".format(
227fc46f261SDan Klein                            filename=filename))
228fc46f261SDan Klein                raise error
229fc46f261SDan Klein            return result
230fc46f261SDan Klein
231fc46f261SDan Klein        def fake_os_open_okay(filename, flags, mode):
232fc46f261SDan Klein            result = testcase.scenario['pidfile'].fileno()
233fc46f261SDan Klein            return result
234fc46f261SDan Klein
235fc46f261SDan Klein        funcs = dict(
236fc46f261SDan Klein                (name, obj) for (name, obj) in vars().items()
237fc46f261SDan Klein                if callable(obj))
238fc46f261SDan Klein
239fc46f261SDan Klein        return funcs
240fc46f261SDan Klein
241fc46f261SDan Klein    testcase.fake_pidfile_open_funcs = make_fake_open_funcs(testcase)
242fc46f261SDan Klein
243fc46f261SDan Klein    def fake_open(filename, mode='rt', buffering=None):
244fc46f261SDan Klein        scenario_path = get_scenario_option(testcase, 'pidfile_path')
245fc46f261SDan Klein        if filename == scenario_path:
246fc46f261SDan Klein            func_name = testcase.scenario['open_func_name']
247fc46f261SDan Klein            fake_open_func = testcase.fake_pidfile_open_funcs[func_name]
248fc46f261SDan Klein            result = fake_open_func(filename, mode, buffering)
249fc46f261SDan Klein        else:
250fc46f261SDan Klein            result = FakeFileDescriptorStringIO()
251fc46f261SDan Klein        return result
252fc46f261SDan Klein
253fc46f261SDan Klein    mock_open = mock.mock_open()
254fc46f261SDan Klein    mock_open.side_effect = fake_open
255fc46f261SDan Klein
256fc46f261SDan Klein    func_patcher_builtin_open = mock.patch.object(
257fc46f261SDan Klein            builtins, "open",
258fc46f261SDan Klein            new=mock_open)
259fc46f261SDan Klein    func_patcher_builtin_open.start()
260fc46f261SDan Klein    testcase.addCleanup(func_patcher_builtin_open.stop)
261fc46f261SDan Klein
262fc46f261SDan Klein    def fake_os_open(filename, flags, mode=None):
263fc46f261SDan Klein        scenario_path = get_scenario_option(testcase, 'pidfile_path')
264fc46f261SDan Klein        if filename == scenario_path:
265fc46f261SDan Klein            func_name = testcase.scenario['os_open_func_name']
266fc46f261SDan Klein            fake_os_open_func = testcase.fake_pidfile_open_funcs[func_name]
267fc46f261SDan Klein            result = fake_os_open_func(filename, flags, mode)
268fc46f261SDan Klein        else:
269fc46f261SDan Klein            result = FakeFileDescriptorStringIO().fileno()
270fc46f261SDan Klein        return result
271fc46f261SDan Klein
272fc46f261SDan Klein    mock_os_open = mock.MagicMock(side_effect=fake_os_open)
273fc46f261SDan Klein
274fc46f261SDan Klein    func_patcher_os_open = mock.patch.object(
275fc46f261SDan Klein            os, "open",
276fc46f261SDan Klein            new=mock_os_open)
277fc46f261SDan Klein    func_patcher_os_open.start()
278fc46f261SDan Klein    testcase.addCleanup(func_patcher_os_open.stop)
279fc46f261SDan Klein
280fc46f261SDan Klein    def fake_os_fdopen(fd, mode='rt', buffering=None):
281fc46f261SDan Klein        scenario_pidfile = get_scenario_option(
282fc46f261SDan Klein                testcase, 'pidfile', FakeFileDescriptorStringIO())
283fc46f261SDan Klein        if fd == testcase.scenario['pidfile'].fileno():
284fc46f261SDan Klein            result = testcase.scenario['pidfile']
285fc46f261SDan Klein        else:
286fc46f261SDan Klein            raise OSError(errno.EBADF, "Bad file descriptor")
287fc46f261SDan Klein        return result
288fc46f261SDan Klein
289fc46f261SDan Klein    mock_os_fdopen = mock.MagicMock(side_effect=fake_os_fdopen)
290fc46f261SDan Klein
291fc46f261SDan Klein    func_patcher_os_fdopen = mock.patch.object(
292fc46f261SDan Klein            os, "fdopen",
293fc46f261SDan Klein            new=mock_os_fdopen)
294fc46f261SDan Klein    func_patcher_os_fdopen.start()
295fc46f261SDan Klein    testcase.addCleanup(func_patcher_os_fdopen.stop)
296fc46f261SDan Klein
297fc46f261SDan Klein
298fc46f261SDan Kleindef make_lockfile_method_fakes(scenario):
299fc46f261SDan Klein    """ Make common fake methods for lockfile class.
300fc46f261SDan Klein
301fc46f261SDan Klein        :param scenario: A scenario for testing with PIDLockFile.
302fc46f261SDan Klein        :return: A mapping from normal function name to the corresponding
303fc46f261SDan Klein            fake function.
304fc46f261SDan Klein
305fc46f261SDan Klein        Each fake function behaves appropriately for the specified `scenario`.
306fc46f261SDan Klein
307fc46f261SDan Klein        """
308fc46f261SDan Klein
309fc46f261SDan Klein    def fake_func_read_pid():
310fc46f261SDan Klein        return scenario['pidfile_pid']
311fc46f261SDan Klein    def fake_func_is_locked():
312fc46f261SDan Klein        return (scenario['locking_pid'] is not None)
313fc46f261SDan Klein    def fake_func_i_am_locking():
314fc46f261SDan Klein        return (
315fc46f261SDan Klein                scenario['locking_pid'] == scenario['pid'])
316fc46f261SDan Klein    def fake_func_acquire(timeout=None):
317fc46f261SDan Klein        if scenario['locking_pid'] is not None:
318fc46f261SDan Klein            raise lockfile.AlreadyLocked()
319fc46f261SDan Klein        scenario['locking_pid'] = scenario['pid']
320fc46f261SDan Klein    def fake_func_release():
321fc46f261SDan Klein        if scenario['locking_pid'] is None:
322fc46f261SDan Klein            raise lockfile.NotLocked()
323fc46f261SDan Klein        if scenario['locking_pid'] != scenario['pid']:
324fc46f261SDan Klein            raise lockfile.NotMyLock()
325fc46f261SDan Klein        scenario['locking_pid'] = None
326fc46f261SDan Klein    def fake_func_break_lock():
327fc46f261SDan Klein        scenario['locking_pid'] = None
328fc46f261SDan Klein
329fc46f261SDan Klein    fake_methods = dict(
330fc46f261SDan Klein            (
331fc46f261SDan Klein                func_name.replace('fake_func_', ''),
332fc46f261SDan Klein                mock.MagicMock(side_effect=fake_func))
333fc46f261SDan Klein            for (func_name, fake_func) in vars().items()
334fc46f261SDan Klein                if func_name.startswith('fake_func_'))
335fc46f261SDan Klein
336fc46f261SDan Klein    return fake_methods
337fc46f261SDan Klein
338fc46f261SDan Klein
339fc46f261SDan Kleindef apply_lockfile_method_mocks(mock_lockfile, testcase, scenario):
340fc46f261SDan Klein    """ Apply common fake methods to mock lockfile class.
341fc46f261SDan Klein
342fc46f261SDan Klein        :param mock_lockfile: An object providing the `LockFile` interface.
343fc46f261SDan Klein        :param testcase: The `TestCase` instance providing the context for
344fc46f261SDan Klein            the patch.
345fc46f261SDan Klein        :param scenario: The `PIDLockFile` test scenario to use.
346fc46f261SDan Klein
347fc46f261SDan Klein        Mock the `LockFile` methods of `mock_lockfile`, by applying fake
348fc46f261SDan Klein        methods customised for `scenario`. The mock is does by a patch
349fc46f261SDan Klein        within the context of `testcase`.
350fc46f261SDan Klein
351fc46f261SDan Klein        """
352fc46f261SDan Klein    fake_methods = dict(
353fc46f261SDan Klein            (func_name, fake_func)
354fc46f261SDan Klein            for (func_name, fake_func) in
355fc46f261SDan Klein                make_lockfile_method_fakes(scenario).items()
356fc46f261SDan Klein            if func_name not in ['read_pid'])
357fc46f261SDan Klein
358fc46f261SDan Klein    for (func_name, fake_func) in fake_methods.items():
359fc46f261SDan Klein        func_patcher = mock.patch.object(
360fc46f261SDan Klein                mock_lockfile, func_name,
361fc46f261SDan Klein                new=fake_func)
362fc46f261SDan Klein        func_patcher.start()
363fc46f261SDan Klein        testcase.addCleanup(func_patcher.stop)
364fc46f261SDan Klein
365fc46f261SDan Klein
366fc46f261SDan Kleindef setup_pidlockfile_fixtures(testcase, scenario_name=None):
367fc46f261SDan Klein    """ Set up common fixtures for PIDLockFile test cases.
368fc46f261SDan Klein
369fc46f261SDan Klein        :param testcase: The `TestCase` instance to decorate.
370fc46f261SDan Klein        :param scenario_name: The name of the `PIDLockFile` scenario to use.
371fc46f261SDan Klein
372fc46f261SDan Klein        Decorate the `testcase` with attributes that are fixtures for test
373fc46f261SDan Klein        cases involving `PIDLockFile` instances.`
374fc46f261SDan Klein
375fc46f261SDan Klein        """
376fc46f261SDan Klein
377fc46f261SDan Klein    setup_pidfile_fixtures(testcase)
378fc46f261SDan Klein
379fc46f261SDan Klein    for func_name in [
380fc46f261SDan Klein            'write_pid_to_pidfile',
381fc46f261SDan Klein            'remove_existing_pidfile',
382fc46f261SDan Klein            ]:
383fc46f261SDan Klein        func_patcher = mock.patch.object(lockfile.pidlockfile, func_name)
384fc46f261SDan Klein        func_patcher.start()
385fc46f261SDan Klein        testcase.addCleanup(func_patcher.stop)
386fc46f261SDan Klein
387fc46f261SDan Klein
388fc46f261SDan Kleinclass TimeoutPIDLockFile_TestCase(scaffold.TestCase):
389fc46f261SDan Klein    """ Test cases for ‘TimeoutPIDLockFile’ class. """
390fc46f261SDan Klein
391fc46f261SDan Klein    def setUp(self):
392fc46f261SDan Klein        """ Set up test fixtures. """
393fc46f261SDan Klein        super(TimeoutPIDLockFile_TestCase, self).setUp()
394fc46f261SDan Klein
395fc46f261SDan Klein        pidlockfile_scenarios = make_pidlockfile_scenarios()
396fc46f261SDan Klein        self.pidlockfile_scenario = pidlockfile_scenarios['simple']
397fc46f261SDan Klein        pidfile_path = self.pidlockfile_scenario['pidfile_path']
398fc46f261SDan Klein
399fc46f261SDan Klein        for func_name in ['__init__', 'acquire']:
400fc46f261SDan Klein            func_patcher = mock.patch.object(
401fc46f261SDan Klein                    lockfile.pidlockfile.PIDLockFile, func_name)
402fc46f261SDan Klein            func_patcher.start()
403fc46f261SDan Klein            self.addCleanup(func_patcher.stop)
404fc46f261SDan Klein
405fc46f261SDan Klein        self.scenario = {
406fc46f261SDan Klein                'pidfile_path': self.pidlockfile_scenario['pidfile_path'],
407fc46f261SDan Klein                'acquire_timeout': self.getUniqueInteger(),
408fc46f261SDan Klein                }
409fc46f261SDan Klein
410fc46f261SDan Klein        self.test_kwargs = dict(
411fc46f261SDan Klein                path=self.scenario['pidfile_path'],
412fc46f261SDan Klein                acquire_timeout=self.scenario['acquire_timeout'],
413fc46f261SDan Klein                )
414fc46f261SDan Klein        self.test_instance = daemon.pidfile.TimeoutPIDLockFile(
415fc46f261SDan Klein                **self.test_kwargs)
416fc46f261SDan Klein
417fc46f261SDan Klein    def test_inherits_from_pidlockfile(self):
418fc46f261SDan Klein        """ Should inherit from PIDLockFile. """
419fc46f261SDan Klein        instance = self.test_instance
420fc46f261SDan Klein        self.assertIsInstance(instance, lockfile.pidlockfile.PIDLockFile)
421fc46f261SDan Klein
422fc46f261SDan Klein    def test_init_has_expected_signature(self):
423fc46f261SDan Klein        """ Should have expected signature for ‘__init__’. """
424fc46f261SDan Klein        def test_func(self, path, acquire_timeout=None, *args, **kwargs): pass
425fc46f261SDan Klein        test_func.__name__ = str('__init__')
426fc46f261SDan Klein        self.assertFunctionSignatureMatch(
427fc46f261SDan Klein                test_func,
428fc46f261SDan Klein                daemon.pidfile.TimeoutPIDLockFile.__init__)
429fc46f261SDan Klein
430fc46f261SDan Klein    def test_has_specified_acquire_timeout(self):
431fc46f261SDan Klein        """ Should have specified ‘acquire_timeout’ value. """
432fc46f261SDan Klein        instance = self.test_instance
433fc46f261SDan Klein        expected_timeout = self.test_kwargs['acquire_timeout']
434fc46f261SDan Klein        self.assertEqual(expected_timeout, instance.acquire_timeout)
435fc46f261SDan Klein
436fc46f261SDan Klein    @mock.patch.object(
437fc46f261SDan Klein            lockfile.pidlockfile.PIDLockFile, "__init__",
438fc46f261SDan Klein            autospec=True)
439fc46f261SDan Klein    def test_calls_superclass_init(self, mock_init):
440fc46f261SDan Klein        """ Should call the superclass ‘__init__’. """
441fc46f261SDan Klein        expected_path = self.test_kwargs['path']
442fc46f261SDan Klein        instance = daemon.pidfile.TimeoutPIDLockFile(**self.test_kwargs)
443fc46f261SDan Klein        mock_init.assert_called_with(instance, expected_path)
444fc46f261SDan Klein
445fc46f261SDan Klein    @mock.patch.object(
446fc46f261SDan Klein            lockfile.pidlockfile.PIDLockFile, "acquire",
447fc46f261SDan Klein            autospec=True)
448fc46f261SDan Klein    def test_acquire_uses_specified_timeout(self, mock_func_acquire):
449fc46f261SDan Klein        """ Should call the superclass ‘acquire’ with specified timeout. """
450fc46f261SDan Klein        instance = self.test_instance
451fc46f261SDan Klein        test_timeout = self.getUniqueInteger()
452fc46f261SDan Klein        expected_timeout = test_timeout
453fc46f261SDan Klein        instance.acquire(test_timeout)
454fc46f261SDan Klein        mock_func_acquire.assert_called_with(instance, expected_timeout)
455fc46f261SDan Klein
456fc46f261SDan Klein    @mock.patch.object(
457fc46f261SDan Klein            lockfile.pidlockfile.PIDLockFile, "acquire",
458fc46f261SDan Klein            autospec=True)
459fc46f261SDan Klein    def test_acquire_uses_stored_timeout_by_default(self, mock_func_acquire):
460fc46f261SDan Klein        """ Should call superclass ‘acquire’ with stored timeout by default. """
461fc46f261SDan Klein        instance = self.test_instance
462fc46f261SDan Klein        test_timeout = self.test_kwargs['acquire_timeout']
463fc46f261SDan Klein        expected_timeout = test_timeout
464fc46f261SDan Klein        instance.acquire()
465fc46f261SDan Klein        mock_func_acquire.assert_called_with(instance, expected_timeout)
466fc46f261SDan Klein
467fc46f261SDan Klein
468fc46f261SDan Klein# Local variables:
469fc46f261SDan Klein# coding: utf-8
470fc46f261SDan Klein# mode: python
471fc46f261SDan Klein# End:
472fc46f261SDan Klein# vim: fileencoding=utf-8 filetype=python :
473