Signature files are not input to the command; remove the attempt to match.
[dput.git] / test / test_dput_main.py
bloba1b1d48a5da0ef3fe5b27212f1cf28094ce9cb83
1 # -*- coding: utf-8; -*-
3 # test/test_dput_main.py
4 # Part of ‘dput’, a Debian package upload toolkit.
6 # This is free software, and you are welcome to redistribute it under
7 # certain conditions; see the end of this file for copyright
8 # information, grant of license, and disclaimer of warranty.
10 """ Unit tests for ‘dput’ module `main` function. """
12 from __future__ import (absolute_import, unicode_literals)
14 import doctest
15 import os
16 import sys
17 import tempfile
18 import textwrap
20 import pkg_resources
21 import testscenarios
22 import testtools
23 import testtools.matchers
25 import dput.dput
26 from dput.helper import dputhelper
28 from .helper import (
29 EXIT_STATUS_FAILURE, EXIT_STATUS_SUCCESS,
30 FakeSystemExit,
31 PasswdEntry,
32 mock,
33 patch_os_environ,
34 patch_os_getuid,
35 patch_pwd_getpwuid,
36 patch_sys_argv,
37 patch_system_interfaces,
39 from .test_changesfile import (
40 make_changes_file_path,
41 set_changes_file_scenario,
42 setup_changes_file_fixtures,
44 from .test_configfile import (
45 patch_runtime_config_options,
46 set_config,
48 from .test_dputhelper import (
49 patch_getopt,
50 patch_pkg_resources_get_distribution,
54 def patch_parse_changes(testcase):
55 """ Patch the `parse_changes` function for the test case. """
56 func_patcher = mock.patch.object(
57 dput.dput, "parse_changes", autospec=True)
58 func_patcher.start()
59 testcase.addCleanup(func_patcher.stop)
62 def patch_read_configs(testcase):
63 """ Patch the `read_configs` function for the test case. """
64 def fake_read_configs(*args, **kwargs):
65 return testcase.runtime_config_parser
67 func_patcher = mock.patch.object(
68 dput.dput, "read_configs", autospec=True,
69 side_effect=fake_read_configs)
70 func_patcher.start()
71 testcase.addCleanup(func_patcher.stop)
74 def patch_print_config(testcase):
75 """ Patch the `print_config` function for the test case. """
76 func_patcher = mock.patch.object(
77 dput.dput, "print_config", autospec=True)
78 func_patcher.start()
79 testcase.addCleanup(func_patcher.stop)
82 def set_upload_methods(testcase):
83 """ Set the `upload_methods` value for the test case. """
84 if not hasattr(testcase, 'upload_methods'):
85 upload_methods = dput.dput.import_upload_functions()
86 testcase.upload_methods = {
87 name: mock.MagicMock(upload_methods[name])
88 for name in upload_methods}
91 def patch_import_upload_functions(testcase):
92 """ Patch the `import_upload_functions` function for the test case. """
93 func_patcher = mock.patch.object(
94 dput.dput, "import_upload_functions", autospec=True,
95 return_value=testcase.upload_methods)
96 func_patcher.start()
97 testcase.addCleanup(func_patcher.stop)
100 class make_usage_message_TestCase(testtools.TestCase):
101 """ Test cases for `make_usage_message` function. """
103 def test_returns_text_with_program_name(self):
104 """ Should return text with expected program name. """
105 result = dput.dput.make_usage_message()
106 expected_result = textwrap.dedent("""\
107 Usage: dput ...
109 """)
110 self.expectThat(
111 result,
112 testtools.matchers.DocTestMatches(
113 expected_result, flags=doctest.ELLIPSIS))
116 class main_TestCase(
117 testtools.TestCase):
118 """ Base for test cases for `main` function. """
120 function_to_test = staticmethod(dput.dput.main)
122 def setUp(self):
123 """ Set up test fixtures. """
124 super(main_TestCase, self).setUp()
125 patch_system_interfaces(self)
127 set_config(
128 self,
129 getattr(self, 'config_scenario_name', 'exist-simple'))
130 patch_runtime_config_options(self)
132 setup_changes_file_fixtures(self)
133 set_changes_file_scenario(self, 'exist-minimal')
135 self.patch_os_isatty()
136 patch_os_environ(self)
137 patch_os_getuid(self)
138 patch_pwd_getpwuid(self)
139 patch_sys_argv(self)
141 self.set_files_to_upload()
142 set_upload_methods(self)
144 self.set_test_args()
146 self.patch_make_usage_message()
147 self.patch_distribution()
148 self.patch_getopt()
149 patch_read_configs(self)
150 patch_print_config(self)
151 patch_import_upload_functions(self)
152 self.patch_guess_upload_host()
153 self.patch_check_upload_logfile()
154 self.patch_verify_files()
155 self.patch_run_lintian_test()
156 self.patch_execute_command()
157 self.patch_create_upload_file()
158 self.patch_dinstall_caller()
160 def patch_os_isatty(self):
161 """ Patch the `os.isatty` function. """
162 if not hasattr(self, 'os_isatty_return_value'):
163 self.os_isatty_return_value = False
164 func_patcher = mock.patch.object(
165 os, "isatty", autospec=True,
166 return_value=self.os_isatty_return_value)
167 func_patcher.start()
168 self.addCleanup(func_patcher.stop)
170 def set_files_to_upload(self):
171 """ Set the `files_to_upload` collection for this instance. """
172 if not hasattr(self, 'files_to_upload'):
173 self.files_to_upload = []
175 def set_test_args(self):
176 """ Set the arguments for the test call to the function. """
177 self.test_args = dict()
179 def patch_make_usage_message(self):
180 """ Patch the `make_usage_message` function. """
181 if not hasattr(self, 'dput_usage_message'):
182 self.dput_usage_message = self.getUniqueString()
183 func_patcher = mock.patch.object(
184 dput.dput, "make_usage_message", autospec=True,
185 return_value=self.dput_usage_message)
186 func_patcher.start()
187 self.addCleanup(func_patcher.stop)
189 def patch_distribution(self):
190 """ Patch the Python distribution for this test case. """
191 self.fake_distribution = mock.MagicMock(pkg_resources.Distribution)
192 if hasattr(self, 'dput_version'):
193 self.fake_distribution.version = self.dput_version
194 patch_pkg_resources_get_distribution(self)
196 def patch_getopt(self):
197 """ Patch the `dputhelper.getopt` function. """
198 if not hasattr(self, 'getopt_opts'):
199 self.getopt_opts = []
200 if not hasattr(self, 'getopt_args'):
201 self.getopt_args = [self.changes_file_double.path]
203 patch_getopt(self)
205 def patch_guess_upload_host(self):
206 """ Patch the `guess_upload_host` function. """
207 if not hasattr(self, 'guess_upload_host_return_value'):
208 self.guess_upload_host_return_value = self.test_host
209 func_patcher = mock.patch.object(
210 dput.dput, "guess_upload_host", autospec=True,
211 return_value=self.guess_upload_host_return_value)
212 func_patcher.start()
213 self.addCleanup(func_patcher.stop)
215 def patch_check_upload_logfile(self):
216 """ Patch the `check_upload_logfile` function. """
217 func_patcher = mock.patch.object(
218 dput.dput, "check_upload_logfile", autospec=True)
219 func_patcher.start()
220 self.addCleanup(func_patcher.stop)
222 def patch_verify_files(self):
223 """ Patch the `verify_files` function. """
224 func_patcher = mock.patch.object(
225 dput.dput, "verify_files", autospec=True,
226 return_value=self.files_to_upload)
227 func_patcher.start()
228 self.addCleanup(func_patcher.stop)
230 def patch_run_lintian_test(self):
231 """ Patch the `run_lintian_test` function. """
232 func_patcher = mock.patch.object(
233 dput.dput, "run_lintian_test", autospec=True)
234 func_patcher.start()
235 self.addCleanup(func_patcher.stop)
237 def patch_execute_command(self):
238 """ Patch the `execute_command` function. """
239 func_patcher = mock.patch.object(
240 dput.dput, "execute_command", autospec=True)
241 func_patcher.start()
242 self.addCleanup(func_patcher.stop)
244 def patch_create_upload_file(self):
245 """ Patch the `create_upload_file` function. """
246 func_patcher = mock.patch.object(
247 dput.dput, "create_upload_file", autospec=True)
248 func_patcher.start()
249 self.addCleanup(func_patcher.stop)
251 def patch_dinstall_caller(self):
252 """ Patch the `dinstall_caller` function. """
253 func_patcher = mock.patch.object(
254 dput.dput, "dinstall_caller", autospec=True)
255 func_patcher.start()
256 self.addCleanup(func_patcher.stop)
259 class main_CommandLineProcessingTestCase(main_TestCase):
260 """ Test cases for `main` command-line processing. """
262 sys_argv = ["lorem"] + [
263 make_changes_file_path(tempfile.mktemp())
264 for __ in range(3)]
266 def test_calls_getopt_with_expected_args(self):
267 """ Should call `getopt` with expected arguments. """
268 self.function_to_test(**self.test_args)
269 dputhelper.getopt.assert_called_with(
270 self.sys_argv[1:], mock.ANY, mock.ANY)
272 def test_emits_error_when_invalid_delayed_value(self):
273 """ Should emit error message when ‘--delayed’ invalid. """
274 self.getopt_opts = [("--delayed", "b0gUs")]
275 try:
276 self.function_to_test(**self.test_args)
277 except FakeSystemExit:
278 pass
279 expected_output = textwrap.dedent("""\
280 Incorrect delayed argument, ...
281 """)
282 self.assertThat(
283 sys.stdout.getvalue(),
284 testtools.matchers.DocTestMatches(
285 expected_output, doctest.ELLIPSIS))
287 def test_calls_sys_exit_when_invalid_delayed_value(self):
288 """ Should call `sys.exit` when ‘--delayed’ invalid. """
289 self.getopt_opts = [("--delayed", "b0gUs")]
290 with testtools.ExpectedException(FakeSystemExit):
291 self.function_to_test(**self.test_args)
292 sys.exit.assert_called_with(EXIT_STATUS_FAILURE)
294 def test_emits_error_when_no_nonoption_args(self):
295 """ Should emit error message when no non-option args. """
296 self.getopt_args = []
297 try:
298 self.function_to_test(**self.test_args)
299 except FakeSystemExit:
300 pass
301 expected_output = textwrap.dedent("""\
303 No package or host has been provided, ...
304 """)
305 self.assertThat(
306 sys.stdout.getvalue(),
307 testtools.matchers.DocTestMatches(
308 expected_output, doctest.ELLIPSIS))
310 def test_calls_sys_exit_when_no_nonoption_args(self):
311 """ Should call `sys.exit` when no non-option args. """
312 self.getopt_args = []
313 with testtools.ExpectedException(FakeSystemExit):
314 self.function_to_test(**self.test_args)
315 sys.exit.assert_called_with(EXIT_STATUS_SUCCESS)
317 @mock.patch.object(dput.dput, 'debug', new=object())
318 def test_sets_debug_flag_when_debug_option(self):
319 """ Should set `debug` when ‘--debug’ option. """
320 self.getopt_opts = [("--debug", None)]
321 self.function_to_test(**self.test_args)
322 self.assertEqual(dput.dput.debug, True)
325 class main_DebugMessageTestCase(main_TestCase):
326 """ Test cases for `main` debug messages. """
328 progname = "lorem"
329 dput_version = "ipsum"
331 @mock.patch.object(dput.dput, 'debug', new=True)
332 def test_emits_debug_message_with_dput_version(self):
333 """ Should emit debug message showing Dput version string. """
334 self.getopts_opts = [("--version", None)]
335 self.function_to_test(**self.test_args)
336 expected_progname = self.progname
337 expected_version = self.fake_distribution.version
338 expected_output = textwrap.dedent("""\
339 D: {progname} {version}
340 """).format(
341 progname=expected_progname,
342 version=expected_version)
343 self.assertIn(expected_output, sys.stdout.getvalue())
345 @mock.patch.object(dput.dput, 'debug', new=True)
346 def test_emits_debug_message_for_discovered_methods(self):
347 """ Should emit debug message for discovered upload methods. """
348 self.function_to_test(**self.test_args)
349 expected_output = textwrap.dedent("""\
350 D: Default Method: {default_method}
351 D: Host Method: {host_method}
352 """).format(
353 default_method=self.runtime_config_parser.get(
354 'DEFAULT', 'method'),
355 host_method=self.runtime_config_parser.get(
356 self.test_host, 'method'))
357 self.assertIn(expected_output, sys.stdout.getvalue())
360 class main_DefaultFunctionCallTestCase(main_TestCase):
361 """ Test cases for `main` defaults calling other functions. """
363 def test_calls_import_upload_functions_with_expected_args(self):
364 """ Should call `import_upload_functions` with expected arguments. """
365 self.function_to_test(**self.test_args)
366 dput.dput.import_upload_functions.assert_called_with()
369 class main_ConfigFileTestCase(
370 testscenarios.WithScenarios,
371 main_TestCase):
372 """ Test cases for `main` specification of configuration file. """
374 scenarios = [
375 ('default', {
376 'expected_args': ("", mock.ANY),
378 ('config-from-command-line', {
379 'getopt_opts': [("--config", "lorem.conf")],
380 'expected_args': ("lorem.conf", mock.ANY),
384 def test_calls_read_configs_with_expected_args(self):
385 """ Should call `read_configs` with expected arguments. """
386 self.function_to_test(**self.test_args)
387 dput.dput.read_configs.assert_called_with(*self.expected_args)
390 class main_PrintAndEndTestCase(main_TestCase):
391 """ Test cases for `main` that print and end. """
393 progname = "lorem"
394 dput_version = "ipsum"
395 dput_usage_message = "Lorem ipsum, dolor sit amet."
397 def test_emit_usage_message_when_option_help(self):
398 """ Should emit usage message when ‘--help’ option. """
399 self.getopt_opts = [("--help", None)]
400 self.function_to_test(**self.test_args)
401 expected_output = self.dput_usage_message
402 self.assertIn(expected_output, sys.stdout.getvalue())
404 def test_emit_version_message_when_option_help(self):
405 """ Should emit usage message when ‘--version’ option. """
406 self.getopt_opts = [("--version", None)]
407 self.function_to_test(**self.test_args)
408 expected_progname = self.progname
409 expected_version = self.fake_distribution.version
410 expected_output = "{progname} {version}".format(
411 progname=expected_progname, version=expected_version)
412 self.assertIn(expected_output, sys.stdout.getvalue())
414 def test_print_config_when_option_print(self):
415 """ Should call `print_config` when ‘--print’. """
416 self.getopt_opts = [("--print", None)]
417 try:
418 self.function_to_test(**self.test_args)
419 except FakeSystemExit:
420 pass
421 dput.dput.print_config.assert_called_with(
422 self.runtime_config_parser, dput.dput.debug)
424 def test_print_config_then_sys_exit_when_option_print(self):
425 """ Should call `print_config`, then `sys.exit`, when ‘--print’. """
426 self.getopt_opts = [("--print", None)]
427 with testtools.ExpectedException(FakeSystemExit):
428 self.function_to_test(**self.test_args)
429 sys.exit.assert_called_with(EXIT_STATUS_SUCCESS)
431 def test_calls_sys_exit_when_option_host_list(self):
432 """ Should call `sys.exit` when option ‘--host-list’. """
433 self.getopt_opts = [("--host-list", None)]
434 with testtools.ExpectedException(FakeSystemExit):
435 self.function_to_test(**self.test_args)
436 sys.exit.assert_called_with(EXIT_STATUS_SUCCESS)
439 class main_DiscoverLoginTestCase(
440 testscenarios.WithScenarios,
441 main_TestCase):
442 """ Test cases for `main` discovery of login name. """
444 fallback_login_scenarios = [
445 ('login-from-environ', {
446 'os_environ': {
447 'USER': "login-from-environ",
449 'expected_fallback_login': "login-from-environ",
451 ('login-from-pwd', {
452 'pwd_getpwuid_return_value': PasswdEntry(
453 *(["login-from-pwd"] + [object()] * 6)),
454 'expected_fallback_login': "login-from-pwd",
458 config_login_scenarios = [
459 ('config-default-login', {
460 'config_extras': {
461 'default': {
462 'login': "login-from-config-default",
465 'expected_login': "login-from-config-default",
466 'expected_output_template':
467 "D: Login {login} from section {host} used",
469 ('config-host-login', {
470 'config_extras': {
471 'host': {
472 'login': "login-from-config-host",
475 'expected_login': "login-from-config-host",
476 'expected_output_template':
477 "D: Login {login} from section {host} used",
479 ('config-default-login sentinel', {
480 'config_extras': {
481 'default': {
482 'login': "username",
485 'expected_output_template':
486 "D: Neither host {host} nor default login used.",
488 ('config-host-login sentinel', {
489 'config_extras': {
490 'host': {
491 'login': "username",
494 'expected_output_template':
495 "D: Neither host {host} nor default login used.",
499 scenarios = testscenarios.multiply_scenarios(
500 fallback_login_scenarios, config_login_scenarios)
501 for (scenario_name, scenario) in scenarios:
502 if 'expected_login' not in scenario:
503 scenario['expected_login'] = scenario['expected_fallback_login']
504 del scenario_name, scenario
506 @mock.patch.object(dput.dput, 'debug', new=True)
507 def test_emits_debug_message_for_fallback_login(self):
508 """ Should emit a debug message for the fallback login. """
509 self.function_to_test(**self.test_args)
510 expected_output = textwrap.dedent("""\
511 D: Login: {fallback_login}
512 """).format(fallback_login=self.expected_fallback_login)
513 self.assertIn(expected_output, sys.stdout.getvalue())
515 @mock.patch.object(dput.dput, 'debug', new=True)
516 def test_emits_debug_message_for_discovered_login(self):
517 """ Should emit a debug message for the discovered login. """
518 self.function_to_test(**self.test_args)
519 expected_output = self.expected_output_template.format(
520 login=self.expected_login, host=self.test_host)
521 self.assertIn(expected_output, sys.stdout.getvalue())
523 def test_calls_upload_method_with_expected_login(self):
524 """ Should call upload method function with expected login arg. """
525 expected_method_name = self.runtime_config_parser.get(
526 self.test_host, 'method')
527 expected_method_func = self.upload_methods[expected_method_name]
528 self.function_to_test(**self.test_args)
529 expected_method_func.assert_called_with(
530 mock.ANY, self.expected_login,
531 mock.ANY, mock.ANY, mock.ANY, mock.ANY,
532 progress=mock.ANY, port=mock.ANY)
535 class main_HostListTestCase(
536 testscenarios.WithScenarios,
537 main_TestCase):
538 """ Test cases for `main` with ‘--host-list’ option. """
540 scenarios = [
541 ('default', {}),
542 ('config-distribution-one', {
543 'config_scenario_name': "exist-distribution-one",
545 ('config-distribution-three', {
546 'config_scenario_name': "exist-distribution-three",
550 def test_iterate_config_sections_when_option_host_list(self):
551 """ Should iteratively emit config sections when ‘--host-list’. """
552 self.getopt_opts = [("--host-list", None)]
553 try:
554 self.function_to_test(**self.test_args)
555 except FakeSystemExit:
556 pass
557 expected_output_lines = ["..."] + [
558 "{section} => {fqdn} (Upload method: {method}...)".format(
559 section=section_name,
560 fqdn=self.runtime_config_parser.get(section_name, 'fqdn'),
561 method=self.runtime_config_parser.get(
562 section_name, 'method'),
564 for section_name in self.runtime_config_parser.sections()
565 ] + ["..."]
566 expected_output = "\n".join(expected_output_lines)
567 self.assertThat(
568 sys.stdout.getvalue(),
569 testtools.matchers.DocTestMatches(
570 expected_output, doctest.ELLIPSIS))
573 class main_NamedHostTestCase(
574 testscenarios.WithScenarios,
575 main_TestCase):
576 """ Test cases for `main` function, named host processing. """
578 scenarios = [
579 ('host-from-command-line', {
580 'config_scenario_name': "exist-simple-host-three",
581 'getopt_args': ["bar", "lorem.changes", "ipsum.changes"],
582 'expected_host': "bar",
583 'expected_debug_output': "D: Host bar found in config",
585 ('host-from-command-line check-only', {
586 'config_scenario_name': "exist-simple-host-three",
587 'getopt_opts': [("--check-only", None)],
588 'getopt_args': ["bar", "lorem.changes", "ipsum.changes"],
589 'expected_host': "bar",
590 'expected_debug_output': "D: Host bar found in config",
592 ('host-from-command-line force-upload', {
593 'config_scenario_name': "exist-simple-host-three",
594 'getopt_opts': [("--force", None)],
595 'getopt_args': ["bar", "lorem.changes", "ipsum.changes"],
596 'expected_host': "bar",
597 'expected_debug_output': "D: Host bar found in config",
599 ('only-changes-filenames', {
600 'config_scenario_name': "exist-simple-host-three",
601 'getopt_args': ["lorem.changes", "ipsum.changes"],
602 'expected_host': "foo",
603 'expected_debug_output': "D: No host named on command line.",
607 @mock.patch.object(dput.dput, 'debug', new=True)
608 def test_emits_expected_debug_message(self):
609 """ Should emit expected debug message. """
610 try:
611 self.function_to_test(**self.test_args)
612 except FakeSystemExit:
613 pass
614 expected_output = self.expected_debug_output.format(
615 host=self.test_host)
616 self.assertIn(expected_output, sys.stdout.getvalue())
618 def test_check_upload_logfile_called_with_expected_host(self):
619 """ Should call `check_upload_logfile` with expected host. """
620 try:
621 self.function_to_test(**self.test_args)
622 except FakeSystemExit:
623 pass
624 expected_fqdn = self.runtime_config_parser.get(self.test_host, 'fqdn')
625 expected_args = (
626 mock.ANY, self.expected_host, expected_fqdn,
627 mock.ANY, mock.ANY, mock.ANY, mock.ANY)
628 dput.dput.check_upload_logfile.assert_called_with(*expected_args)
630 def test_verify_files_called_with_expected_host(self):
631 """ Should call `verify_files` with expected host. """
632 try:
633 self.function_to_test(**self.test_args)
634 except FakeSystemExit:
635 pass
636 expected_args = (
637 mock.ANY, mock.ANY, self.expected_host,
638 mock.ANY, mock.ANY, mock.ANY, mock.ANY, mock.ANY)
639 dput.dput.verify_files.assert_called_with(*expected_args)
642 class main_NamedHostNotInConfigTestCase(
643 testscenarios.WithScenarios,
644 main_TestCase):
645 """ Test cases for `main` function, named host not in config. """
647 scenarios = [
648 ('host-from-command-line', {
649 'config_scenario_name': "exist-simple-host-three",
650 'getopt_args': ["b0gUs", "lorem.changes", "ipsum.changes"],
651 'expected_stderr_output': "No host b0gUs found in config",
652 'expected_exception': FakeSystemExit,
653 'expected_exit_status': EXIT_STATUS_FAILURE,
655 ('host-from-command-line gluck_delayed', {
656 'config_scenario_name': "exist-simple-host-three",
657 'getopt_args': [
658 "gluck_delayed", "lorem.changes", "ipsum.changes"],
659 'expected_stderr_output': textwrap.dedent("""\
660 No host gluck_delayed found in config
662 The delayed upload queue has been moved back to
663 ftp-master (aka ftp.upload.debian.org).
664 """),
665 'expected_exception': FakeSystemExit,
666 'expected_exit_status': EXIT_STATUS_FAILURE,
668 ('host-from-command-line check-only', {
669 'config_scenario_name': "exist-simple-host-three",
670 'getopt_opts': [("--check-only", None)],
671 'getopt_args': ["b0gUs", "lorem.changes", "ipsum.changes"],
672 'expected_stdout_output': "D: No host b0gUs found in config",
674 ('only-changes-filenames check-only', {
675 'config_scenario_name': "exist-simple-host-three",
676 'getopt_opts': [("--check-only", None)],
677 'getopt_args': ["lorem.changes", "ipsum.changes"],
678 'expected_stdout_output':
679 "No host lorem.changes found in config",
683 @mock.patch.object(dput.dput, 'debug', new=True)
684 def test_emits_expected_debug_message(self):
685 """ Should emit expected debug message. """
686 try:
687 self.function_to_test(**self.test_args)
688 except FakeSystemExit:
689 pass
690 if hasattr(self, 'expected_stdout_output'):
691 self.assertIn(self.expected_stdout_output, sys.stdout.getvalue())
692 if hasattr(self, 'expected_stderr_output'):
693 self.assertIn(self.expected_stderr_output, sys.stderr.getvalue())
695 def test_calls_sys_exit_when_host_not_in_config(self):
696 """ Should call `sys.exit` when named host not in config. """
697 if not hasattr(self, 'expected_exception'):
698 self.skipTest("No expected exception for this scenario")
699 with testtools.ExpectedException(self.expected_exception):
700 self.function_to_test(**self.test_args)
701 sys.exit.assert_called_with(self.expected_exit_status)
704 class main_check_upload_logfile_CallTestCase(
705 testscenarios.WithScenarios,
706 main_TestCase):
707 """ Test cases for `main` function calling `check_upload_logfile`. """
709 scenarios = [
710 ('default', {}),
711 ('command-line-option-check-only', {
712 'getopt_opts': [("--check-only", None)],
713 'expected_check_only': True,
715 ('command-line-option-lintian', {
716 'getopt_opts': [("--lintian", None)],
717 'expected_lintian': True,
719 ('command-line-option-force', {
720 'getopt_opts': [("--force", None)],
721 'expected_force_upload': True,
723 ('command-line-options-three', {
724 'getopt_opts': [
725 ("--force", None),
726 ("--lintian", None),
727 ("--check-only", None),
729 'expected_check_only': True,
730 'expected_lintian': True,
731 'expected_force_upload': True,
735 def test_calls_check_upload_logfile_with_expected_args(self):
736 """ Should invoke `check_upload_logfile` with expected args. """
737 self.function_to_test(**self.test_args)
738 expected_args = (
739 self.changes_file_double.path, self.test_host,
740 self.runtime_config_parser.get(self.test_host, 'fqdn'),
741 getattr(self, 'expected_check_only', False),
742 getattr(self, 'expected_lintian', False),
743 getattr(self, 'expected_force_upload', False),
744 mock.ANY)
745 dput.dput.check_upload_logfile.assert_called_with(*expected_args)
748 class main_verify_files_CallTestCase(
749 testscenarios.WithScenarios,
750 main_TestCase):
751 """ Test cases for `main` function calling `verify_files`. """
753 scenarios = [
754 ('default', {}),
755 ('command-line-option-check-only', {
756 'getopt_opts': [("--check-only", None)],
757 'expected_check_only': True,
759 ('command-line-option-check-version', {
760 'getopt_opts': [("--check_version", None)],
761 'expected_check_version': True,
763 ('command-line-option-unchecked', {
764 'getopt_opts': [("--unchecked", None)],
765 'expected_unsigned_upload': True,
767 ('command-line-options-three', {
768 'getopt_opts': [
769 ("--check-only", None),
770 ("--check_version", None),
771 ("--unchecked", None),
773 'expected_check_only': True,
774 'expected_check_version': True,
775 'expected_unsigned_upload': True,
779 def test_calls_verify_files_with_expected_args(self):
780 """ Should invoke `verify_files` with expected args. """
781 self.function_to_test(**self.test_args)
782 expected_args = (
783 os.path.dirname(self.changes_file_double.path),
784 os.path.basename(self.changes_file_double.path),
785 self.test_host,
786 self.runtime_config_parser,
787 getattr(self, 'expected_check_only', False),
788 getattr(self, 'expected_check_version', False),
789 getattr(self, 'expected_unsigned_upload', False),
790 mock.ANY)
791 dput.dput.verify_files.assert_called_with(*expected_args)
794 class main_run_lintian_test_CallTestCase(
795 testscenarios.WithScenarios,
796 main_TestCase):
797 """ Test cases for `main` function calling `run_lintian_test`. """
799 scenarios = [
800 ('option-from-command-line', {
801 'getopt_opts': [("--lintian", None)],
803 ('option-from-config', {
804 'config_run_lintian': True,
808 def test_calls_run_lintian_test_with_expected_args(self):
809 """ Should invoke `run_lintian_test` with expected args. """
810 self.function_to_test(**self.test_args)
811 expected_args = (self.changes_file_double.path,)
812 dput.dput.run_lintian_test.assert_called_with(*expected_args)
815 class main_run_lintian_test_NoCallTestCase(
816 testscenarios.WithScenarios,
817 main_TestCase):
818 """ Test cases for `main` function omitting `run_lintian_test`. """
820 def test_emits_warning_message_when_check_only(self):
821 """ Should emit warning message when `--check-only`. """
822 self.getopt_opts = [("--check-only", None)]
823 self.function_to_test(**self.test_args)
824 expected_output = (
825 "Warning: The option -o does not automatically include \n"
826 "a lintian run any more. Please use the option -ol if \n"
827 "you want to include running lintian in your checking.")
828 self.assertIn(expected_output, sys.stdout.getvalue())
830 def test_omits_run_lintian_test(self):
831 """ Should invoke `run_lintian_test` with specified args. """
832 self.function_to_test(**self.test_args)
833 self.assertFalse(dput.dput.run_lintian_test.called)
836 class main_UploadHookCommandsTestCase(main_TestCase):
837 """ Test cases for `main` function upload hook commands. """
839 def test_calls_execute_command_with_pre_upload_command(self):
840 """ Should invoke `execute_command` when `pre_upload_command`. """
841 test_command = self.getUniqueString()
842 self.runtime_config_parser.set(
843 self.test_host, 'pre_upload_command', test_command)
844 self.function_to_test(**self.test_args)
845 expected_command = test_command
846 dput.dput.execute_command.assert_called_with(
847 expected_command, "pre", mock.ANY)
849 def test_calls_execute_command_with_post_upload_command(self):
850 """ Should invoke `execute_command` when `post_upload_command`. """
851 test_command = self.getUniqueString()
852 self.runtime_config_parser.set(
853 self.test_host, 'post_upload_command', test_command)
854 self.function_to_test(**self.test_args)
855 expected_command = test_command
856 dput.dput.execute_command.assert_called_with(
857 expected_command, "post", mock.ANY)
860 class main_SimulateTestCase(
861 testscenarios.WithScenarios,
862 main_TestCase):
863 """ Test cases for `main` function when ‘--simulate’ option. """
865 scenarios = [
866 ('simulate', {
867 'getopt_opts': [("--simulate", None)],
869 ('simulate three-files', {
870 'getopt_opts': [("--simulate", None)],
871 'files_to_upload': [tempfile.mktemp() for __ in range(3)],
873 ('simulate dinstall', {
874 'config_run_dinstall': True,
875 'getopt_opts': [("--simulate", None)],
879 def test_omits_upload_method(self):
880 """ Should omit call to upload method function. """
881 expected_method_name = self.runtime_config_parser.get(
882 self.test_host, 'method')
883 expected_method_func = self.upload_methods[expected_method_name]
884 self.function_to_test(**self.test_args)
885 self.assertFalse(expected_method_func.called)
887 def test_omits_create_upload_file(self):
888 """ Should omit call to `create_upload_file` function. """
889 self.function_to_test(**self.test_args)
890 self.assertFalse(dput.dput.create_upload_file.called)
892 def test_omits_dinstall_caller(self):
893 """ Should omit call to `create_upload_log` function. """
894 self.function_to_test(**self.test_args)
895 self.assertFalse(dput.dput.dinstall_caller.called)
898 class main_DelayedQueueTestCase(
899 testscenarios.WithScenarios,
900 main_TestCase):
901 """ Test cases for `main` function, delayed queue processing. """
903 delayed_scenarios = [
904 ('default-delayed', {
905 'config_default_delayed': "",
906 'config_delayed': None,
907 'expected_delay': None,
909 ('config-default-option-delayed-0', {
910 'config_default_delayed': "0",
911 'config_delayed': None,
912 'expected_output': "Uploading to DELAYED/0-day.",
913 'expected_delay': "0-day",
915 ('config-default-option-delayed-5', {
916 'config_default_delayed': "5",
917 'config_delayed': None,
918 'expected_delay': "5-day",
920 ('config-option-delayed-0', {
921 'config_delayed': "0",
922 'expected_output': "Uploading to DELAYED/0-day.",
923 'expected_delay': "0-day",
925 ('config-option-delayed-5', {
926 'config_delayed': "5",
927 'expected_delay': "5-day",
929 ('command-line-option-delayed-0', {
930 'getopt_opts': [("--delayed", "0")],
931 'expected_output': "Uploading to DELAYED/0-day.",
932 'expected_delay': "0-day",
934 ('command-line-option-delayed-5', {
935 'getopt_opts': [("--delayed", "5")],
936 'expected_delay': "5-day",
938 ('different-options-config-and-command-line', {
939 'config_delayed': "13",
940 'getopt_opts': [("--delayed", "5")],
941 'expected_delay': "5-day",
945 incoming_scenarios = [
946 ('default-incoming', {
947 'expected_incoming_host': "quux",
949 ('config-option-incoming-with-trailing-slash', {
950 'config_incoming': "xyzzy/",
951 'expected_incoming_host': "xyzzy/",
953 ('config-option-incoming-no-trailing-slash', {
954 'config_incoming': "xyzzy",
955 'expected_incoming_host': "xyzzy",
959 scenarios = testscenarios.multiply_scenarios(
960 delayed_scenarios, incoming_scenarios)
962 def test_emits_expected_queue_message(self):
963 """ Should emit expected message for the upload queue. """
964 self.function_to_test(**self.test_args)
965 expected_output = getattr(self, 'expected_output', "")
966 self.assertIn(expected_output, sys.stdout.getvalue())
968 def test_calls_upload_method_with_expected_incoming_value(self):
969 """ Should call the upload method with expected `incoming` value. """
970 expected_method_name = self.runtime_config_parser.get(
971 self.test_host, 'method')
972 expected_method_func = self.upload_methods[expected_method_name]
973 expected_incoming = self.expected_incoming_host
974 if self.expected_delay is not None:
975 expected_incoming = os.path.join(
976 self.expected_incoming_host,
977 "DELAYED", self.expected_delay)
978 self.function_to_test(**self.test_args)
979 expected_method_func.assert_called_with(
980 mock.ANY, mock.ANY, expected_incoming,
981 mock.ANY, mock.ANY, mock.ANY,
982 progress=mock.ANY, port=mock.ANY)
985 class main_UnknownUploadMethodTestCase(
986 testscenarios.WithScenarios,
987 main_TestCase):
988 """ Test cases for `main` function when unknown upload method. """
990 scenarios = [
991 ('bogus-default-method', {
992 'config_extras': {
993 'default': {
994 'method': "b0gUs",
997 'expected_output': "Unknown upload method: b0gUs",
999 ('bogus-host-method', {
1000 'config_extras': {
1001 'host': {
1002 'method': "b0gUs",
1005 'expected_output': "Unknown upload method: b0gUs",
1009 def test_emits_error_message_when_unknown_method(self):
1010 """ Should emit error message when unknown upload method. """
1011 try:
1012 self.function_to_test(**self.test_args)
1013 except FakeSystemExit:
1014 pass
1015 self.assertIn(self.expected_output, sys.stdout.getvalue())
1017 def test_calls_sys_exit_when_unknown_method(self):
1018 """ Should call `sys.exit` when unknown upload method. """
1019 with testtools.ExpectedException(FakeSystemExit):
1020 self.function_to_test(**self.test_args)
1021 sys.exit.assert_called_with(EXIT_STATUS_FAILURE)
1024 class main_UploadMethodTestCase(
1025 testscenarios.WithScenarios,
1026 main_TestCase):
1027 """ Test cases for `main` function, invoking upload method. """
1029 method_scenarios = [
1030 ('method-local', {
1031 'config_method': "local",
1032 'config_progress_indicator': 23,
1033 'expected_args': (
1034 "localhost", mock.ANY, mock.ANY, mock.ANY, mock.ANY,
1036 'expected_kwargs': {'progress': 23},
1038 ('method-ftp', {
1039 'config_method': "ftp",
1040 'config_fqdn': "foo.example.com",
1041 'config_passive_ftp': False,
1042 'config_progress_indicator': 23,
1043 'expected_args': (
1044 "foo.example.com", mock.ANY, mock.ANY, mock.ANY, mock.ANY,
1045 False),
1046 'expected_kwargs': {'progress': 23, 'port': 21},
1047 'expected_stdout_output': "D: Using active ftp",
1049 ('method-ftp port-custom', {
1050 'config_method': "ftp",
1051 'config_fqdn': "foo.example.com:42",
1052 'config_passive_ftp': False,
1053 'config_progress_indicator': 23,
1054 'expected_args': (
1055 "foo.example.com", mock.ANY, mock.ANY, mock.ANY, mock.ANY,
1056 False),
1057 'expected_kwargs': {'progress': 23, 'port': "42"},
1058 'expected_stdout_output': "D: Using active ftp",
1060 ('method-ftp config-passive-mode', {
1061 'config_method': "ftp",
1062 'config_fqdn': "foo.example.com",
1063 'config_passive_ftp': True,
1064 'config_progress_indicator': 23,
1065 'expected_args': (
1066 "foo.example.com", mock.ANY, mock.ANY, mock.ANY, mock.ANY,
1067 True),
1068 'expected_kwargs': {'progress': 23, 'port': 21},
1069 'expected_stdout_output': "D: Using passive ftp",
1071 ('method-ftp command-line-passive-mode', {
1072 'config_method': "ftp",
1073 'config_fqdn': "foo.example.com",
1074 'config_progress_indicator': 23,
1075 'getopt_opts': [("--passive", None)],
1076 'expected_args': (
1077 "foo.example.com", mock.ANY, mock.ANY, mock.ANY, mock.ANY,
1078 True),
1079 'expected_kwargs': {'progress': 23, 'port': 21},
1080 'expected_stdout_output': "D: Using passive ftp",
1082 ('method-scp', {
1083 'config_method': "scp",
1084 'config_fqdn': "foo.example.com",
1085 'expected_args': (
1086 "foo.example.com", mock.ANY, mock.ANY, mock.ANY, mock.ANY,
1087 False, []),
1088 'expected_kwargs': {},
1089 'expected_stdout_output': "D: ssh config options:",
1091 ('method-scp scp-compress', {
1092 'config_method': "scp",
1093 'config_fqdn': "foo.example.com",
1094 'config_extras': {
1095 'host': {
1096 'scp_compress': "True",
1097 'ssh_config_options': "spam eggs beans",
1100 'expected_args': (
1101 "foo.example.com", mock.ANY, mock.ANY, mock.ANY, mock.ANY,
1102 True, ["spam eggs beans"]),
1103 'expected_kwargs': {},
1104 'expected_stdout_output': (
1105 "D: Setting compression for scp\n"
1106 "D: ssh config options:\n spam eggs beans"),
1110 isatty_scenarios = [
1111 ('isatty-true', {
1112 'os_isatty_return_value': True,
1114 ('isatty-false', {
1115 'os_isatty_return_value': False,
1116 'progress_indicator_override': 0,
1120 scenarios = testscenarios.multiply_scenarios(
1121 method_scenarios, isatty_scenarios)
1123 for (scenario_name, scenario) in scenarios:
1124 if (
1125 'progress' in scenario['expected_kwargs']
1126 and 'progress_indicator_override' in scenario
1128 expected_kwargs = scenario['expected_kwargs'].copy()
1129 expected_kwargs['progress'] = scenario[
1130 'progress_indicator_override']
1131 scenario['expected_kwargs'] = expected_kwargs
1132 del expected_kwargs
1133 del scenario_name, scenario
1135 @mock.patch.object(dput.dput, 'debug', new=True)
1136 def test_emits_expected_debug_message(self):
1137 """ Should emit expected debug message. """
1138 self.function_to_test(**self.test_args)
1139 if hasattr(self, 'expected_stdout_output'):
1140 self.assertIn(self.expected_stdout_output, sys.stdout.getvalue())
1142 def test_calls_upload_method_with_expected_args(self):
1143 """ Should call upload method function with expected args. """
1144 expected_method_name = self.runtime_config_parser.get(
1145 self.test_host, 'method')
1146 expected_method_func = self.upload_methods[expected_method_name]
1147 self.function_to_test(**self.test_args)
1148 expected_method_func.assert_called_with(
1149 *self.expected_args, **self.expected_kwargs)
1152 class main_UploadLogTestCase(
1153 testscenarios.WithScenarios,
1154 main_TestCase):
1155 """ Test cases for `main` function, creating upload log file. """
1157 scenarios = [
1158 ('default', {
1159 'expect_call': True,
1161 ('command-line-disable-option', {
1162 'getopt_opts': [("--no-upload-log", None)],
1163 'expect_call': False,
1165 ('command-line-simulate-option', {
1166 'getopt_opts': [("--simulate", None)],
1167 'expect_call': False,
1171 def test_calls_create_upload_file_if_specified(self):
1172 """ Should call `create_upload_file` if specified. """
1173 self.function_to_test(**self.test_args)
1174 if self.expect_call:
1175 expected_args = (
1176 os.path.basename(self.changes_file_double.path),
1177 self.test_host,
1178 self.runtime_config_parser.get(self.test_host, 'fqdn'),
1179 os.path.dirname(self.changes_file_double.path),
1180 self.files_to_upload,
1181 mock.ANY)
1182 dput.dput.create_upload_file.assert_called_with(*expected_args)
1183 else:
1184 self.assertFalse(dput.dput.create_upload_file.called)
1187 class main_DinstallTestCase(
1188 testscenarios.WithScenarios,
1189 main_TestCase):
1190 """ Test cases for `main` function, invoking ‘dinstall’ command. """
1192 scenarios = [
1193 ('option-from-command-line', {
1194 'getopt_opts': [("--dinstall", None)],
1195 'expected_output': "D: dinstall: True",
1197 ('option-from-config', {
1198 'config_run_dinstall': True,
1199 'expected_output': "D: Host Config: True",
1203 @mock.patch.object(dput.dput, 'debug', new=True)
1204 def test_emits_debug_message_for_options(self):
1205 """ Should emit debug message for options. """
1206 self.function_to_test(**self.test_args)
1207 self.assertIn(self.expected_output, sys.stdout.getvalue())
1209 def test_calls_dinstall_caller_with_expected_args(self):
1210 """ Should call `dinstall_caller` with expected args. """
1211 expected_args = (
1212 os.path.basename(self.changes_file_double.path),
1213 self.test_host,
1214 self.runtime_config_parser.get(self.test_host, 'fqdn'),
1215 mock.ANY, mock.ANY, mock.ANY)
1216 self.function_to_test(**self.test_args)
1217 dput.dput.dinstall_caller.assert_called_with(*expected_args)
1220 # Copyright © 2015–2016 Ben Finney <bignose@debian.org>
1222 # This is free software: you may copy, modify, and/or distribute this work
1223 # under the terms of the GNU General Public License as published by the
1224 # Free Software Foundation; version 3 of that license or any later version.
1225 # No warranty expressed or implied. See the file ‘LICENSE.GPL-3’ for details.
1228 # Local variables:
1229 # coding: utf-8
1230 # mode: python
1231 # End:
1232 # vim: fileencoding=utf-8 filetype=python :