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
)
24 import testtools
.matchers
28 __package__
= str("test")
29 __import__(__package__
)
30 sys
.path
.insert(1, os
.path
.dirname(os
.path
.dirname(__file__
)))
32 from dput
.helper
import dputhelper
37 EXIT_STATUS_SUCCESS
, EXIT_STATUS_FAILURE
,
39 patch_system_interfaces
,
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 (
52 patch_runtime_config_options
,
54 from .test_dputhelper
import (
55 patch_pkg_resources_get_distribution
,
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)
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
)
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)
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
)
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("""\
118 testtools
.matchers
.DocTestMatches(
119 expected_result
, flags
=doctest
.ELLIPSIS
))
124 """ Base for test cases for `main` function. """
126 function_to_test
= staticmethod(dput
.dput
.main
)
129 """ Set up test fixtures. """
130 super(main_TestCase
, self
).setUp()
131 patch_system_interfaces(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
)
147 self
.set_files_to_upload()
148 set_upload_methods(self
)
152 self
.patch_make_usage_message()
153 self
.patch_distribution()
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
)
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
)
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
]
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
)
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)
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
)
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)
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)
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)
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)
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())
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")]
282 self
.function_to_test(**self
.test_args
)
283 except FakeSystemExit
:
285 expected_output
= textwrap
.dedent("""\
286 Incorrect delayed argument, ...
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
= []
304 self
.function_to_test(**self
.test_args
)
305 except FakeSystemExit
:
307 expected_output
= textwrap
.dedent("""\
309 No package or host has been provided, ...
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. """
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}
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}
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
,
378 """ Test cases for `main` specification of configuration file. """
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. """
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)]
424 self
.function_to_test(**self
.test_args
)
425 except FakeSystemExit
:
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
,
448 """ Test cases for `main` discovery of login name. """
450 fallback_login_scenarios
= [
451 ('login-from-environ', {
453 'USER': "login-from-environ",
455 'expected_fallback_login': "login-from-environ",
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', {
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', {
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', {
491 'expected_output_template':
492 "D: Neither host {host} nor default login used.",
494 ('config-host-login sentinel', {
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
,
544 """ Test cases for `main` with ‘--host-list’ option. """
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)]
560 self
.function_to_test(**self
.test_args
)
561 except FakeSystemExit
:
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()
572 expected_output
= "\n".join(expected_output_lines
)
574 sys
.stdout
.getvalue(),
575 testtools
.matchers
.DocTestMatches(
576 expected_output
, doctest
.ELLIPSIS
))
579 class main_NamedHostTestCase(
580 testscenarios
.WithScenarios
,
582 """ Test cases for `main` function, named host processing. """
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. """
617 self
.function_to_test(**self
.test_args
)
618 except FakeSystemExit
:
620 expected_output
= self
.expected_debug_output
.format(
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. """
627 self
.function_to_test(**self
.test_args
)
628 except FakeSystemExit
:
630 expected_fqdn
= self
.runtime_config_parser
.get(self
.test_host
, 'fqdn')
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. """
639 self
.function_to_test(**self
.test_args
)
640 except FakeSystemExit
:
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
,
651 """ Test cases for `main` function, named host not in config. """
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",
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).
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. """
693 self
.function_to_test(**self
.test_args
)
694 except FakeSystemExit
:
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
,
713 """ Test cases for `main` function calling `check_upload_logfile`. """
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', {
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
)
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),
751 dput
.dput
.check_upload_logfile
.assert_called_with(*expected_args
)
754 class main_verify_files_CallTestCase(
755 testscenarios
.WithScenarios
,
757 """ Test cases for `main` function calling `verify_files`. """
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', {
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
)
789 os
.path
.dirname(self
.changes_file_double
.path
),
790 os
.path
.basename(self
.changes_file_double
.path
),
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),
797 dput
.dput
.verify_files
.assert_called_with(*expected_args
)
800 class main_run_lintian_test_CallTestCase(
801 testscenarios
.WithScenarios
,
803 """ Test cases for `main` function calling `run_lintian_test`. """
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
,
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
)
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
,
869 """ Test cases for `main` function when ‘--simulate’ option. """
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
,
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
,
994 """ Test cases for `main` function when unknown upload method. """
997 ('bogus-default-method', {
1003 'expected_output': "Unknown upload method: b0gUs",
1005 ('bogus-host-method', {
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. """
1018 self
.function_to_test(**self
.test_args
)
1019 except FakeSystemExit
:
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
,
1033 """ Test cases for `main` function, invoking upload method. """
1035 method_scenarios
= [
1037 'config_method': "local",
1038 'config_progress_indicator': 23,
1040 "localhost", mock
.ANY
, mock
.ANY
, mock
.ANY
, mock
.ANY
,
1042 'expected_kwargs': {'progress': 23},
1045 'config_method': "ftp",
1046 'config_fqdn': "foo.example.com",
1047 'config_passive_ftp': False,
1048 'config_progress_indicator': 23,
1050 "foo.example.com", mock
.ANY
, mock
.ANY
, mock
.ANY
, mock
.ANY
,
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,
1061 "foo.example.com", mock
.ANY
, mock
.ANY
, mock
.ANY
, mock
.ANY
,
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,
1072 "foo.example.com", mock
.ANY
, mock
.ANY
, mock
.ANY
, mock
.ANY
,
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)],
1083 "foo.example.com", mock
.ANY
, mock
.ANY
, mock
.ANY
, mock
.ANY
,
1085 'expected_kwargs': {'progress': 23, 'port': 21},
1086 'expected_stdout_output': "D: Using passive ftp",
1089 'config_method': "scp",
1090 'config_fqdn': "foo.example.com",
1092 "foo.example.com", mock
.ANY
, mock
.ANY
, mock
.ANY
, mock
.ANY
,
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",
1102 'scp_compress': "True",
1103 'ssh_config_options': "spam eggs beans",
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
= [
1118 'os_isatty_return_value': True,
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
:
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
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
,
1161 """ Test cases for `main` function, creating upload log file. """
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
:
1182 os
.path
.basename(self
.changes_file_double
.path
),
1184 self
.runtime_config_parser
.get(self
.test_host
, 'fqdn'),
1185 os
.path
.dirname(self
.changes_file_double
.path
),
1186 self
.files_to_upload
,
1188 dput
.dput
.create_upload_file
.assert_called_with(*expected_args
)
1190 self
.assertFalse(dput
.dput
.create_upload_file
.called
)
1193 class main_DinstallTestCase(
1194 testscenarios
.WithScenarios
,
1196 """ Test cases for `main` function, invoking ‘dinstall’ command. """
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. """
1218 os
.path
.basename(self
.changes_file_double
.path
),
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
)
1230 # vim: fileencoding=utf-8 filetype=python :