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
)
23 import testtools
.matchers
26 from dput
.helper
import dputhelper
29 EXIT_STATUS_FAILURE
, EXIT_STATUS_SUCCESS
,
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
,
48 from .test_dputhelper
import (
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)
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
)
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)
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
)
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("""\
112 testtools
.matchers
.DocTestMatches(
113 expected_result
, flags
=doctest
.ELLIPSIS
))
118 """ Base for test cases for `main` function. """
120 function_to_test
= staticmethod(dput
.dput
.main
)
123 """ Set up test fixtures. """
124 super(main_TestCase
, self
).setUp()
125 patch_system_interfaces(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
)
141 self
.set_files_to_upload()
142 set_upload_methods(self
)
146 self
.patch_make_usage_message()
147 self
.patch_distribution()
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
)
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
)
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
]
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
)
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)
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
)
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)
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)
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)
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)
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())
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")]
276 self
.function_to_test(**self
.test_args
)
277 except FakeSystemExit
:
279 expected_output
= textwrap
.dedent("""\
280 Incorrect delayed argument, ...
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
= []
298 self
.function_to_test(**self
.test_args
)
299 except FakeSystemExit
:
301 expected_output
= textwrap
.dedent("""\
303 No package or host has been provided, ...
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. """
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}
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}
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
,
372 """ Test cases for `main` specification of configuration file. """
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. """
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)]
418 self
.function_to_test(**self
.test_args
)
419 except FakeSystemExit
:
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
,
442 """ Test cases for `main` discovery of login name. """
444 fallback_login_scenarios
= [
445 ('login-from-environ', {
447 'USER': "login-from-environ",
449 'expected_fallback_login': "login-from-environ",
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', {
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', {
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', {
485 'expected_output_template':
486 "D: Neither host {host} nor default login used.",
488 ('config-host-login sentinel', {
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
,
538 """ Test cases for `main` with ‘--host-list’ option. """
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)]
554 self
.function_to_test(**self
.test_args
)
555 except FakeSystemExit
:
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()
566 expected_output
= "\n".join(expected_output_lines
)
568 sys
.stdout
.getvalue(),
569 testtools
.matchers
.DocTestMatches(
570 expected_output
, doctest
.ELLIPSIS
))
573 class main_NamedHostTestCase(
574 testscenarios
.WithScenarios
,
576 """ Test cases for `main` function, named host processing. """
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. """
611 self
.function_to_test(**self
.test_args
)
612 except FakeSystemExit
:
614 expected_output
= self
.expected_debug_output
.format(
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. """
621 self
.function_to_test(**self
.test_args
)
622 except FakeSystemExit
:
624 expected_fqdn
= self
.runtime_config_parser
.get(self
.test_host
, 'fqdn')
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. """
633 self
.function_to_test(**self
.test_args
)
634 except FakeSystemExit
:
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
,
645 """ Test cases for `main` function, named host not in config. """
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",
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).
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. """
687 self
.function_to_test(**self
.test_args
)
688 except FakeSystemExit
:
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
,
707 """ Test cases for `main` function calling `check_upload_logfile`. """
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', {
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
)
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),
745 dput
.dput
.check_upload_logfile
.assert_called_with(*expected_args
)
748 class main_verify_files_CallTestCase(
749 testscenarios
.WithScenarios
,
751 """ Test cases for `main` function calling `verify_files`. """
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', {
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
)
783 os
.path
.dirname(self
.changes_file_double
.path
),
784 os
.path
.basename(self
.changes_file_double
.path
),
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),
791 dput
.dput
.verify_files
.assert_called_with(*expected_args
)
794 class main_run_lintian_test_CallTestCase(
795 testscenarios
.WithScenarios
,
797 """ Test cases for `main` function calling `run_lintian_test`. """
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
,
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
)
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
,
863 """ Test cases for `main` function when ‘--simulate’ option. """
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
,
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
,
988 """ Test cases for `main` function when unknown upload method. """
991 ('bogus-default-method', {
997 'expected_output': "Unknown upload method: b0gUs",
999 ('bogus-host-method', {
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. """
1012 self
.function_to_test(**self
.test_args
)
1013 except FakeSystemExit
:
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
,
1027 """ Test cases for `main` function, invoking upload method. """
1029 method_scenarios
= [
1031 'config_method': "local",
1032 'config_progress_indicator': 23,
1034 "localhost", mock
.ANY
, mock
.ANY
, mock
.ANY
, mock
.ANY
,
1036 'expected_kwargs': {'progress': 23},
1039 'config_method': "ftp",
1040 'config_fqdn': "foo.example.com",
1041 'config_passive_ftp': False,
1042 'config_progress_indicator': 23,
1044 "foo.example.com", mock
.ANY
, mock
.ANY
, mock
.ANY
, mock
.ANY
,
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,
1055 "foo.example.com", mock
.ANY
, mock
.ANY
, mock
.ANY
, mock
.ANY
,
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,
1066 "foo.example.com", mock
.ANY
, mock
.ANY
, mock
.ANY
, mock
.ANY
,
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)],
1077 "foo.example.com", mock
.ANY
, mock
.ANY
, mock
.ANY
, mock
.ANY
,
1079 'expected_kwargs': {'progress': 23, 'port': 21},
1080 'expected_stdout_output': "D: Using passive ftp",
1083 'config_method': "scp",
1084 'config_fqdn': "foo.example.com",
1086 "foo.example.com", mock
.ANY
, mock
.ANY
, mock
.ANY
, mock
.ANY
,
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",
1096 'scp_compress': "True",
1097 'ssh_config_options': "spam eggs beans",
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
= [
1112 'os_isatty_return_value': True,
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
:
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
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
,
1155 """ Test cases for `main` function, creating upload log file. """
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
:
1176 os
.path
.basename(self
.changes_file_double
.path
),
1178 self
.runtime_config_parser
.get(self
.test_host
, 'fqdn'),
1179 os
.path
.dirname(self
.changes_file_double
.path
),
1180 self
.files_to_upload
,
1182 dput
.dput
.create_upload_file
.assert_called_with(*expected_args
)
1184 self
.assertFalse(dput
.dput
.create_upload_file
.called
)
1187 class main_DinstallTestCase(
1188 testscenarios
.WithScenarios
,
1190 """ Test cases for `main` function, invoking ‘dinstall’ command. """
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. """
1212 os
.path
.basename(self
.changes_file_double
.path
),
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.
1232 # vim: fileencoding=utf-8 filetype=python :