Add autopkgtest smoke tests to verify the commands will run.
[dput.git] / test / test_dput_main.py
blob148debf27f61c9fd0b9ce2a37991eba02974a54b
1 # -*- coding: utf-8; -*-
3 # test/test_dput_main.py
4 # Part of ‘dput’, a Debian package upload toolkit.
6 # Copyright © 2015–2016 Ben Finney <ben+python@benfinney.id.au>
8 # This is free software: you may copy, modify, and/or distribute this work
9 # under the terms of the GNU General Public License as published by the
10 # Free Software Foundation; version 3 of that license or any later version.
11 # No warranty expressed or implied. See the file ‘LICENSE.GPL-3’ for details.
13 """ Unit tests for ‘dput’ module `main` function. """
15 from __future__ import (absolute_import, unicode_literals)
17 import sys
18 import os
19 import tempfile
20 import textwrap
21 import doctest
23 import testtools
24 import testtools.matchers
25 import testscenarios
26 import pkg_resources
28 __package__ = str("test")
29 __import__(__package__)
30 sys.path.insert(1, os.path.dirname(os.path.dirname(__file__)))
31 import dput.dput
32 from dput.helper import dputhelper
34 from .helper import (
35 mock,
36 FakeSystemExit,
37 EXIT_STATUS_SUCCESS, EXIT_STATUS_FAILURE,
38 patch_sys_argv,
39 patch_system_interfaces,
40 patch_os_environ,
41 patch_os_getuid,
42 PasswdEntry,
43 patch_pwd_getpwuid,
45 from .test_changesfile import (
46 make_changes_file_path,
47 setup_changes_file_fixtures,
48 set_changes_file_scenario,
50 from .test_configfile import (
51 set_config,
52 patch_runtime_config_options,
54 from .test_dputhelper import (
55 patch_pkg_resources_get_distribution,
56 patch_getopt,
60 def patch_parse_changes(testcase):
61 """ Patch the `parse_changes` function for the test case. """
62 func_patcher = mock.patch.object(
63 dput.dput, "parse_changes", autospec=True)
64 func_patcher.start()
65 testcase.addCleanup(func_patcher.stop)
68 def patch_read_configs(testcase):
69 """ Patch the `read_configs` function for the test case. """
70 def fake_read_configs(*args, **kwargs):
71 return testcase.runtime_config_parser
73 func_patcher = mock.patch.object(
74 dput.dput, "read_configs", autospec=True,
75 side_effect=fake_read_configs)
76 func_patcher.start()
77 testcase.addCleanup(func_patcher.stop)
80 def patch_print_config(testcase):
81 """ Patch the `print_config` function for the test case. """
82 func_patcher = mock.patch.object(
83 dput.dput, "print_config", autospec=True)
84 func_patcher.start()
85 testcase.addCleanup(func_patcher.stop)
88 def set_upload_methods(testcase):
89 """ Set the `upload_methods` value for the test case. """
90 if not hasattr(testcase, 'upload_methods'):
91 upload_methods = dput.dput.import_upload_functions()
92 testcase.upload_methods = {
93 name: mock.MagicMock(upload_methods[name])
94 for name in upload_methods}
97 def patch_import_upload_functions(testcase):
98 """ Patch the `import_upload_functions` function for the test case. """
99 func_patcher = mock.patch.object(
100 dput.dput, "import_upload_functions", autospec=True,
101 return_value=testcase.upload_methods)
102 func_patcher.start()
103 testcase.addCleanup(func_patcher.stop)
106 class make_usage_message_TestCase(testtools.TestCase):
107 """ Test cases for `make_usage_message` function. """
109 def test_returns_text_with_program_name(self):
110 """ Should return text with expected program name. """
111 result = dput.dput.make_usage_message()
112 expected_result = textwrap.dedent("""\
113 Usage: dput ...
115 """)
116 self.expectThat(
117 result,
118 testtools.matchers.DocTestMatches(
119 expected_result, flags=doctest.ELLIPSIS))
122 class main_TestCase(
123 testtools.TestCase):
124 """ Base for test cases for `main` function. """
126 function_to_test = staticmethod(dput.dput.main)
128 def setUp(self):
129 """ Set up test fixtures. """
130 super(main_TestCase, self).setUp()
131 patch_system_interfaces(self)
133 set_config(
134 self,
135 getattr(self, 'config_scenario_name', 'exist-simple'))
136 patch_runtime_config_options(self)
138 setup_changes_file_fixtures(self)
139 set_changes_file_scenario(self, 'exist-minimal')
141 self.patch_os_isatty()
142 patch_os_environ(self)
143 patch_os_getuid(self)
144 patch_pwd_getpwuid(self)
145 patch_sys_argv(self)
147 self.set_files_to_upload()
148 set_upload_methods(self)
150 self.set_test_args()
152 self.patch_make_usage_message()
153 self.patch_distribution()
154 self.patch_getopt()
155 patch_read_configs(self)
156 patch_print_config(self)
157 patch_import_upload_functions(self)
158 self.patch_guess_upload_host()
159 self.patch_check_upload_logfile()
160 self.patch_verify_files()
161 self.patch_run_lintian_test()
162 self.patch_execute_command()
163 self.patch_create_upload_file()
164 self.patch_dinstall_caller()
166 def patch_os_isatty(self):
167 """ Patch the `os.isatty` function. """
168 if not hasattr(self, 'os_isatty_return_value'):
169 self.os_isatty_return_value = False
170 func_patcher = mock.patch.object(
171 os, "isatty", autospec=True,
172 return_value=self.os_isatty_return_value)
173 func_patcher.start()
174 self.addCleanup(func_patcher.stop)
176 def set_files_to_upload(self):
177 """ Set the `files_to_upload` collection for this instance. """
178 if not hasattr(self, 'files_to_upload'):
179 self.files_to_upload = []
181 def set_test_args(self):
182 """ Set the arguments for the test call to the function. """
183 self.test_args = dict()
185 def patch_make_usage_message(self):
186 """ Patch the `make_usage_message` function. """
187 if not hasattr(self, 'dput_usage_message'):
188 self.dput_usage_message = self.getUniqueString()
189 func_patcher = mock.patch.object(
190 dput.dput, "make_usage_message", autospec=True,
191 return_value=self.dput_usage_message)
192 func_patcher.start()
193 self.addCleanup(func_patcher.stop)
195 def patch_distribution(self):
196 """ Patch the Python distribution for this test case. """
197 self.fake_distribution = mock.MagicMock(pkg_resources.Distribution)
198 if hasattr(self, 'dput_version'):
199 self.fake_distribution.version = self.dput_version
200 patch_pkg_resources_get_distribution(self)
202 def patch_getopt(self):
203 """ Patch the `dputhelper.getopt` function. """
204 if not hasattr(self, 'getopt_opts'):
205 self.getopt_opts = []
206 if not hasattr(self, 'getopt_args'):
207 self.getopt_args = [self.changes_file_double.path]
209 patch_getopt(self)
211 def patch_guess_upload_host(self):
212 """ Patch the `guess_upload_host` function. """
213 if not hasattr(self, 'guess_upload_host_return_value'):
214 self.guess_upload_host_return_value = self.test_host
215 func_patcher = mock.patch.object(
216 dput.dput, "guess_upload_host", autospec=True,
217 return_value=self.guess_upload_host_return_value)
218 func_patcher.start()
219 self.addCleanup(func_patcher.stop)
221 def patch_check_upload_logfile(self):
222 """ Patch the `check_upload_logfile` function. """
223 func_patcher = mock.patch.object(
224 dput.dput, "check_upload_logfile", autospec=True)
225 func_patcher.start()
226 self.addCleanup(func_patcher.stop)
228 def patch_verify_files(self):
229 """ Patch the `verify_files` function. """
230 func_patcher = mock.patch.object(
231 dput.dput, "verify_files", autospec=True,
232 return_value=self.files_to_upload)
233 func_patcher.start()
234 self.addCleanup(func_patcher.stop)
236 def patch_run_lintian_test(self):
237 """ Patch the `run_lintian_test` function. """
238 func_patcher = mock.patch.object(
239 dput.dput, "run_lintian_test", autospec=True)
240 func_patcher.start()
241 self.addCleanup(func_patcher.stop)
243 def patch_execute_command(self):
244 """ Patch the `execute_command` function. """
245 func_patcher = mock.patch.object(
246 dput.dput, "execute_command", autospec=True)
247 func_patcher.start()
248 self.addCleanup(func_patcher.stop)
250 def patch_create_upload_file(self):
251 """ Patch the `create_upload_file` function. """
252 func_patcher = mock.patch.object(
253 dput.dput, "create_upload_file", autospec=True)
254 func_patcher.start()
255 self.addCleanup(func_patcher.stop)
257 def patch_dinstall_caller(self):
258 """ Patch the `dinstall_caller` function. """
259 func_patcher = mock.patch.object(
260 dput.dput, "dinstall_caller", autospec=True)
261 func_patcher.start()
262 self.addCleanup(func_patcher.stop)
265 class main_CommandLineProcessingTestCase(main_TestCase):
266 """ Test cases for `main` command-line processing. """
268 sys_argv = ["lorem"] + [
269 make_changes_file_path(tempfile.mktemp())
270 for __ in range(3)]
272 def test_calls_getopt_with_expected_args(self):
273 """ Should call `getopt` with expected arguments. """
274 self.function_to_test(**self.test_args)
275 dputhelper.getopt.assert_called_with(
276 self.sys_argv[1:], mock.ANY, mock.ANY)
278 def test_emits_error_when_invalid_delayed_value(self):
279 """ Should emit error message when ‘--delayed’ invalid. """
280 self.getopt_opts = [("--delayed", "b0gUs")]
281 try:
282 self.function_to_test(**self.test_args)
283 except FakeSystemExit:
284 pass
285 expected_output = textwrap.dedent("""\
286 Incorrect delayed argument, ...
287 """)
288 self.assertThat(
289 sys.stdout.getvalue(),
290 testtools.matchers.DocTestMatches(
291 expected_output, doctest.ELLIPSIS))
293 def test_calls_sys_exit_when_invalid_delayed_value(self):
294 """ Should call `sys.exit` when ‘--delayed’ invalid. """
295 self.getopt_opts = [("--delayed", "b0gUs")]
296 with testtools.ExpectedException(FakeSystemExit):
297 self.function_to_test(**self.test_args)
298 sys.exit.assert_called_with(EXIT_STATUS_FAILURE)
300 def test_emits_error_when_no_nonoption_args(self):
301 """ Should emit error message when no non-option args. """
302 self.getopt_args = []
303 try:
304 self.function_to_test(**self.test_args)
305 except FakeSystemExit:
306 pass
307 expected_output = textwrap.dedent("""\
309 No package or host has been provided, ...
310 """)
311 self.assertThat(
312 sys.stdout.getvalue(),
313 testtools.matchers.DocTestMatches(
314 expected_output, doctest.ELLIPSIS))
316 def test_calls_sys_exit_when_no_nonoption_args(self):
317 """ Should call `sys.exit` when no non-option args. """
318 self.getopt_args = []
319 with testtools.ExpectedException(FakeSystemExit):
320 self.function_to_test(**self.test_args)
321 sys.exit.assert_called_with(EXIT_STATUS_SUCCESS)
323 @mock.patch.object(dput.dput, 'debug', new=object())
324 def test_sets_debug_flag_when_debug_option(self):
325 """ Should set `debug` when ‘--debug’ option. """
326 self.getopt_opts = [("--debug", None)]
327 self.function_to_test(**self.test_args)
328 self.assertEqual(dput.dput.debug, True)
331 class main_DebugMessageTestCase(main_TestCase):
332 """ Test cases for `main` debug messages. """
334 progname = "lorem"
335 dput_version = "ipsum"
337 @mock.patch.object(dput.dput, 'debug', new=True)
338 def test_emits_debug_message_with_dput_version(self):
339 """ Should emit debug message showing Dput version string. """
340 self.getopts_opts = [("--version", None)]
341 self.function_to_test(**self.test_args)
342 expected_progname = self.progname
343 expected_version = self.fake_distribution.version
344 expected_output = textwrap.dedent("""\
345 D: {progname} {version}
346 """).format(
347 progname=expected_progname,
348 version=expected_version)
349 self.assertIn(expected_output, sys.stdout.getvalue())
351 @mock.patch.object(dput.dput, 'debug', new=True)
352 def test_emits_debug_message_for_discovered_methods(self):
353 """ Should emit debug message for discovered upload methods. """
354 self.function_to_test(**self.test_args)
355 expected_output = textwrap.dedent("""\
356 D: Default Method: {default_method}
357 D: Host Method: {host_method}
358 """).format(
359 default_method=self.runtime_config_parser.get(
360 'DEFAULT', 'method'),
361 host_method=self.runtime_config_parser.get(
362 self.test_host, 'method'))
363 self.assertIn(expected_output, sys.stdout.getvalue())
366 class main_DefaultFunctionCallTestCase(main_TestCase):
367 """ Test cases for `main` defaults calling other functions. """
369 def test_calls_import_upload_functions_with_expected_args(self):
370 """ Should call `import_upload_functions` with expected arguments. """
371 self.function_to_test(**self.test_args)
372 dput.dput.import_upload_functions.assert_called_with()
375 class main_ConfigFileTestCase(
376 testscenarios.WithScenarios,
377 main_TestCase):
378 """ Test cases for `main` specification of configuration file. """
380 scenarios = [
381 ('default', {
382 'expected_args': ("", mock.ANY),
384 ('config-from-command-line', {
385 'getopt_opts': [("--config", "lorem.conf")],
386 'expected_args': ("lorem.conf", mock.ANY),
390 def test_calls_read_configs_with_expected_args(self):
391 """ Should call `read_configs` with expected arguments. """
392 self.function_to_test(**self.test_args)
393 dput.dput.read_configs.assert_called_with(*self.expected_args)
396 class main_PrintAndEndTestCase(main_TestCase):
397 """ Test cases for `main` that print and end. """
399 progname = "lorem"
400 dput_version = "ipsum"
401 dput_usage_message = "Lorem ipsum, dolor sit amet."
403 def test_emit_usage_message_when_option_help(self):
404 """ Should emit usage message when ‘--help’ option. """
405 self.getopt_opts = [("--help", None)]
406 self.function_to_test(**self.test_args)
407 expected_output = self.dput_usage_message
408 self.assertIn(expected_output, sys.stdout.getvalue())
410 def test_emit_version_message_when_option_help(self):
411 """ Should emit usage message when ‘--version’ option. """
412 self.getopt_opts = [("--version", None)]
413 self.function_to_test(**self.test_args)
414 expected_progname = self.progname
415 expected_version = self.fake_distribution.version
416 expected_output = "{progname} {version}".format(
417 progname=expected_progname, version=expected_version)
418 self.assertIn(expected_output, sys.stdout.getvalue())
420 def test_print_config_when_option_print(self):
421 """ Should call `print_config` when ‘--print’. """
422 self.getopt_opts = [("--print", None)]
423 try:
424 self.function_to_test(**self.test_args)
425 except FakeSystemExit:
426 pass
427 dput.dput.print_config.assert_called_with(
428 self.runtime_config_parser, dput.dput.debug)
430 def test_print_config_then_sys_exit_when_option_print(self):
431 """ Should call `print_config`, then `sys.exit`, when ‘--print’. """
432 self.getopt_opts = [("--print", None)]
433 with testtools.ExpectedException(FakeSystemExit):
434 self.function_to_test(**self.test_args)
435 sys.exit.assert_called_with(EXIT_STATUS_SUCCESS)
437 def test_calls_sys_exit_when_option_host_list(self):
438 """ Should call `sys.exit` when option ‘--host-list’. """
439 self.getopt_opts = [("--host-list", None)]
440 with testtools.ExpectedException(FakeSystemExit):
441 self.function_to_test(**self.test_args)
442 sys.exit.assert_called_with(EXIT_STATUS_SUCCESS)
445 class main_DiscoverLoginTestCase(
446 testscenarios.WithScenarios,
447 main_TestCase):
448 """ Test cases for `main` discovery of login name. """
450 fallback_login_scenarios = [
451 ('login-from-environ', {
452 'os_environ': {
453 'USER': "login-from-environ",
455 'expected_fallback_login': "login-from-environ",
457 ('login-from-pwd', {
458 'pwd_getpwuid_return_value': PasswdEntry(
459 *(["login-from-pwd"] + [object()] * 6)),
460 'expected_fallback_login': "login-from-pwd",
464 config_login_scenarios = [
465 ('config-default-login', {
466 'config_extras': {
467 'default': {
468 'login': "login-from-config-default",
471 'expected_login': "login-from-config-default",
472 'expected_output_template':
473 "D: Login {login} from section {host} used",
475 ('config-host-login', {
476 'config_extras': {
477 'host': {
478 'login': "login-from-config-host",
481 'expected_login': "login-from-config-host",
482 'expected_output_template':
483 "D: Login {login} from section {host} used",
485 ('config-default-login sentinel', {
486 'config_extras': {
487 'default': {
488 'login': "username",
491 'expected_output_template':
492 "D: Neither host {host} nor default login used.",
494 ('config-host-login sentinel', {
495 'config_extras': {
496 'host': {
497 'login': "username",
500 'expected_output_template':
501 "D: Neither host {host} nor default login used.",
505 scenarios = testscenarios.multiply_scenarios(
506 fallback_login_scenarios, config_login_scenarios)
507 for (scenario_name, scenario) in scenarios:
508 if 'expected_login' not in scenario:
509 scenario['expected_login'] = scenario['expected_fallback_login']
510 del scenario_name, scenario
512 @mock.patch.object(dput.dput, 'debug', new=True)
513 def test_emits_debug_message_for_fallback_login(self):
514 """ Should emit a debug message for the fallback login. """
515 self.function_to_test(**self.test_args)
516 expected_output = textwrap.dedent("""\
517 D: Login: {fallback_login}
518 """).format(fallback_login=self.expected_fallback_login)
519 self.assertIn(expected_output, sys.stdout.getvalue())
521 @mock.patch.object(dput.dput, 'debug', new=True)
522 def test_emits_debug_message_for_discovered_login(self):
523 """ Should emit a debug message for the discovered login. """
524 self.function_to_test(**self.test_args)
525 expected_output = self.expected_output_template.format(
526 login=self.expected_login, host=self.test_host)
527 self.assertIn(expected_output, sys.stdout.getvalue())
529 def test_calls_upload_method_with_expected_login(self):
530 """ Should call upload method function with expected login arg. """
531 expected_method_name = self.runtime_config_parser.get(
532 self.test_host, 'method')
533 expected_method_func = self.upload_methods[expected_method_name]
534 self.function_to_test(**self.test_args)
535 expected_method_func.assert_called_with(
536 mock.ANY, self.expected_login,
537 mock.ANY, mock.ANY, mock.ANY, mock.ANY,
538 progress=mock.ANY, port=mock.ANY)
541 class main_HostListTestCase(
542 testscenarios.WithScenarios,
543 main_TestCase):
544 """ Test cases for `main` with ‘--host-list’ option. """
546 scenarios = [
547 ('default', {}),
548 ('config-distribution-one', {
549 'config_scenario_name': "exist-distribution-one",
551 ('config-distribution-three', {
552 'config_scenario_name': "exist-distribution-three",
556 def test_iterate_config_sections_when_option_host_list(self):
557 """ Should iteratively emit config sections when ‘--host-list’. """
558 self.getopt_opts = [("--host-list", None)]
559 try:
560 self.function_to_test(**self.test_args)
561 except FakeSystemExit:
562 pass
563 expected_output_lines = ["..."] + [
564 "{section} => {fqdn} (Upload method: {method}...)".format(
565 section=section_name,
566 fqdn=self.runtime_config_parser.get(section_name, 'fqdn'),
567 method=self.runtime_config_parser.get(
568 section_name, 'method'),
570 for section_name in self.runtime_config_parser.sections()
571 ] + ["..."]
572 expected_output = "\n".join(expected_output_lines)
573 self.assertThat(
574 sys.stdout.getvalue(),
575 testtools.matchers.DocTestMatches(
576 expected_output, doctest.ELLIPSIS))
579 class main_NamedHostTestCase(
580 testscenarios.WithScenarios,
581 main_TestCase):
582 """ Test cases for `main` function, named host processing. """
584 scenarios = [
585 ('host-from-command-line', {
586 'config_scenario_name': "exist-simple-host-three",
587 'getopt_args': ["bar", "lorem.changes", "ipsum.changes"],
588 'expected_host': "bar",
589 'expected_debug_output': "D: Host bar found in config",
591 ('host-from-command-line check-only', {
592 'config_scenario_name': "exist-simple-host-three",
593 'getopt_opts': [("--check-only", None)],
594 'getopt_args': ["bar", "lorem.changes", "ipsum.changes"],
595 'expected_host': "bar",
596 'expected_debug_output': "D: Host bar found in config",
598 ('host-from-command-line force-upload', {
599 'config_scenario_name': "exist-simple-host-three",
600 'getopt_opts': [("--force", None)],
601 'getopt_args': ["bar", "lorem.changes", "ipsum.changes"],
602 'expected_host': "bar",
603 'expected_debug_output': "D: Host bar found in config",
605 ('only-changes-filenames', {
606 'config_scenario_name': "exist-simple-host-three",
607 'getopt_args': ["lorem.changes", "ipsum.changes"],
608 'expected_host': "foo",
609 'expected_debug_output': "D: No host named on command line.",
613 @mock.patch.object(dput.dput, 'debug', new=True)
614 def test_emits_expected_debug_message(self):
615 """ Should emit expected debug message. """
616 try:
617 self.function_to_test(**self.test_args)
618 except FakeSystemExit:
619 pass
620 expected_output = self.expected_debug_output.format(
621 host=self.test_host)
622 self.assertIn(expected_output, sys.stdout.getvalue())
624 def test_check_upload_logfile_called_with_expected_host(self):
625 """ Should call `check_upload_logfile` with expected host. """
626 try:
627 self.function_to_test(**self.test_args)
628 except FakeSystemExit:
629 pass
630 expected_fqdn = self.runtime_config_parser.get(self.test_host, 'fqdn')
631 expected_args = (
632 mock.ANY, self.expected_host, expected_fqdn,
633 mock.ANY, mock.ANY, mock.ANY, mock.ANY)
634 dput.dput.check_upload_logfile.assert_called_with(*expected_args)
636 def test_verify_files_called_with_expected_host(self):
637 """ Should call `verify_files` with expected host. """
638 try:
639 self.function_to_test(**self.test_args)
640 except FakeSystemExit:
641 pass
642 expected_args = (
643 mock.ANY, mock.ANY, self.expected_host,
644 mock.ANY, mock.ANY, mock.ANY, mock.ANY, mock.ANY)
645 dput.dput.verify_files.assert_called_with(*expected_args)
648 class main_NamedHostNotInConfigTestCase(
649 testscenarios.WithScenarios,
650 main_TestCase):
651 """ Test cases for `main` function, named host not in config. """
653 scenarios = [
654 ('host-from-command-line', {
655 'config_scenario_name': "exist-simple-host-three",
656 'getopt_args': ["b0gUs", "lorem.changes", "ipsum.changes"],
657 'expected_stderr_output': "No host b0gUs found in config",
658 'expected_exception': FakeSystemExit,
659 'expected_exit_status': EXIT_STATUS_FAILURE,
661 ('host-from-command-line gluck_delayed', {
662 'config_scenario_name': "exist-simple-host-three",
663 'getopt_args': [
664 "gluck_delayed", "lorem.changes", "ipsum.changes"],
665 'expected_stderr_output': textwrap.dedent("""\
666 No host gluck_delayed found in config
668 The delayed upload queue has been moved back to
669 ftp-master (aka ftp.upload.debian.org).
670 """),
671 'expected_exception': FakeSystemExit,
672 'expected_exit_status': EXIT_STATUS_FAILURE,
674 ('host-from-command-line check-only', {
675 'config_scenario_name': "exist-simple-host-three",
676 'getopt_opts': [("--check-only", None)],
677 'getopt_args': ["b0gUs", "lorem.changes", "ipsum.changes"],
678 'expected_stdout_output': "D: No host b0gUs found in config",
680 ('only-changes-filenames check-only', {
681 'config_scenario_name': "exist-simple-host-three",
682 'getopt_opts': [("--check-only", None)],
683 'getopt_args': ["lorem.changes", "ipsum.changes"],
684 'expected_stdout_output':
685 "No host lorem.changes found in config",
689 @mock.patch.object(dput.dput, 'debug', new=True)
690 def test_emits_expected_debug_message(self):
691 """ Should emit expected debug message. """
692 try:
693 self.function_to_test(**self.test_args)
694 except FakeSystemExit:
695 pass
696 if hasattr(self, 'expected_stdout_output'):
697 self.assertIn(self.expected_stdout_output, sys.stdout.getvalue())
698 if hasattr(self, 'expected_stderr_output'):
699 self.assertIn(self.expected_stderr_output, sys.stderr.getvalue())
701 def test_calls_sys_exit_when_host_not_in_config(self):
702 """ Should call `sys.exit` when named host not in config. """
703 if not hasattr(self, 'expected_exception'):
704 self.skipTest("No expected exception for this scenario")
705 with testtools.ExpectedException(self.expected_exception):
706 self.function_to_test(**self.test_args)
707 sys.exit.assert_called_with(self.expected_exit_status)
710 class main_check_upload_logfile_CallTestCase(
711 testscenarios.WithScenarios,
712 main_TestCase):
713 """ Test cases for `main` function calling `check_upload_logfile`. """
715 scenarios = [
716 ('default', {}),
717 ('command-line-option-check-only', {
718 'getopt_opts': [("--check-only", None)],
719 'expected_check_only': True,
721 ('command-line-option-lintian', {
722 'getopt_opts': [("--lintian", None)],
723 'expected_lintian': True,
725 ('command-line-option-force', {
726 'getopt_opts': [("--force", None)],
727 'expected_force_upload': True,
729 ('command-line-options-three', {
730 'getopt_opts': [
731 ("--force", None),
732 ("--lintian", None),
733 ("--check-only", None),
735 'expected_check_only': True,
736 'expected_lintian': True,
737 'expected_force_upload': True,
741 def test_calls_check_upload_logfile_with_expected_args(self):
742 """ Should invoke `check_upload_logfile` with expected args. """
743 self.function_to_test(**self.test_args)
744 expected_args = (
745 self.changes_file_double.path, self.test_host,
746 self.runtime_config_parser.get(self.test_host, 'fqdn'),
747 getattr(self, 'expected_check_only', False),
748 getattr(self, 'expected_lintian', False),
749 getattr(self, 'expected_force_upload', False),
750 mock.ANY)
751 dput.dput.check_upload_logfile.assert_called_with(*expected_args)
754 class main_verify_files_CallTestCase(
755 testscenarios.WithScenarios,
756 main_TestCase):
757 """ Test cases for `main` function calling `verify_files`. """
759 scenarios = [
760 ('default', {}),
761 ('command-line-option-check-only', {
762 'getopt_opts': [("--check-only", None)],
763 'expected_check_only': True,
765 ('command-line-option-check-version', {
766 'getopt_opts': [("--check_version", None)],
767 'expected_check_version': True,
769 ('command-line-option-unchecked', {
770 'getopt_opts': [("--unchecked", None)],
771 'expected_unsigned_upload': True,
773 ('command-line-options-three', {
774 'getopt_opts': [
775 ("--check-only", None),
776 ("--check_version", None),
777 ("--unchecked", None),
779 'expected_check_only': True,
780 'expected_check_version': True,
781 'expected_unsigned_upload': True,
785 def test_calls_verify_files_with_expected_args(self):
786 """ Should invoke `verify_files` with expected args. """
787 self.function_to_test(**self.test_args)
788 expected_args = (
789 os.path.dirname(self.changes_file_double.path),
790 os.path.basename(self.changes_file_double.path),
791 self.test_host,
792 self.runtime_config_parser,
793 getattr(self, 'expected_check_only', False),
794 getattr(self, 'expected_check_version', False),
795 getattr(self, 'expected_unsigned_upload', False),
796 mock.ANY)
797 dput.dput.verify_files.assert_called_with(*expected_args)
800 class main_run_lintian_test_CallTestCase(
801 testscenarios.WithScenarios,
802 main_TestCase):
803 """ Test cases for `main` function calling `run_lintian_test`. """
805 scenarios = [
806 ('option-from-command-line', {
807 'getopt_opts': [("--lintian", None)],
809 ('option-from-config', {
810 'config_run_lintian': True,
814 def test_calls_run_lintian_test_with_expected_args(self):
815 """ Should invoke `run_lintian_test` with expected args. """
816 self.function_to_test(**self.test_args)
817 expected_args = (self.changes_file_double.path,)
818 dput.dput.run_lintian_test.assert_called_with(*expected_args)
821 class main_run_lintian_test_NoCallTestCase(
822 testscenarios.WithScenarios,
823 main_TestCase):
824 """ Test cases for `main` function omitting `run_lintian_test`. """
826 def test_emits_warning_message_when_check_only(self):
827 """ Should emit warning message when `--check-only`. """
828 self.getopt_opts = [("--check-only", None)]
829 self.function_to_test(**self.test_args)
830 expected_output = (
831 "Warning: The option -o does not automatically include \n"
832 "a lintian run any more. Please use the option -ol if \n"
833 "you want to include running lintian in your checking.")
834 self.assertIn(expected_output, sys.stdout.getvalue())
836 def test_omits_run_lintian_test(self):
837 """ Should invoke `run_lintian_test` with specified args. """
838 self.function_to_test(**self.test_args)
839 self.assertFalse(dput.dput.run_lintian_test.called)
842 class main_UploadHookCommandsTestCase(main_TestCase):
843 """ Test cases for `main` function upload hook commands. """
845 def test_calls_execute_command_with_pre_upload_command(self):
846 """ Should invoke `execute_command` when `pre_upload_command`. """
847 test_command = self.getUniqueString()
848 self.runtime_config_parser.set(
849 self.test_host, 'pre_upload_command', test_command)
850 self.function_to_test(**self.test_args)
851 expected_command = test_command
852 dput.dput.execute_command.assert_called_with(
853 expected_command, "pre", mock.ANY)
855 def test_calls_execute_command_with_post_upload_command(self):
856 """ Should invoke `execute_command` when `post_upload_command`. """
857 test_command = self.getUniqueString()
858 self.runtime_config_parser.set(
859 self.test_host, 'post_upload_command', test_command)
860 self.function_to_test(**self.test_args)
861 expected_command = test_command
862 dput.dput.execute_command.assert_called_with(
863 expected_command, "post", mock.ANY)
866 class main_SimulateTestCase(
867 testscenarios.WithScenarios,
868 main_TestCase):
869 """ Test cases for `main` function when ‘--simulate’ option. """
871 scenarios = [
872 ('simulate', {
873 'getopt_opts': [("--simulate", None)],
875 ('simulate three-files', {
876 'getopt_opts': [("--simulate", None)],
877 'files_to_upload': [tempfile.mktemp() for __ in range(3)],
879 ('simulate dinstall', {
880 'config_run_dinstall': True,
881 'getopt_opts': [("--simulate", None)],
885 def test_omits_upload_method(self):
886 """ Should omit call to upload method function. """
887 expected_method_name = self.runtime_config_parser.get(
888 self.test_host, 'method')
889 expected_method_func = self.upload_methods[expected_method_name]
890 self.function_to_test(**self.test_args)
891 self.assertFalse(expected_method_func.called)
893 def test_omits_create_upload_file(self):
894 """ Should omit call to `create_upload_file` function. """
895 self.function_to_test(**self.test_args)
896 self.assertFalse(dput.dput.create_upload_file.called)
898 def test_omits_dinstall_caller(self):
899 """ Should omit call to `create_upload_log` function. """
900 self.function_to_test(**self.test_args)
901 self.assertFalse(dput.dput.dinstall_caller.called)
904 class main_DelayedQueueTestCase(
905 testscenarios.WithScenarios,
906 main_TestCase):
907 """ Test cases for `main` function, delayed queue processing. """
909 delayed_scenarios = [
910 ('default-delayed', {
911 'config_default_delayed': "",
912 'config_delayed': None,
913 'expected_delay': None,
915 ('config-default-option-delayed-0', {
916 'config_default_delayed': "0",
917 'config_delayed': None,
918 'expected_output': "Uploading to DELAYED/0-day.",
919 'expected_delay': "0-day",
921 ('config-default-option-delayed-5', {
922 'config_default_delayed': "5",
923 'config_delayed': None,
924 'expected_delay': "5-day",
926 ('config-option-delayed-0', {
927 'config_delayed': "0",
928 'expected_output': "Uploading to DELAYED/0-day.",
929 'expected_delay': "0-day",
931 ('config-option-delayed-5', {
932 'config_delayed': "5",
933 'expected_delay': "5-day",
935 ('command-line-option-delayed-0', {
936 'getopt_opts': [("--delayed", "0")],
937 'expected_output': "Uploading to DELAYED/0-day.",
938 'expected_delay': "0-day",
940 ('command-line-option-delayed-5', {
941 'getopt_opts': [("--delayed", "5")],
942 'expected_delay': "5-day",
944 ('different-options-config-and-command-line', {
945 'config_delayed': "13",
946 'getopt_opts': [("--delayed", "5")],
947 'expected_delay': "5-day",
951 incoming_scenarios = [
952 ('default-incoming', {
953 'expected_incoming_host': "quux",
955 ('config-option-incoming-with-trailing-slash', {
956 'config_incoming': "xyzzy/",
957 'expected_incoming_host': "xyzzy/",
959 ('config-option-incoming-no-trailing-slash', {
960 'config_incoming': "xyzzy",
961 'expected_incoming_host': "xyzzy",
965 scenarios = testscenarios.multiply_scenarios(
966 delayed_scenarios, incoming_scenarios)
968 def test_emits_expected_queue_message(self):
969 """ Should emit expected message for the upload queue. """
970 self.function_to_test(**self.test_args)
971 expected_output = getattr(self, 'expected_output', "")
972 self.assertIn(expected_output, sys.stdout.getvalue())
974 def test_calls_upload_method_with_expected_incoming_value(self):
975 """ Should call the upload method with expected `incoming` value. """
976 expected_method_name = self.runtime_config_parser.get(
977 self.test_host, 'method')
978 expected_method_func = self.upload_methods[expected_method_name]
979 expected_incoming = self.expected_incoming_host
980 if self.expected_delay is not None:
981 expected_incoming = os.path.join(
982 self.expected_incoming_host,
983 "DELAYED", self.expected_delay)
984 self.function_to_test(**self.test_args)
985 expected_method_func.assert_called_with(
986 mock.ANY, mock.ANY, expected_incoming,
987 mock.ANY, mock.ANY, mock.ANY,
988 progress=mock.ANY, port=mock.ANY)
991 class main_UnknownUploadMethodTestCase(
992 testscenarios.WithScenarios,
993 main_TestCase):
994 """ Test cases for `main` function when unknown upload method. """
996 scenarios = [
997 ('bogus-default-method', {
998 'config_extras': {
999 'default': {
1000 'method': "b0gUs",
1003 'expected_output': "Unknown upload method: b0gUs",
1005 ('bogus-host-method', {
1006 'config_extras': {
1007 'host': {
1008 'method': "b0gUs",
1011 'expected_output': "Unknown upload method: b0gUs",
1015 def test_emits_error_message_when_unknown_method(self):
1016 """ Should emit error message when unknown upload method. """
1017 try:
1018 self.function_to_test(**self.test_args)
1019 except FakeSystemExit:
1020 pass
1021 self.assertIn(self.expected_output, sys.stdout.getvalue())
1023 def test_calls_sys_exit_when_unknown_method(self):
1024 """ Should call `sys.exit` when unknown upload method. """
1025 with testtools.ExpectedException(FakeSystemExit):
1026 self.function_to_test(**self.test_args)
1027 sys.exit.assert_called_with(EXIT_STATUS_FAILURE)
1030 class main_UploadMethodTestCase(
1031 testscenarios.WithScenarios,
1032 main_TestCase):
1033 """ Test cases for `main` function, invoking upload method. """
1035 method_scenarios = [
1036 ('method-local', {
1037 'config_method': "local",
1038 'config_progress_indicator': 23,
1039 'expected_args': (
1040 "localhost", mock.ANY, mock.ANY, mock.ANY, mock.ANY,
1042 'expected_kwargs': {'progress': 23},
1044 ('method-ftp', {
1045 'config_method': "ftp",
1046 'config_fqdn': "foo.example.com",
1047 'config_passive_ftp': False,
1048 'config_progress_indicator': 23,
1049 'expected_args': (
1050 "foo.example.com", mock.ANY, mock.ANY, mock.ANY, mock.ANY,
1051 False),
1052 'expected_kwargs': {'progress': 23, 'port': 21},
1053 'expected_stdout_output': "D: Using active ftp",
1055 ('method-ftp port-custom', {
1056 'config_method': "ftp",
1057 'config_fqdn': "foo.example.com:42",
1058 'config_passive_ftp': False,
1059 'config_progress_indicator': 23,
1060 'expected_args': (
1061 "foo.example.com", mock.ANY, mock.ANY, mock.ANY, mock.ANY,
1062 False),
1063 'expected_kwargs': {'progress': 23, 'port': "42"},
1064 'expected_stdout_output': "D: Using active ftp",
1066 ('method-ftp config-passive-mode', {
1067 'config_method': "ftp",
1068 'config_fqdn': "foo.example.com",
1069 'config_passive_ftp': True,
1070 'config_progress_indicator': 23,
1071 'expected_args': (
1072 "foo.example.com", mock.ANY, mock.ANY, mock.ANY, mock.ANY,
1073 True),
1074 'expected_kwargs': {'progress': 23, 'port': 21},
1075 'expected_stdout_output': "D: Using passive ftp",
1077 ('method-ftp command-line-passive-mode', {
1078 'config_method': "ftp",
1079 'config_fqdn': "foo.example.com",
1080 'config_progress_indicator': 23,
1081 'getopt_opts': [("--passive", None)],
1082 'expected_args': (
1083 "foo.example.com", mock.ANY, mock.ANY, mock.ANY, mock.ANY,
1084 True),
1085 'expected_kwargs': {'progress': 23, 'port': 21},
1086 'expected_stdout_output': "D: Using passive ftp",
1088 ('method-scp', {
1089 'config_method': "scp",
1090 'config_fqdn': "foo.example.com",
1091 'expected_args': (
1092 "foo.example.com", mock.ANY, mock.ANY, mock.ANY, mock.ANY,
1093 False, []),
1094 'expected_kwargs': {},
1095 'expected_stdout_output': "D: ssh config options:",
1097 ('method-scp scp-compress', {
1098 'config_method': "scp",
1099 'config_fqdn': "foo.example.com",
1100 'config_extras': {
1101 'host': {
1102 'scp_compress': "True",
1103 'ssh_config_options': "spam eggs beans",
1106 'expected_args': (
1107 "foo.example.com", mock.ANY, mock.ANY, mock.ANY, mock.ANY,
1108 True, ["spam eggs beans"]),
1109 'expected_kwargs': {},
1110 'expected_stdout_output': (
1111 "D: Setting compression for scp\n"
1112 "D: ssh config options:\n spam eggs beans"),
1116 isatty_scenarios = [
1117 ('isatty-true', {
1118 'os_isatty_return_value': True,
1120 ('isatty-false', {
1121 'os_isatty_return_value': False,
1122 'progress_indicator_override': 0,
1126 scenarios = testscenarios.multiply_scenarios(
1127 method_scenarios, isatty_scenarios)
1129 for (scenario_name, scenario) in scenarios:
1130 if (
1131 'progress' in scenario['expected_kwargs']
1132 and 'progress_indicator_override' in scenario
1134 expected_kwargs = scenario['expected_kwargs'].copy()
1135 expected_kwargs['progress'] = scenario[
1136 'progress_indicator_override']
1137 scenario['expected_kwargs'] = expected_kwargs
1138 del expected_kwargs
1139 del scenario_name, scenario
1141 @mock.patch.object(dput.dput, 'debug', new=True)
1142 def test_emits_expected_debug_message(self):
1143 """ Should emit expected debug message. """
1144 self.function_to_test(**self.test_args)
1145 if hasattr(self, 'expected_stdout_output'):
1146 self.assertIn(self.expected_stdout_output, sys.stdout.getvalue())
1148 def test_calls_upload_method_with_expected_args(self):
1149 """ Should call upload method function with expected args. """
1150 expected_method_name = self.runtime_config_parser.get(
1151 self.test_host, 'method')
1152 expected_method_func = self.upload_methods[expected_method_name]
1153 self.function_to_test(**self.test_args)
1154 expected_method_func.assert_called_with(
1155 *self.expected_args, **self.expected_kwargs)
1158 class main_UploadLogTestCase(
1159 testscenarios.WithScenarios,
1160 main_TestCase):
1161 """ Test cases for `main` function, creating upload log file. """
1163 scenarios = [
1164 ('default', {
1165 'expect_call': True,
1167 ('command-line-disable-option', {
1168 'getopt_opts': [("--no-upload-log", None)],
1169 'expect_call': False,
1171 ('command-line-simulate-option', {
1172 'getopt_opts': [("--simulate", None)],
1173 'expect_call': False,
1177 def test_calls_create_upload_file_if_specified(self):
1178 """ Should call `create_upload_file` if specified. """
1179 self.function_to_test(**self.test_args)
1180 if self.expect_call:
1181 expected_args = (
1182 os.path.basename(self.changes_file_double.path),
1183 self.test_host,
1184 self.runtime_config_parser.get(self.test_host, 'fqdn'),
1185 os.path.dirname(self.changes_file_double.path),
1186 self.files_to_upload,
1187 mock.ANY)
1188 dput.dput.create_upload_file.assert_called_with(*expected_args)
1189 else:
1190 self.assertFalse(dput.dput.create_upload_file.called)
1193 class main_DinstallTestCase(
1194 testscenarios.WithScenarios,
1195 main_TestCase):
1196 """ Test cases for `main` function, invoking ‘dinstall’ command. """
1198 scenarios = [
1199 ('option-from-command-line', {
1200 'getopt_opts': [("--dinstall", None)],
1201 'expected_output': "D: dinstall: True",
1203 ('option-from-config', {
1204 'config_run_dinstall': True,
1205 'expected_output': "D: Host Config: True",
1209 @mock.patch.object(dput.dput, 'debug', new=True)
1210 def test_emits_debug_message_for_options(self):
1211 """ Should emit debug message for options. """
1212 self.function_to_test(**self.test_args)
1213 self.assertIn(self.expected_output, sys.stdout.getvalue())
1215 def test_calls_dinstall_caller_with_expected_args(self):
1216 """ Should call `dinstall_caller` with expected args. """
1217 expected_args = (
1218 os.path.basename(self.changes_file_double.path),
1219 self.test_host,
1220 self.runtime_config_parser.get(self.test_host, 'fqdn'),
1221 mock.ANY, mock.ANY, mock.ANY)
1222 self.function_to_test(**self.test_args)
1223 dput.dput.dinstall_caller.assert_called_with(*expected_args)
1226 # Local variables:
1227 # coding: utf-8
1228 # mode: python
1229 # End:
1230 # vim: fileencoding=utf-8 filetype=python :