Mercurial > repos > melissacline > ucsc_xena_platform
comparison python-daemon-2.0.5/test/test_runner.py @ 33:7ceb967147c3
start xena with no gui
add library files
author | jingchunzhu <jingchunzhu@gmail.com> |
---|---|
date | Wed, 22 Jul 2015 13:24:44 -0700 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
32:63b1ba1e3424 | 33:7ceb967147c3 |
---|---|
1 # -*- coding: utf-8 -*- | |
2 # | |
3 # test/test_runner.py | |
4 # Part of ‘python-daemon’, an implementation of PEP 3143. | |
5 # | |
6 # Copyright © 2009–2015 Ben Finney <ben+python@benfinney.id.au> | |
7 # | |
8 # This is free software: you may copy, modify, and/or distribute this work | |
9 # under the terms of the Apache License, version 2.0 as published by the | |
10 # Apache Software Foundation. | |
11 # No warranty expressed or implied. See the file ‘LICENSE.ASF-2’ for details. | |
12 | |
13 """ Unit test for ‘runner’ module. | |
14 """ | |
15 | |
16 from __future__ import (absolute_import, unicode_literals) | |
17 | |
18 try: | |
19 # Python 3 standard library. | |
20 import builtins | |
21 except ImportError: | |
22 # Python 2 standard library. | |
23 import __builtin__ as builtins | |
24 import os | |
25 import os.path | |
26 import sys | |
27 import tempfile | |
28 import errno | |
29 import signal | |
30 import functools | |
31 | |
32 import lockfile | |
33 import mock | |
34 import testtools | |
35 | |
36 from . import scaffold | |
37 from .scaffold import (basestring, unicode) | |
38 from .test_pidfile import ( | |
39 FakeFileDescriptorStringIO, | |
40 setup_pidfile_fixtures, | |
41 make_pidlockfile_scenarios, | |
42 apply_lockfile_method_mocks, | |
43 ) | |
44 from .test_daemon import ( | |
45 setup_streams_fixtures, | |
46 ) | |
47 | |
48 import daemon.daemon | |
49 import daemon.runner | |
50 import daemon.pidfile | |
51 | |
52 | |
53 class ModuleExceptions_TestCase(scaffold.Exception_TestCase): | |
54 """ Test cases for module exception classes. """ | |
55 | |
56 scenarios = scaffold.make_exception_scenarios([ | |
57 ('daemon.runner.DaemonRunnerError', dict( | |
58 exc_type = daemon.runner.DaemonRunnerError, | |
59 min_args = 1, | |
60 types = [Exception], | |
61 )), | |
62 ('daemon.runner.DaemonRunnerInvalidActionError', dict( | |
63 exc_type = daemon.runner.DaemonRunnerInvalidActionError, | |
64 min_args = 1, | |
65 types = [daemon.runner.DaemonRunnerError, ValueError], | |
66 )), | |
67 ('daemon.runner.DaemonRunnerStartFailureError', dict( | |
68 exc_type = daemon.runner.DaemonRunnerStartFailureError, | |
69 min_args = 1, | |
70 types = [daemon.runner.DaemonRunnerError, RuntimeError], | |
71 )), | |
72 ('daemon.runner.DaemonRunnerStopFailureError', dict( | |
73 exc_type = daemon.runner.DaemonRunnerStopFailureError, | |
74 min_args = 1, | |
75 types = [daemon.runner.DaemonRunnerError, RuntimeError], | |
76 )), | |
77 ]) | |
78 | |
79 | |
80 def make_runner_scenarios(): | |
81 """ Make a collection of scenarios for testing `DaemonRunner` instances. | |
82 | |
83 :return: A collection of scenarios for tests involving | |
84 `DaemonRunner` instances. | |
85 | |
86 The collection is a mapping from scenario name to a dictionary of | |
87 scenario attributes. | |
88 | |
89 """ | |
90 | |
91 pidlockfile_scenarios = make_pidlockfile_scenarios() | |
92 | |
93 scenarios = { | |
94 'simple': { | |
95 'pidlockfile_scenario_name': 'simple', | |
96 }, | |
97 'pidfile-locked': { | |
98 'pidlockfile_scenario_name': 'exist-other-pid-locked', | |
99 }, | |
100 } | |
101 | |
102 for scenario in scenarios.values(): | |
103 if 'pidlockfile_scenario_name' in scenario: | |
104 pidlockfile_scenario = pidlockfile_scenarios.pop( | |
105 scenario['pidlockfile_scenario_name']) | |
106 scenario['pid'] = pidlockfile_scenario['pid'] | |
107 scenario['pidfile_path'] = pidlockfile_scenario['pidfile_path'] | |
108 scenario['pidfile_timeout'] = 23 | |
109 scenario['pidlockfile_scenario'] = pidlockfile_scenario | |
110 | |
111 return scenarios | |
112 | |
113 | |
114 def set_runner_scenario(testcase, scenario_name): | |
115 """ Set the DaemonRunner test scenario for the test case. | |
116 | |
117 :param testcase: The `TestCase` instance to decorate. | |
118 :param scenario_name: The name of the scenario to use. | |
119 | |
120 Set the `DaemonRunner` test scenario name and decorate the | |
121 `testcase` with the corresponding scenario fixtures. | |
122 | |
123 """ | |
124 scenarios = testcase.runner_scenarios | |
125 testcase.scenario = scenarios[scenario_name] | |
126 apply_lockfile_method_mocks( | |
127 testcase.mock_runner_lockfile, | |
128 testcase, | |
129 testcase.scenario['pidlockfile_scenario']) | |
130 | |
131 | |
132 def setup_runner_fixtures(testcase): | |
133 """ Set up common fixtures for `DaemonRunner` test cases. | |
134 | |
135 :param testcase: A `TestCase` instance to decorate. | |
136 | |
137 Decorate the `testcase` with attributes to be fixtures for tests | |
138 involving `DaemonRunner` instances. | |
139 | |
140 """ | |
141 setup_pidfile_fixtures(testcase) | |
142 setup_streams_fixtures(testcase) | |
143 | |
144 testcase.runner_scenarios = make_runner_scenarios() | |
145 | |
146 patcher_stderr = mock.patch.object( | |
147 sys, "stderr", | |
148 new=FakeFileDescriptorStringIO()) | |
149 testcase.fake_stderr = patcher_stderr.start() | |
150 testcase.addCleanup(patcher_stderr.stop) | |
151 | |
152 simple_scenario = testcase.runner_scenarios['simple'] | |
153 | |
154 testcase.mock_runner_lockfile = mock.MagicMock( | |
155 spec=daemon.pidfile.TimeoutPIDLockFile) | |
156 apply_lockfile_method_mocks( | |
157 testcase.mock_runner_lockfile, | |
158 testcase, | |
159 simple_scenario['pidlockfile_scenario']) | |
160 testcase.mock_runner_lockfile.path = simple_scenario['pidfile_path'] | |
161 | |
162 patcher_lockfile_class = mock.patch.object( | |
163 daemon.pidfile, "TimeoutPIDLockFile", | |
164 return_value=testcase.mock_runner_lockfile) | |
165 patcher_lockfile_class.start() | |
166 testcase.addCleanup(patcher_lockfile_class.stop) | |
167 | |
168 class TestApp(object): | |
169 | |
170 def __init__(self): | |
171 self.stdin_path = testcase.stream_file_paths['stdin'] | |
172 self.stdout_path = testcase.stream_file_paths['stdout'] | |
173 self.stderr_path = testcase.stream_file_paths['stderr'] | |
174 self.pidfile_path = simple_scenario['pidfile_path'] | |
175 self.pidfile_timeout = simple_scenario['pidfile_timeout'] | |
176 | |
177 run = mock.MagicMock(name="TestApp.run") | |
178 | |
179 testcase.TestApp = TestApp | |
180 | |
181 patcher_runner_daemoncontext = mock.patch.object( | |
182 daemon.runner, "DaemonContext", autospec=True) | |
183 patcher_runner_daemoncontext.start() | |
184 testcase.addCleanup(patcher_runner_daemoncontext.stop) | |
185 | |
186 testcase.test_app = testcase.TestApp() | |
187 | |
188 testcase.test_program_name = "bazprog" | |
189 testcase.test_program_path = os.path.join( | |
190 "/foo/bar", testcase.test_program_name) | |
191 testcase.valid_argv_params = { | |
192 'start': [testcase.test_program_path, 'start'], | |
193 'stop': [testcase.test_program_path, 'stop'], | |
194 'restart': [testcase.test_program_path, 'restart'], | |
195 } | |
196 | |
197 def fake_open(filename, mode=None, buffering=None): | |
198 if filename in testcase.stream_files_by_path: | |
199 result = testcase.stream_files_by_path[filename] | |
200 else: | |
201 result = FakeFileDescriptorStringIO() | |
202 result.mode = mode | |
203 result.buffering = buffering | |
204 return result | |
205 | |
206 mock_open = mock.mock_open() | |
207 mock_open.side_effect = fake_open | |
208 | |
209 func_patcher_builtin_open = mock.patch.object( | |
210 builtins, "open", | |
211 new=mock_open) | |
212 func_patcher_builtin_open.start() | |
213 testcase.addCleanup(func_patcher_builtin_open.stop) | |
214 | |
215 func_patcher_os_kill = mock.patch.object(os, "kill") | |
216 func_patcher_os_kill.start() | |
217 testcase.addCleanup(func_patcher_os_kill.stop) | |
218 | |
219 patcher_sys_argv = mock.patch.object( | |
220 sys, "argv", | |
221 new=testcase.valid_argv_params['start']) | |
222 patcher_sys_argv.start() | |
223 testcase.addCleanup(patcher_sys_argv.stop) | |
224 | |
225 testcase.test_instance = daemon.runner.DaemonRunner(testcase.test_app) | |
226 | |
227 testcase.scenario = NotImplemented | |
228 | |
229 | |
230 class DaemonRunner_BaseTestCase(scaffold.TestCase): | |
231 """ Base class for DaemonRunner test case classes. """ | |
232 | |
233 def setUp(self): | |
234 """ Set up test fixtures. """ | |
235 super(DaemonRunner_BaseTestCase, self).setUp() | |
236 | |
237 setup_runner_fixtures(self) | |
238 set_runner_scenario(self, 'simple') | |
239 | |
240 | |
241 class DaemonRunner_TestCase(DaemonRunner_BaseTestCase): | |
242 """ Test cases for DaemonRunner class. """ | |
243 | |
244 def setUp(self): | |
245 """ Set up test fixtures. """ | |
246 super(DaemonRunner_TestCase, self).setUp() | |
247 | |
248 func_patcher_parse_args = mock.patch.object( | |
249 daemon.runner.DaemonRunner, "parse_args") | |
250 func_patcher_parse_args.start() | |
251 self.addCleanup(func_patcher_parse_args.stop) | |
252 | |
253 # Create a new instance now with our custom patches. | |
254 self.test_instance = daemon.runner.DaemonRunner(self.test_app) | |
255 | |
256 def test_instantiate(self): | |
257 """ New instance of DaemonRunner should be created. """ | |
258 self.assertIsInstance(self.test_instance, daemon.runner.DaemonRunner) | |
259 | |
260 def test_parses_commandline_args(self): | |
261 """ Should parse commandline arguments. """ | |
262 self.test_instance.parse_args.assert_called_with() | |
263 | |
264 def test_has_specified_app(self): | |
265 """ Should have specified application object. """ | |
266 self.assertIs(self.test_app, self.test_instance.app) | |
267 | |
268 def test_sets_pidfile_none_when_pidfile_path_is_none(self): | |
269 """ Should set ‘pidfile’ to ‘None’ when ‘pidfile_path’ is ‘None’. """ | |
270 pidfile_path = None | |
271 self.test_app.pidfile_path = pidfile_path | |
272 expected_pidfile = None | |
273 instance = daemon.runner.DaemonRunner(self.test_app) | |
274 self.assertIs(expected_pidfile, instance.pidfile) | |
275 | |
276 def test_error_when_pidfile_path_not_string(self): | |
277 """ Should raise ValueError when PID file path not a string. """ | |
278 pidfile_path = object() | |
279 self.test_app.pidfile_path = pidfile_path | |
280 expected_error = ValueError | |
281 self.assertRaises( | |
282 expected_error, | |
283 daemon.runner.DaemonRunner, self.test_app) | |
284 | |
285 def test_error_when_pidfile_path_not_absolute(self): | |
286 """ Should raise ValueError when PID file path not absolute. """ | |
287 pidfile_path = "foo/bar.pid" | |
288 self.test_app.pidfile_path = pidfile_path | |
289 expected_error = ValueError | |
290 self.assertRaises( | |
291 expected_error, | |
292 daemon.runner.DaemonRunner, self.test_app) | |
293 | |
294 def test_creates_lock_with_specified_parameters(self): | |
295 """ Should create a TimeoutPIDLockFile with specified params. """ | |
296 pidfile_path = self.scenario['pidfile_path'] | |
297 pidfile_timeout = self.scenario['pidfile_timeout'] | |
298 daemon.pidfile.TimeoutPIDLockFile.assert_called_with( | |
299 pidfile_path, pidfile_timeout) | |
300 | |
301 def test_has_created_pidfile(self): | |
302 """ Should have new PID lock file as `pidfile` attribute. """ | |
303 expected_pidfile = self.mock_runner_lockfile | |
304 instance = self.test_instance | |
305 self.assertIs( | |
306 expected_pidfile, instance.pidfile) | |
307 | |
308 def test_daemon_context_has_created_pidfile(self): | |
309 """ DaemonContext component should have new PID lock file. """ | |
310 expected_pidfile = self.mock_runner_lockfile | |
311 daemon_context = self.test_instance.daemon_context | |
312 self.assertIs( | |
313 expected_pidfile, daemon_context.pidfile) | |
314 | |
315 def test_daemon_context_has_specified_stdin_stream(self): | |
316 """ DaemonContext component should have specified stdin file. """ | |
317 test_app = self.test_app | |
318 expected_file = self.stream_files_by_name['stdin'] | |
319 daemon_context = self.test_instance.daemon_context | |
320 self.assertEqual(expected_file, daemon_context.stdin) | |
321 | |
322 def test_daemon_context_has_stdin_in_read_mode(self): | |
323 """ DaemonContext component should open stdin file for read. """ | |
324 expected_mode = 'rt' | |
325 daemon_context = self.test_instance.daemon_context | |
326 self.assertIn(expected_mode, daemon_context.stdin.mode) | |
327 | |
328 def test_daemon_context_has_specified_stdout_stream(self): | |
329 """ DaemonContext component should have specified stdout file. """ | |
330 test_app = self.test_app | |
331 expected_file = self.stream_files_by_name['stdout'] | |
332 daemon_context = self.test_instance.daemon_context | |
333 self.assertEqual(expected_file, daemon_context.stdout) | |
334 | |
335 def test_daemon_context_has_stdout_in_append_mode(self): | |
336 """ DaemonContext component should open stdout file for append. """ | |
337 expected_mode = 'w+t' | |
338 daemon_context = self.test_instance.daemon_context | |
339 self.assertIn(expected_mode, daemon_context.stdout.mode) | |
340 | |
341 def test_daemon_context_has_specified_stderr_stream(self): | |
342 """ DaemonContext component should have specified stderr file. """ | |
343 test_app = self.test_app | |
344 expected_file = self.stream_files_by_name['stderr'] | |
345 daemon_context = self.test_instance.daemon_context | |
346 self.assertEqual(expected_file, daemon_context.stderr) | |
347 | |
348 def test_daemon_context_has_stderr_in_append_mode(self): | |
349 """ DaemonContext component should open stderr file for append. """ | |
350 expected_mode = 'w+t' | |
351 daemon_context = self.test_instance.daemon_context | |
352 self.assertIn(expected_mode, daemon_context.stderr.mode) | |
353 | |
354 def test_daemon_context_has_stderr_with_no_buffering(self): | |
355 """ DaemonContext component should open stderr file unbuffered. """ | |
356 expected_buffering = 0 | |
357 daemon_context = self.test_instance.daemon_context | |
358 self.assertEqual( | |
359 expected_buffering, daemon_context.stderr.buffering) | |
360 | |
361 | |
362 class DaemonRunner_usage_exit_TestCase(DaemonRunner_BaseTestCase): | |
363 """ Test cases for DaemonRunner.usage_exit method. """ | |
364 | |
365 def test_raises_system_exit(self): | |
366 """ Should raise SystemExit exception. """ | |
367 instance = self.test_instance | |
368 argv = [self.test_program_path] | |
369 self.assertRaises( | |
370 SystemExit, | |
371 instance._usage_exit, argv) | |
372 | |
373 def test_message_follows_conventional_format(self): | |
374 """ Should emit a conventional usage message. """ | |
375 instance = self.test_instance | |
376 argv = [self.test_program_path] | |
377 expected_stderr_output = """\ | |
378 usage: {progname} ... | |
379 """.format( | |
380 progname=self.test_program_name) | |
381 self.assertRaises( | |
382 SystemExit, | |
383 instance._usage_exit, argv) | |
384 self.assertOutputCheckerMatch( | |
385 expected_stderr_output, self.fake_stderr.getvalue()) | |
386 | |
387 | |
388 class DaemonRunner_parse_args_TestCase(DaemonRunner_BaseTestCase): | |
389 """ Test cases for DaemonRunner.parse_args method. """ | |
390 | |
391 def setUp(self): | |
392 """ Set up test fixtures. """ | |
393 super(DaemonRunner_parse_args_TestCase, self).setUp() | |
394 | |
395 func_patcher_usage_exit = mock.patch.object( | |
396 daemon.runner.DaemonRunner, "_usage_exit", | |
397 side_effect=NotImplementedError) | |
398 func_patcher_usage_exit.start() | |
399 self.addCleanup(func_patcher_usage_exit.stop) | |
400 | |
401 def test_emits_usage_message_if_insufficient_args(self): | |
402 """ Should emit a usage message and exit if too few arguments. """ | |
403 instance = self.test_instance | |
404 argv = [self.test_program_path] | |
405 exc = self.assertRaises( | |
406 NotImplementedError, | |
407 instance.parse_args, argv) | |
408 daemon.runner.DaemonRunner._usage_exit.assert_called_with(argv) | |
409 | |
410 def test_emits_usage_message_if_unknown_action_arg(self): | |
411 """ Should emit a usage message and exit if unknown action. """ | |
412 instance = self.test_instance | |
413 progname = self.test_program_name | |
414 argv = [self.test_program_path, 'bogus'] | |
415 exc = self.assertRaises( | |
416 NotImplementedError, | |
417 instance.parse_args, argv) | |
418 daemon.runner.DaemonRunner._usage_exit.assert_called_with(argv) | |
419 | |
420 def test_should_parse_system_argv_by_default(self): | |
421 """ Should parse sys.argv by default. """ | |
422 instance = self.test_instance | |
423 expected_action = 'start' | |
424 argv = self.valid_argv_params['start'] | |
425 with mock.patch.object(sys, "argv", new=argv): | |
426 instance.parse_args() | |
427 self.assertEqual(expected_action, instance.action) | |
428 | |
429 def test_sets_action_from_first_argument(self): | |
430 """ Should set action from first commandline argument. """ | |
431 instance = self.test_instance | |
432 for name, argv in self.valid_argv_params.items(): | |
433 expected_action = name | |
434 instance.parse_args(argv) | |
435 self.assertEqual(expected_action, instance.action) | |
436 | |
437 | |
438 try: | |
439 ProcessLookupError | |
440 except NameError: | |
441 # Python 2 uses OSError. | |
442 ProcessLookupError = functools.partial(OSError, errno.ESRCH) | |
443 | |
444 class DaemonRunner_do_action_TestCase(DaemonRunner_BaseTestCase): | |
445 """ Test cases for DaemonRunner.do_action method. """ | |
446 | |
447 def test_raises_error_if_unknown_action(self): | |
448 """ Should emit a usage message and exit if action is unknown. """ | |
449 instance = self.test_instance | |
450 instance.action = 'bogus' | |
451 expected_error = daemon.runner.DaemonRunnerInvalidActionError | |
452 self.assertRaises( | |
453 expected_error, | |
454 instance.do_action) | |
455 | |
456 | |
457 class DaemonRunner_do_action_start_TestCase(DaemonRunner_BaseTestCase): | |
458 """ Test cases for DaemonRunner.do_action method, action 'start'. """ | |
459 | |
460 def setUp(self): | |
461 """ Set up test fixtures. """ | |
462 super(DaemonRunner_do_action_start_TestCase, self).setUp() | |
463 | |
464 self.test_instance.action = 'start' | |
465 | |
466 def test_raises_error_if_pidfile_locked(self): | |
467 """ Should raise error if PID file is locked. """ | |
468 | |
469 instance = self.test_instance | |
470 instance.daemon_context.open.side_effect = lockfile.AlreadyLocked | |
471 pidfile_path = self.scenario['pidfile_path'] | |
472 expected_error = daemon.runner.DaemonRunnerStartFailureError | |
473 expected_message_content = pidfile_path | |
474 exc = self.assertRaises( | |
475 expected_error, | |
476 instance.do_action) | |
477 self.assertIn(expected_message_content, unicode(exc)) | |
478 | |
479 def test_breaks_lock_if_no_such_process(self): | |
480 """ Should request breaking lock if PID file process is not running. """ | |
481 set_runner_scenario(self, 'pidfile-locked') | |
482 instance = self.test_instance | |
483 self.mock_runner_lockfile.read_pid.return_value = ( | |
484 self.scenario['pidlockfile_scenario']['pidfile_pid']) | |
485 pidfile_path = self.scenario['pidfile_path'] | |
486 test_pid = self.scenario['pidlockfile_scenario']['pidfile_pid'] | |
487 expected_signal = signal.SIG_DFL | |
488 test_error = ProcessLookupError("Not running") | |
489 os.kill.side_effect = test_error | |
490 instance.do_action() | |
491 os.kill.assert_called_with(test_pid, expected_signal) | |
492 self.mock_runner_lockfile.break_lock.assert_called_with() | |
493 | |
494 def test_requests_daemon_context_open(self): | |
495 """ Should request the daemon context to open. """ | |
496 instance = self.test_instance | |
497 instance.do_action() | |
498 instance.daemon_context.open.assert_called_with() | |
499 | |
500 def test_emits_start_message_to_stderr(self): | |
501 """ Should emit start message to stderr. """ | |
502 instance = self.test_instance | |
503 expected_stderr = """\ | |
504 started with pid {pid:d} | |
505 """.format( | |
506 pid=self.scenario['pid']) | |
507 instance.do_action() | |
508 self.assertOutputCheckerMatch( | |
509 expected_stderr, self.fake_stderr.getvalue()) | |
510 | |
511 def test_requests_app_run(self): | |
512 """ Should request the application to run. """ | |
513 instance = self.test_instance | |
514 instance.do_action() | |
515 self.test_app.run.assert_called_with() | |
516 | |
517 | |
518 class DaemonRunner_do_action_stop_TestCase(DaemonRunner_BaseTestCase): | |
519 """ Test cases for DaemonRunner.do_action method, action 'stop'. """ | |
520 | |
521 def setUp(self): | |
522 """ Set up test fixtures. """ | |
523 super(DaemonRunner_do_action_stop_TestCase, self).setUp() | |
524 | |
525 set_runner_scenario(self, 'pidfile-locked') | |
526 | |
527 self.test_instance.action = 'stop' | |
528 | |
529 self.mock_runner_lockfile.is_locked.return_value = True | |
530 self.mock_runner_lockfile.i_am_locking.return_value = False | |
531 self.mock_runner_lockfile.read_pid.return_value = ( | |
532 self.scenario['pidlockfile_scenario']['pidfile_pid']) | |
533 | |
534 def test_raises_error_if_pidfile_not_locked(self): | |
535 """ Should raise error if PID file is not locked. """ | |
536 set_runner_scenario(self, 'simple') | |
537 instance = self.test_instance | |
538 self.mock_runner_lockfile.is_locked.return_value = False | |
539 self.mock_runner_lockfile.i_am_locking.return_value = False | |
540 self.mock_runner_lockfile.read_pid.return_value = ( | |
541 self.scenario['pidlockfile_scenario']['pidfile_pid']) | |
542 pidfile_path = self.scenario['pidfile_path'] | |
543 expected_error = daemon.runner.DaemonRunnerStopFailureError | |
544 expected_message_content = pidfile_path | |
545 exc = self.assertRaises( | |
546 expected_error, | |
547 instance.do_action) | |
548 self.assertIn(expected_message_content, unicode(exc)) | |
549 | |
550 def test_breaks_lock_if_pidfile_stale(self): | |
551 """ Should break lock if PID file is stale. """ | |
552 instance = self.test_instance | |
553 pidfile_path = self.scenario['pidfile_path'] | |
554 test_pid = self.scenario['pidlockfile_scenario']['pidfile_pid'] | |
555 expected_signal = signal.SIG_DFL | |
556 test_error = OSError(errno.ESRCH, "Not running") | |
557 os.kill.side_effect = test_error | |
558 instance.do_action() | |
559 self.mock_runner_lockfile.break_lock.assert_called_with() | |
560 | |
561 def test_sends_terminate_signal_to_process_from_pidfile(self): | |
562 """ Should send SIGTERM to the daemon process. """ | |
563 instance = self.test_instance | |
564 test_pid = self.scenario['pidlockfile_scenario']['pidfile_pid'] | |
565 expected_signal = signal.SIGTERM | |
566 instance.do_action() | |
567 os.kill.assert_called_with(test_pid, expected_signal) | |
568 | |
569 def test_raises_error_if_cannot_send_signal_to_process(self): | |
570 """ Should raise error if cannot send signal to daemon process. """ | |
571 instance = self.test_instance | |
572 test_pid = self.scenario['pidlockfile_scenario']['pidfile_pid'] | |
573 pidfile_path = self.scenario['pidfile_path'] | |
574 test_error = OSError(errno.EPERM, "Nice try") | |
575 os.kill.side_effect = test_error | |
576 expected_error = daemon.runner.DaemonRunnerStopFailureError | |
577 expected_message_content = unicode(test_pid) | |
578 exc = self.assertRaises( | |
579 expected_error, | |
580 instance.do_action) | |
581 self.assertIn(expected_message_content, unicode(exc)) | |
582 | |
583 | |
584 @mock.patch.object(daemon.runner.DaemonRunner, "_start") | |
585 @mock.patch.object(daemon.runner.DaemonRunner, "_stop") | |
586 class DaemonRunner_do_action_restart_TestCase(DaemonRunner_BaseTestCase): | |
587 """ Test cases for DaemonRunner.do_action method, action 'restart'. """ | |
588 | |
589 def setUp(self): | |
590 """ Set up test fixtures. """ | |
591 super(DaemonRunner_do_action_restart_TestCase, self).setUp() | |
592 | |
593 set_runner_scenario(self, 'pidfile-locked') | |
594 | |
595 self.test_instance.action = 'restart' | |
596 | |
597 def test_requests_stop_then_start( | |
598 self, | |
599 mock_func_daemonrunner_start, mock_func_daemonrunner_stop): | |
600 """ Should request stop, then start. """ | |
601 instance = self.test_instance | |
602 instance.do_action() | |
603 mock_func_daemonrunner_start.assert_called_with() | |
604 mock_func_daemonrunner_stop.assert_called_with() | |
605 | |
606 | |
607 @mock.patch.object(sys, "stderr") | |
608 class emit_message_TestCase(scaffold.TestCase): | |
609 """ Test cases for ‘emit_message’ function. """ | |
610 | |
611 def test_writes_specified_message_to_stream(self, mock_stderr): | |
612 """ Should write specified message to stream. """ | |
613 test_message = self.getUniqueString() | |
614 expected_content = "{message}\n".format(message=test_message) | |
615 daemon.runner.emit_message(test_message, stream=mock_stderr) | |
616 mock_stderr.write.assert_called_with(expected_content) | |
617 | |
618 def test_writes_to_specified_stream(self, mock_stderr): | |
619 """ Should write message to specified stream. """ | |
620 test_message = self.getUniqueString() | |
621 mock_stream = mock.MagicMock() | |
622 daemon.runner.emit_message(test_message, stream=mock_stream) | |
623 mock_stream.write.assert_called_with(mock.ANY) | |
624 | |
625 def test_writes_to_stderr_by_default(self, mock_stderr): | |
626 """ Should write message to ‘sys.stderr’ by default. """ | |
627 test_message = self.getUniqueString() | |
628 daemon.runner.emit_message(test_message) | |
629 mock_stderr.write.assert_called_with(mock.ANY) | |
630 | |
631 | |
632 class is_pidfile_stale_TestCase(scaffold.TestCase): | |
633 """ Test cases for ‘is_pidfile_stale’ function. """ | |
634 | |
635 def setUp(self): | |
636 """ Set up test fixtures. """ | |
637 super(is_pidfile_stale_TestCase, self).setUp() | |
638 | |
639 func_patcher_os_kill = mock.patch.object(os, "kill") | |
640 func_patcher_os_kill.start() | |
641 self.addCleanup(func_patcher_os_kill.stop) | |
642 os.kill.return_value = None | |
643 | |
644 self.test_pid = self.getUniqueInteger() | |
645 self.test_pidfile = mock.MagicMock(daemon.pidfile.TimeoutPIDLockFile) | |
646 self.test_pidfile.read_pid.return_value = self.test_pid | |
647 | |
648 def test_returns_false_if_no_pid_in_file(self): | |
649 """ Should return False if the pidfile contains no PID. """ | |
650 self.test_pidfile.read_pid.return_value = None | |
651 expected_result = False | |
652 result = daemon.runner.is_pidfile_stale(self.test_pidfile) | |
653 self.assertEqual(expected_result, result) | |
654 | |
655 def test_returns_false_if_process_exists(self): | |
656 """ Should return False if the process with its PID exists. """ | |
657 expected_result = False | |
658 result = daemon.runner.is_pidfile_stale(self.test_pidfile) | |
659 self.assertEqual(expected_result, result) | |
660 | |
661 def test_returns_true_if_process_does_not_exist(self): | |
662 """ Should return True if the process does not exist. """ | |
663 test_error = ProcessLookupError("No such process") | |
664 del os.kill.return_value | |
665 os.kill.side_effect = test_error | |
666 expected_result = True | |
667 result = daemon.runner.is_pidfile_stale(self.test_pidfile) | |
668 self.assertEqual(expected_result, result) | |
669 | |
670 | |
671 # Local variables: | |
672 # coding: utf-8 | |
673 # mode: python | |
674 # End: | |
675 # vim: fileencoding=utf-8 filetype=python : |