1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882 |
- # -*- coding: utf-8; -*-
- #
- # test/test_dcut.py
- # Part of ‘dput’, a Debian package upload toolkit.
- #
- # Copyright © 2015 Ben Finney <ben+python@benfinney.id.au>
- #
- # This is free software: you may copy, modify, and/or distribute this work
- # under the terms of the GNU General Public License as published by the
- # Free Software Foundation; version 3 of that license or any later version.
- # No warranty expressed or implied. See the file ‘LICENSE.GPL-3’ for details.
- """ Unit tests for ‘dcut’ module. """
- from __future__ import (absolute_import, unicode_literals)
- import sys
- import os
- import shutil
- import subprocess
- import tempfile
- import itertools
- import textwrap
- import doctest
- import testtools
- import testscenarios
- import pkg_resources
- __package__ = str("test")
- __import__(__package__)
- sys.path.insert(1, os.path.dirname(os.path.dirname(__file__)))
- import dput.dcut
- from dput.helper import dputhelper
- from .helper import (
- StringIO,
- mock,
- FakeSystemExit,
- EXIT_STATUS_SUCCESS, EXIT_STATUS_FAILURE,
- patch_sys_argv,
- patch_system_interfaces,
- patch_time_time,
- patch_os_environ,
- patch_os_getpid,
- patch_os_getuid,
- PasswdEntry,
- patch_pwd_getpwuid,
- patch_os_unlink,
- patch_os_rmdir,
- patch_shutil_rmtree,
- patch_tempfile_mkdtemp,
- FileDouble,
- setup_file_double_behaviour,
- ARG_ANY, ARG_MORE,
- SubprocessDouble,
- patch_subprocess_popen,
- patch_os_system,
- patch_os_popen,
- patch_os_waitpid,
- setup_subprocess_double_behaviour,
- )
- from .test_configfile import (
- set_config,
- )
- from .test_dputhelper import (
- patch_pkg_resources_get_distribution,
- patch_getopt,
- )
- from .test_changesfile import (
- make_changes_file_scenarios,
- set_changes_file_scenario,
- )
- from . import test_dput_main
- dummy_pwent = PasswdEntry(
- pw_name="lorem",
- pw_passwd="!",
- pw_uid=1,
- pw_gid=1,
- pw_gecos="Lorem Ipsum,spam,eggs,beans",
- pw_dir=tempfile.mktemp(),
- pw_shell=tempfile.mktemp())
- def patch_getoptions(testcase):
- """ Patch the `getoptions` function for this test case. """
- default_options = {
- 'debug': False,
- 'simulate': False,
- 'config': None,
- 'host': "foo",
- 'passive': False,
- 'changes': None,
- 'filetoupload': None,
- 'filetocreate': None,
- }
- if not hasattr(testcase, 'getoptions_opts'):
- testcase.getoptions_opts = {}
- if not hasattr(testcase, 'getoptions_args'):
- testcase.getoptions_args = []
- def fake_getoptions():
- options = dict(default_options)
- options.update(testcase.getoptions_opts)
- arguments = list(testcase.getoptions_args)
- result = (options, arguments)
- return result
- func_patcher = mock.patch.object(
- dput.dcut, "getoptions", side_effect=fake_getoptions)
- func_patcher.start()
- testcase.addCleanup(func_patcher.stop)
- def get_upload_method_func(testcase):
- """ Get the specified upload method. """
- host = testcase.test_host
- method_name = testcase.runtime_config_parser.get(host, 'method')
- method_func = dput.dput.upload_methods[method_name]
- return method_func
- class make_usage_message_TestCase(testtools.TestCase):
- """ Test cases for `make_usage_message` function. """
- def setUp(self):
- """ Set up test fixtures. """
- super(make_usage_message_TestCase, self).setUp()
- patch_sys_argv(self)
- def test_returns_text_with_program_name(self):
- """ Should return text with expected program name. """
- result = dput.dcut.make_usage_message()
- expected_result = textwrap.dedent("""\
- Usage: {progname} ...
- ...
- """).format(progname=self.progname)
- self.expectThat(
- result,
- testtools.matchers.DocTestMatches(
- expected_result, flags=doctest.ELLIPSIS))
- class getoptions_TestCase(testtools.TestCase):
- """ Base for test cases for `getoptions` function. """
- default_options = NotImplemented
- scenarios = NotImplemented
- def setUp(self):
- """ Set up test fixtures. """
- super(getoptions_TestCase, self).setUp()
- patch_system_interfaces(self)
- patch_os_environ(self)
- patch_os_getuid(self)
- patch_pwd_getpwuid(self)
- patch_sys_argv(self)
- self.patch_etc_mailname()
- setup_file_double_behaviour(
- self, [self.mailname_file_double])
- self.set_hostname_subprocess_double()
- patch_os_popen(self)
- self.patch_getopt()
- if hasattr(self, 'expected_options'):
- self.set_expected_result()
- self.patch_distribution()
- self.patch_make_usage_message()
- def patch_etc_mailname(self):
- """ Patch the ‘/etc/mailname’ file. """
- path = "/etc/mailname"
- if hasattr(self, 'mailname_fake_file'):
- double = FileDouble(path, self.mailname_fake_file)
- else:
- double = FileDouble(path, StringIO())
- if hasattr(self, 'mailname_file_open_scenario_name'):
- double.set_open_scenario(self.mailname_file_open_scenario_name)
- self.mailname_file_double = double
- def set_hostname_subprocess_double(self):
- """ Set the test double for the ‘hostname’ subprocess. """
- path = "/bin/hostname"
- argv = (path, "--fqdn")
- double = SubprocessDouble(path, argv=argv)
- double.register_for_testcase(self)
- double.set_os_popen_scenario('success')
- double.set_stdout_content(getattr(self, 'hostname_stdout_content', ""))
- self.hostname_subprocess_double = double
- def patch_getopt(self):
- """ Patch the `dputhelper.getopt` function. """
- if not hasattr(self, 'getopt_opts'):
- self.getopt_opts = []
- else:
- self.getopt_opts = list(self.getopt_opts)
- if not hasattr(self, 'getopt_args'):
- self.getopt_args = []
- else:
- self.getopt_args = list(self.getopt_args)
- patch_getopt(self)
- def set_expected_result(self):
- """ Set the expected result value. """
- if not hasattr(self, 'expected_arguments'):
- self.expected_arguments = []
- expected_options = self.default_options.copy()
- expected_options.update(self.expected_options)
- self.expected_result = (expected_options, self.expected_arguments)
- def patch_distribution(self):
- """ Patch the Python distribution for this test case. """
- self.fake_distribution = mock.MagicMock(pkg_resources.Distribution)
- if hasattr(self, 'dcut_version'):
- self.fake_distribution.version = self.dcut_version
- patch_pkg_resources_get_distribution(self)
- def patch_make_usage_message(self):
- """ Patch the `make_usage_message` function. """
- if hasattr(self, 'dcut_usage_message'):
- text = self.dcut_usage_message
- else:
- text = self.getUniqueString()
- func_patcher = mock.patch.object(
- dput.dcut, 'make_usage_message', return_value=text)
- func_patcher.start()
- self.addCleanup(func_patcher.stop)
- class getoptions_UploaderTestCase(
- testscenarios.WithScenarios,
- getoptions_TestCase):
- """ Test cases for `getoptions` function, determining uploader. """
- environ_scenarios = [
- ('environ-none', {
- 'os_environ': {},
- }),
- ('environ-email-not-delimited', {
- 'os_environ': {'EMAIL': "quux@example.org"},
- 'expected_environ_uploader': "<quux@example.org>",
- }),
- ('environ-email-delimited', {
- 'os_environ': {'EMAIL': "<quux@example.org>"},
- 'expected_environ_uploader': "<quux@example.org>",
- }),
- ('environ-debemail-not-delimited', {
- 'os_environ': {'DEBEMAIL': "flup@example.org"},
- 'expected_environ_uploader': "<flup@example.org>",
- }),
- ('environ-debemail-delimited', {
- 'os_environ': {'DEBEMAIL': "<flup@example.org>"},
- 'expected_environ_uploader': "<flup@example.org>",
- }),
- ('environ-both-email-and-debfullname', {
- 'os_environ': {
- 'EMAIL': "quux@example.org",
- 'DEBFULLNAME': "Lorem Ipsum",
- },
- 'expected_environ_uploader': "Lorem Ipsum <quux@example.org>",
- }),
- ('environ-both-debemail-and-debfullname', {
- 'os_environ': {
- 'DEBEMAIL': "flup@example.org",
- 'DEBFULLNAME': "Lorem Ipsum",
- },
- 'expected_environ_uploader': "Lorem Ipsum <flup@example.org>",
- }),
- ('environ-both-email-and-debemail', {
- 'os_environ': {
- 'EMAIL': "quux@example.org",
- 'DEBEMAIL': "flup@example.org",
- },
- 'expected_environ_uploader': "<flup@example.org>",
- }),
- ('environ-both-email-and-debemail-and-debfullname', {
- 'os_environ': {
- 'EMAIL': "quux@example.org",
- 'DEBEMAIL': "flup@example.org",
- 'DEBFULLNAME': "Lorem Ipsum",
- },
- 'expected_environ_uploader': "Lorem Ipsum <flup@example.org>",
- }),
- ]
- system_scenarios = [
- ('domain-from-mailname-file', {
- 'mailname_fake_file': StringIO("consecteur.example.org"),
- 'pwd_getpwuid_return_value': dummy_pwent._replace(
- pw_name="grue",
- pw_gecos="Dolor Sit Amet,spam,beans,eggs"),
- 'expected_debug_chatter': textwrap.dedent("""\
- D: Guessing uploader
- """),
- 'expected_system_uploader':
- "Dolor Sit Amet <grue@consecteur.example.org>",
- }),
- ('domain-from-hostname-command', {
- 'mailname_file_open_scenario_name': "read_denied",
- 'hostname_stdout_content': "consecteur.example.org\n",
- 'pwd_getpwuid_return_value': dummy_pwent._replace(
- pw_name="grue",
- pw_gecos="Dolor Sit Amet,spam,beans,eggs"),
- 'expected_debug_chatter': textwrap.dedent("""\
- D: Guessing uploader
- D: Guessing uploader: /etc/mailname was a failure
- """),
- 'expected_system_uploader':
- "Dolor Sit Amet <grue@consecteur.example.org>",
- }),
- ('domain-failure', {
- 'mailname_file_open_scenario_name': "read_denied",
- 'hostname_stdout_content': "",
- 'pwd_getpwuid_return_value': dummy_pwent._replace(
- pw_name="grue",
- pw_gecos="Dolor Sit Amet,spam,beans,eggs"),
- 'expected_debug_chatter': textwrap.dedent("""\
- D: Guessing uploader
- D: Guessing uploader: /etc/mailname was a failure
- D: Couldn't guess uploader
- """),
- }),
- ]
- scenarios = testscenarios.multiply_scenarios(
- environ_scenarios, system_scenarios)
- def setUp(self, *args, **kwargs):
- """ Set up test fixtures. """
- super(getoptions_UploaderTestCase, self).setUp(*args, **kwargs)
- self.set_expected_uploader()
- def set_expected_uploader(self):
- """ Set the expected uploader value for this test case. """
- for attrib_name in [
- 'expected_command_line_uploader',
- 'expected_environ_uploader',
- 'expected_system_uploader']:
- if hasattr(self, attrib_name):
- self.expected_uploader = getattr(self, attrib_name)
- break
- def test_emits_debug_message_for_uploader_discovery(self):
- """ Should emit debug message for uploader discovery. """
- sys.argv.insert(1, "--debug")
- dput.dcut.getoptions()
- expected_output_lines = [
- "D: trying to get maintainer email from environment"]
- if hasattr(self, 'expected_environ_uploader'):
- guess_line_template = "D: Uploader from env: {uploader}"
- else:
- expected_output_lines.extend(
- self.expected_debug_chatter.split("\n")[:-1])
- if hasattr(self, 'expected_system_uploader'):
- guess_line_template = "D: Guessed uploader: {uploader}"
- if hasattr(self, 'expected_uploader'):
- expected_output_lines.append(guess_line_template.format(
- uploader=self.expected_uploader))
- expected_output = "\n".join(expected_output_lines)
- self.assertIn(expected_output, sys.stdout.getvalue())
- class getoptions_ParseCommandLineTestCase(
- testscenarios.WithScenarios,
- getoptions_TestCase):
- """ Test cases for `getoptions` function, parsing command line. """
- dcut_usage_message = "Lorem ipsum, dolor sit amet."
- progname = "lorem"
- dcut_version = "ipsum"
- config_file_path = tempfile.mktemp()
- changes_file_path = tempfile.mktemp()
- output_file_path = tempfile.mktemp()
- upload_file_path = tempfile.mktemp()
- default_options = dict()
- default_options.update(
- (key, None) for key in [
- 'config', 'host', 'uploader', 'keyid',
- 'filetocreate', 'filetoupload', 'changes'])
- default_options.update(
- (key, False) for key in ['debug', 'simulate', 'passive'])
- option_scenarios = [
- ('no-options', {
- 'getopt_opts': [],
- }),
- ('option-bogus', {
- 'getopt_opts': [("--b0gUs", "BOGUS")],
- 'expected_stderr_output': (
- "{progname} internal error:"
- " Option --b0gUs, argument BOGUS unknown").format(
- progname=progname),
- 'expected_exit_status': EXIT_STATUS_FAILURE,
- }),
- ('option-help', {
- 'getopt_opts': [("--help", None)],
- 'expected_stdout_output': dcut_usage_message,
- 'expected_exit_status': EXIT_STATUS_SUCCESS,
- }),
- ('option-version', {
- 'getopt_opts': [("--version", None)],
- 'expected_stdout_output': " ".join(
- [progname, dcut_version]),
- 'expected_exit_status': EXIT_STATUS_SUCCESS,
- }),
- ('option-filetoupload-and-environ-uploader', {
- 'os_environ': {
- 'DEBEMAIL': "flup@example.org",
- 'DEBFULLNAME': "Lorem Ipsum",
- },
- 'getopt_opts': [
- ("--upload", upload_file_path),
- ],
- 'expected_options': {
- 'uploader': "Lorem Ipsum <flup@example.org>",
- 'filetoupload': upload_file_path,
- },
- 'expected_arguments': [],
- }),
- ('option-changes-and-environ-uploader', {
- 'os_environ': {
- 'DEBEMAIL': "flup@example.org",
- 'DEBFULLNAME': "Lorem Ipsum",
- },
- 'getopt_opts': [
- ("--input", changes_file_path),
- ],
- 'expected_options': {
- 'uploader': "Lorem Ipsum <flup@example.org>",
- 'changes': changes_file_path,
- },
- 'expected_arguments': [],
- }),
- ('option-filetoupload-and-option-maintaineraddress', {
- 'getopt_opts': [
- ("--upload", upload_file_path),
- ("--maintaineraddress", "Lorem Ipsum <flup@example.org>"),
- ],
- 'expected_options': {
- 'uploader': "Lorem Ipsum <flup@example.org>",
- 'filetoupload': upload_file_path,
- },
- 'expected_arguments': [],
- }),
- ('option-changes-and-option-maintaineraddress', {
- 'getopt_opts': [
- ("--input", changes_file_path),
- ("--maintaineraddress", "Lorem Ipsum <flup@example.org>"),
- ],
- 'expected_options': {
- 'uploader': "Lorem Ipsum <flup@example.org>",
- 'changes': changes_file_path,
- },
- 'expected_arguments': [],
- }),
- ('option-filetoupload-with-no-uploader', {
- 'getopt_opts': [("--upload", upload_file_path)],
- 'expected_stderr_output': "command file cannot be created",
- 'expected_exit_status': EXIT_STATUS_FAILURE,
- }),
- ('option-changes-with-no-uploader', {
- 'getopt_opts': [("--input", changes_file_path)],
- 'expected_stderr_output': "command file cannot be created",
- 'expected_exit_status': EXIT_STATUS_FAILURE,
- }),
- ('option-several', {
- 'getopt_opts': [
- ("--debug", None),
- ("--simulate", None),
- ("--config", config_file_path),
- ("--maintaineraddress", "Lorem Ipsum <flup@example.org>"),
- ("--keyid", "DEADBEEF"),
- ("--passive", None),
- ("--output", output_file_path),
- ("--host", "quux.example.com"),
- ],
- 'expected_options': {
- 'debug': True,
- 'simulate': True,
- 'config': config_file_path,
- 'uploader': "Lorem Ipsum <flup@example.org>",
- 'keyid': "DEADBEEF",
- 'passive': True,
- 'filetocreate': output_file_path,
- 'host': "quux.example.com",
- },
- 'expected_arguments': [],
- }),
- ]
- scenarios = option_scenarios
- def test_emits_debug_message_for_program_version(self):
- """ Should emit debug message for program version. """
- sys.argv.insert(1, "--debug")
- expected_progname = self.progname
- expected_version = self.dcut_version
- try:
- dput.dcut.getoptions()
- except FakeSystemExit:
- pass
- expected_output = textwrap.dedent("""\
- D: {progname} {version}
- """).format(
- progname=expected_progname,
- version=expected_version)
- self.assertIn(expected_output, sys.stdout.getvalue())
- def test_calls_getopt_with_expected_args(self):
- """ Should call `getopt` with expected arguments. """
- try:
- dput.dcut.getoptions()
- except FakeSystemExit:
- pass
- dputhelper.getopt.assert_called_with(
- self.sys_argv[1:], mock.ANY, mock.ANY)
- def test_emits_debug_message_for_each_option(self):
- """ Should emit a debug message for each option processed. """
- sys.argv.insert(1, "--debug")
- try:
- dput.dcut.getoptions()
- except FakeSystemExit:
- pass
- expected_output_lines = [
- "D: processing arg \"{opt}\", option \"{arg}\"".format(
- opt=option, arg=option_argument)
- for (option, option_argument) in self.getopt_args]
- expected_output = "\n".join(expected_output_lines)
- self.assertIn(expected_output, sys.stdout.getvalue())
- def test_emits_expected_message(self):
- """ Should emit message with expected content. """
- try:
- dput.dcut.getoptions()
- except FakeSystemExit:
- pass
- if hasattr(self, 'expected_stdout_output'):
- self.assertIn(self.expected_stdout_output, sys.stdout.getvalue())
- if hasattr(self, 'expected_stderr_output'):
- self.assertIn(self.expected_stderr_output, sys.stderr.getvalue())
- def test_calls_sys_exit_with_expected_exit_status(self):
- """ Should call `sys.exit` with expected exit status. """
- if not hasattr(self, 'expected_exit_status'):
- dput.dcut.getoptions()
- else:
- with testtools.ExpectedException(FakeSystemExit):
- dput.dcut.getoptions()
- sys.exit.assert_called_with(self.expected_exit_status)
- def test_returns_expected_values(self):
- """ Should return expected values. """
- if not hasattr(self, 'expected_result'):
- self.skipTest("No return result expected")
- result = dput.dcut.getoptions()
- self.assertEqual(self.expected_result, result)
- class getoptions_DetermineHostTestCase(
- testscenarios.WithScenarios,
- getoptions_TestCase):
- """ Test cases for `getoptions` function, determine host name. """
- system_scenarios = [
- ('domain-from-mailname-file', {
- 'mailname_fake_file': StringIO("consecteur.example.org"),
- }),
- ]
- default_options = getattr(
- getoptions_ParseCommandLineTestCase, 'default_options')
- command_scenarios = [
- ('no-opts no-args', {
- 'getopt_opts': [],
- 'getopt_args': [],
- 'expected_options': {
- 'host': None,
- },
- }),
- ('no-opts command-first-arg', {
- 'getopt_opts': [],
- 'getopt_args': ["cancel"],
- 'expected_options': {
- 'host': None,
- },
- 'expected_arguments': ["cancel"],
- }),
- ('no-opts host-first-arg', {
- 'getopt_opts': [],
- 'getopt_args': ["quux.example.com", "cancel"],
- 'expected_options': {
- 'host': "quux.example.com",
- },
- 'expected_arguments': ["cancel"],
- 'expected_debug_output': textwrap.dedent("""\
- D: first argument "quux.example.com" treated as host
- """),
- }),
- ('option-host host-first-arg', {
- 'getopt_opts': [("--host", "quux.example.com")],
- 'getopt_args': ["decoy.example.net", "cancel"],
- 'expected_options': {
- 'host': "quux.example.com",
- },
- 'expected_arguments': ["decoy.example.net", "cancel"],
- }),
- ]
- scenarios = testscenarios.multiply_scenarios(
- system_scenarios, command_scenarios)
- def test_emits_expected_debug_message(self):
- """ Should emit the expected debug message. """
- if not hasattr(self, 'expected_debug_output'):
- self.expected_debug_output = ""
- self.getopt_opts = list(
- self.getopt_opts + [("--debug", None)])
- dput.dcut.getoptions()
- self.assertIn(self.expected_debug_output, sys.stdout.getvalue())
- def test_returns_expected_values(self):
- """ Should return expected values. """
- if not hasattr(self, 'expected_result'):
- self.skipTest("No return result expected")
- (options, arguments) = dput.dcut.getoptions()
- self.assertEqual(self.expected_options['host'], options['host'])
- self.assertEqual(self.expected_arguments, arguments)
- class parse_queuecommands_TestCase(testtools.TestCase):
- """ Base for test cases for `parse_queuecommands` function. """
- scenarios = NotImplemented
- def setUp(self):
- """ Set up test fixtures. """
- super(parse_queuecommands_TestCase, self).setUp()
- patch_system_interfaces(self)
- self.set_test_args()
- def set_test_args(self):
- """ Set the arguments for the test call to the function. """
- default_options = {
- 'debug': False,
- }
- self.test_args = dict(
- arguments=getattr(self, 'arguments', []),
- options=getattr(self, 'options', default_options),
- config=object(),
- )
- class parse_queuecommands_SuccessTestCase(
- testscenarios.WithScenarios,
- parse_queuecommands_TestCase):
- """ Success test cases for `parse_queuecommands` function. """
- scenarios = [
- ('one-command-rm', {
- 'arguments': ["rm", "lorem.deb"],
- 'expected_commands': [
- "rm --searchdirs lorem.deb",
- ],
- }),
- ('one-command-rm nosearchdirs', {
- 'arguments': ["rm", "--nosearchdirs", "lorem.deb"],
- 'expected_commands': [
- "rm lorem.deb",
- ],
- }),
- ('one-command-cancel', {
- 'arguments': ["cancel", "lorem.deb"],
- 'expected_commands': [
- "cancel lorem.deb",
- ],
- }),
- ('one-command-cancel nosearchdirs', {
- 'arguments': ["cancel", "--nosearchdirs", "lorem.deb"],
- 'expected_commands': [
- "cancel --nosearchdirs lorem.deb",
- ],
- }),
- ('one-command-reschedule', {
- 'arguments': ["reschedule", "lorem.deb"],
- 'expected_commands': [
- "reschedule lorem.deb",
- ],
- }),
- ('one-command-reschedule nosearchdirs', {
- 'arguments': ["reschedule", "--nosearchdirs", "lorem.deb"],
- 'expected_commands': [
- "reschedule --nosearchdirs lorem.deb",
- ],
- }),
- ('three-commands comma-separated', {
- 'arguments': [
- "rm", "foo", ",",
- "cancel", "bar", ",",
- "reschedule", "baz"],
- 'expected_commands': [
- "rm --searchdirs foo ",
- "cancel bar ",
- "reschedule baz",
- ],
- }),
- ('three-commands semicolon-separated', {
- 'arguments': [
- "rm", "foo", ";",
- "cancel", "bar", ";",
- "reschedule", "baz"],
- 'expected_commands': [
- "rm --searchdirs foo ",
- "cancel bar ",
- "reschedule baz",
- ],
- }),
- ]
- def test_emits_debug_message_for_each_command(self):
- """ Should emit a debug message for each command. """
- self.test_args['options'] = dict(self.test_args['options'])
- self.test_args['options']['debug'] = True
- dput.dcut.parse_queuecommands(**self.test_args)
- expected_output = "\n".join(
- "D: Successfully parsed command \"{command}\"".format(
- command=command)
- for command in self.expected_commands)
- self.assertIn(expected_output, sys.stdout.getvalue())
- def test_returns_expected_commands(self):
- """ Should return expected commands value. """
- result = dput.dcut.parse_queuecommands(**self.test_args)
- self.assertEqual(self.expected_commands, result)
- class parse_queuecommands_ErrorTestCase(
- testscenarios.WithScenarios,
- parse_queuecommands_TestCase):
- """ Error test cases for `parse_queuecommands` function. """
- scenarios = [
- ('no-arguments', {
- 'arguments': [],
- 'expected_debug_output': textwrap.dedent("""\
- Error: no arguments given, see dcut -h
- """),
- 'expected_exit_status': EXIT_STATUS_FAILURE,
- }),
- ('first-command-bogus', {
- 'arguments': ["b0gUs", "spam", "eggs"],
- 'expected_debug_output': textwrap.dedent("""\
- Error: Could not parse commands at "b0gUs"
- """),
- 'expected_exit_status': EXIT_STATUS_FAILURE,
- }),
- ('third-command-bogus', {
- 'arguments': ["rm", "foo", ",", "cancel", "bar", ",", "b0gUs"],
- 'expected_debug_output': textwrap.dedent("""\
- Error: Could not parse commands at "b0gUs"
- """),
- 'expected_exit_status': EXIT_STATUS_FAILURE,
- }),
- ]
- def test_emits_expected_error_message(self):
- """ Should emit expected error message. """
- try:
- dput.dcut.parse_queuecommands(**self.test_args)
- except FakeSystemExit:
- pass
- self.assertIn(self.expected_debug_output, sys.stderr.getvalue())
- def test_calls_sys_exit_with_exit_status(self):
- """ Should call `sys.exit` with expected exit status. """
- with testtools.ExpectedException(FakeSystemExit):
- dput.dcut.parse_queuecommands(**self.test_args)
- sys.exit.assert_called_with(self.expected_exit_status)
- class create_commands_TestCase(
- testtools.TestCase):
- """ Test cases for `create_commands` function. """
- def setUp(self):
- """ Set up test fixtures. """
- super(create_commands_TestCase, self).setUp()
- patch_system_interfaces(self)
- self.changes_file_scenarios = make_changes_file_scenarios()
- set_changes_file_scenario(self, 'no-format')
- setup_file_double_behaviour(self)
- self.set_expected_commands()
- self.set_options()
- test_dput_main.patch_parse_changes(self)
- dput.dput.parse_changes.return_value = self.changes_file_scenario[
- 'expected_result']
- self.set_test_args()
- def set_options(self):
- """ Set the options mapping to pass to the function. """
- self.options = {
- 'debug': False,
- 'changes': self.changes_file_double.path,
- }
- def set_test_args(self):
- """ Set the arguments for the test call to the function. """
- self.test_args = dict(
- options=dict(self.options),
- config=object(),
- parse_changes=dput.dput.parse_changes,
- )
- def set_expected_commands(self):
- """ Set the expected commands for this test case. """
- files_to_remove = [os.path.basename(self.changes_file_double.path)]
- files_from_changes = self.changes_file_scenario[
- 'expected_result']['files']
- for line in files_from_changes.split("\n"):
- files_to_remove.append(line.split(" ")[4])
- self.expected_commands = [
- "rm --searchdirs {path}".format(path=path)
- for path in files_to_remove]
- def test_emits_debug_message_for_changes_file(self):
- """ Should emit debug message for changes file. """
- self.options['debug'] = True
- self.set_test_args()
- dput.dcut.create_commands(**self.test_args)
- expected_output = textwrap.dedent("""\
- D: Parsing changes file ({path}) for files to remove
- """).format(path=self.changes_file_double.path)
- self.assertIn(expected_output, sys.stdout.getvalue())
- def test_emits_error_message_when_changes_file_open_error(self):
- """ Should emit error message when changes file raises error. """
- self.changes_file_double.set_open_scenario('read_denied')
- try:
- dput.dcut.create_commands(**self.test_args)
- except FakeSystemExit:
- pass
- expected_output = textwrap.dedent("""\
- Can't open changes file: {path}
- """).format(path=self.changes_file_double.path)
- self.assertIn(expected_output, sys.stdout.getvalue())
- def test_calls_sys_exit_when_changes_file_open_error(self):
- """ Should call `sys.exit` when changes file raises error. """
- self.changes_file_double.set_open_scenario('read_denied')
- with testtools.ExpectedException(FakeSystemExit):
- dput.dcut.create_commands(**self.test_args)
- sys.exit.assert_called_with(EXIT_STATUS_FAILURE)
- def test_returns_expected_result(self):
- """ Should return expected result. """
- result = dput.dcut.create_commands(**self.test_args)
- self.assertEqual(self.expected_commands, result)
- class write_commands_TestCase(
- testscenarios.WithScenarios,
- testtools.TestCase):
- """ Test cases for `write_commands` function. """
- default_options = {
- 'filetocreate': None,
- }
- path_scenarios = [
- ('default-path', {}),
- ('filetocreate', {
- 'option_filetocreate': str("ipsum.commands"),
- 'expected_result': "ipsum.commands",
- }),
- ('no-tempdir', {
- 'tempdir': None,
- }),
- ]
- commands_scenarios = [
- ('commands-none', {
- 'commands': [],
- }),
- ('commands-one', {
- 'commands': ["foo"],
- }),
- ('commands-three', {
- 'commands': ["foo", "bar", "baz"],
- }),
- ]
- keyid_scenarios = [
- ('keyid-none', {}),
- ('keyid-set', {
- 'option_keyid': "DEADBEEF",
- }),
- ]
- scenarios = testscenarios.multiply_scenarios(
- path_scenarios, commands_scenarios, keyid_scenarios)
- for (scenario_name, scenario) in scenarios:
- default_options = getattr(
- getoptions_ParseCommandLineTestCase, 'default_options')
- options = dict(default_options)
- options.update({
- 'uploader': "Lorem Ipsum <flup@example.org>",
- })
- scenario['uploader_filename_part'] = "Lorem_Ipsum__flup_example_org_"
- if 'option_filetocreate' in scenario:
- options['filetocreate'] = scenario['option_filetocreate']
- if 'option_keyid' in scenario:
- options['keyid'] = scenario['option_keyid']
- scenario['options'] = options
- if 'tempdir' not in scenario:
- scenario['tempdir'] = tempfile.mktemp()
- del scenario_name, scenario
- del default_options, options
- def setUp(self):
- """ Set up test fixtures. """
- super(write_commands_TestCase, self).setUp()
- patch_system_interfaces(self)
- patch_os_getpid(self)
- os.getpid.return_value = self.getUniqueInteger()
- self.time_return_value = self.getUniqueInteger()
- patch_time_time(self, itertools.repeat(self.time_return_value))
- patch_sys_argv(self)
- self.set_commands_file_double()
- setup_file_double_behaviour(self)
- self.set_expected_result()
- self.set_commands()
- self.set_test_args()
- patch_subprocess_popen(self)
- patch_os_waitpid(self)
- self.set_debsign_subprocess_double()
- setup_subprocess_double_behaviour(self)
- def set_options(self):
- """ Set the options mapping to pass to the function. """
- def set_test_args(self):
- """ Set the arguments for the test call to the function. """
- self.test_args = dict(
- commands=list(self.commands),
- options=dict(self.options),
- config=object(),
- tempdir=self.tempdir,
- )
- def make_commands_filename(self):
- """ Make the filename for the commands output file. """
- expected_progname = self.progname
- filename = "{progname}.{uploadpart}.{time:d}.{pid:d}.commands".format(
- progname=expected_progname,
- uploadpart=self.uploader_filename_part,
- time=self.time_return_value,
- pid=os.getpid.return_value)
- return filename
- def set_commands_file_double(self):
- """ Set the commands file double for this test case. """
- if self.options['filetocreate']:
- path = self.options['filetocreate']
- else:
- output_filename = self.make_commands_filename()
- if self.tempdir:
- path = os.path.join(self.tempdir, output_filename)
- else:
- path = output_filename
- double = FileDouble(path)
- double.register_for_testcase(self)
- self.commands_file_double = double
- def set_expected_result(self):
- """ Set the `expected_result` for this test case. """
- self.expected_result = self.commands_file_double.path
- def set_commands(self):
- """ Set the commands to use for this test case. """
- if not hasattr(self, 'commands'):
- self.commands = []
- def make_expected_content(self):
- """ Make the expected content for the output file. """
- uploader_value = self.options['uploader']
- if self.commands:
- commands_value = "\n".join(
- " {command}".format(command=command)
- for command in self.commands)
- else:
- commands_value = " "
- commands_value += "\n"
- text = textwrap.dedent("""\
- Uploader: {uploader}
- Commands:
- {commands}
- """).format(uploader=uploader_value, commands=commands_value)
- return text
- def set_debsign_subprocess_double(self):
- """ Set the ‘debsign’ subprocess double for this test case. """
- path = "/usr/bin/debsign"
- argv = [os.path.basename(path), ARG_MORE]
- double = SubprocessDouble(path, argv)
- double.register_for_testcase(self)
- self.debsign_subprocess_double = double
- def make_expected_debsign_argv(self):
- """ Make the expected command-line arguments for ‘debsign’. """
- argv = [
- str("debsign"),
- "-m{uploader}".format(uploader=self.options['uploader']),
- ]
- if self.options['keyid']:
- argv.append(
- "-k{keyid}".format(keyid=self.options['keyid']))
- argv.append(self.commands_file_double.path)
- return argv
- def test_returns_expected_file_path(self):
- """ Should return expected file path. """
- result = dput.dcut.write_commands(**self.test_args)
- self.assertEqual(self.expected_result, result)
- def test_output_file_has_expected_content(self):
- """ Should have expected content in output file. """
- with mock.patch.object(self.commands_file_double.fake_file, 'close'):
- dput.dcut.write_commands(**self.test_args)
- expected_value = self.make_expected_content()
- self.assertEqual(
- expected_value, self.commands_file_double.fake_file.getvalue())
- def test_emits_debug_message_for_debsign(self):
- """ Should emit debug message for ‘debsign’ command. """
- self.options['debug'] = True
- self.test_args['options'] = self.options
- dput.dcut.write_commands(**self.test_args)
- debsign_argv = self.make_expected_debsign_argv()
- expected_output = textwrap.dedent("""\
- D: calling debsign: {argv}
- """).format(argv=debsign_argv)
- self.assertIn(expected_output, sys.stdout.getvalue())
- def test_invokes_debsign_with_expected_args(self):
- """ Should invoke ‘debsign’ command with expected args. """
- debsign_argv = self.make_expected_debsign_argv()
- expected_args = (debsign_argv,)
- dput.dcut.write_commands(**self.test_args)
- subprocess.Popen.assert_called_with(*expected_args)
- def test_calls_os_waitpid_with_expected_args(self):
- """ Should call `os.waitpid` with expected args. """
- expected_args = (self.debsign_subprocess_double.pid, 0)
- dput.dcut.write_commands(**self.test_args)
- os.waitpid.assert_called_with(*expected_args)
- def test_emits_error_message_when_debsign_failure(self):
- """ Should emit error message when ‘debsign’ command failure. """
- self.debsign_subprocess_double.set_os_waitpid_scenario('failure')
- try:
- dput.dcut.write_commands(**self.test_args)
- except FakeSystemExit:
- pass
- expected_output = textwrap.dedent("""\
- Error: debsign failed.
- """)
- self.assertIn(expected_output, sys.stderr.getvalue())
- def test_calls_sys_exit_when_debsign_failure(self):
- """ Should call `sys.exit` when ‘debsign’ command failure. """
- self.debsign_subprocess_double.set_os_waitpid_scenario('failure')
- with testtools.ExpectedException(FakeSystemExit):
- dput.dcut.write_commands(**self.test_args)
- sys.exit.assert_called_with(EXIT_STATUS_FAILURE)
- class upload_TestCase(test_dput_main.main_TestCase):
- """ Base for test cases for `upload_stolen_from_dput_main` function. """
- function_to_test = staticmethod(dput.dcut.upload_stolen_from_dput_main)
- def setUp(self):
- """ Set up test fixtures. """
- super(upload_TestCase, self).setUp()
- self.set_cat_subprocess_double()
- patch_os_system(self)
- patch_tempfile_mkdtemp(self)
- patch_os_rmdir(self)
- patch_getoptions(self)
- def patch_globals(self):
- # The `upload_stolen_from_dput_main` function doesn't need these. """
- pass
- def set_cat_subprocess_double(self):
- """ Set the ‘cat’ subprocess double for this test case. """
- path = "/bin/cat"
- argv = [os.path.basename(path), ARG_ANY]
- double = SubprocessDouble(path, argv)
- double.register_for_testcase(self)
- double.set_os_system_scenario('success')
- self.cat_subprocess_double = double
- def set_test_args(self):
- """ Set the arguments for the test call to the function. """
- self.test_args = dict(
- host=self.test_host,
- upload_methods=self.upload_methods,
- config=self.runtime_config_parser,
- debug=False,
- simulate=False,
- files_to_upload=self.files_to_upload,
- ftp_passive_mode=False,
- )
- if hasattr(self, 'test_args_extra'):
- self.test_args.update(self.test_args_extra)
- def get_upload_method_func(self):
- """ Get the specified upload method. """
- method_name = self.runtime_config_parser.get(self.test_host, 'method')
- method_func = dput.dput.upload_methods[method_name]
- return method_func
- class upload_DebugMessageTestCase(upload_TestCase):
- """ Test cases for `upload_stolen_from_dput_main` debug messages. """
- def test_emits_debug_message_for_discovered_methods(self):
- """ Should emit debug message for discovered upload methods. """
- self.test_args['debug'] = True
- self.function_to_test(**self.test_args)
- expected_output = textwrap.dedent("""\
- D: Default Method: {default_method}
- D: Host Method: {host_method}
- """).format(
- default_method=self.runtime_config_parser.get(
- 'DEFAULT', 'method'),
- host_method=self.runtime_config_parser.get(
- self.test_host, 'method'))
- self.assertIn(expected_output, sys.stdout.getvalue())
- class upload_UnknownUploadMethodTestCase(
- testscenarios.WithScenarios,
- upload_TestCase):
- """ Test cases for `upload_stolen_from_dput_main`, unknown method. """
- scenarios = [
- ('bogus-default-method', {
- 'config_extras': {
- 'default': {
- 'method': "b0gUs",
- },
- },
- 'expected_output': "Unknown upload method: b0gUs",
- 'expected_exit_status': EXIT_STATUS_FAILURE,
- }),
- ('bogus-host-method', {
- 'config_extras': {
- 'host': {
- 'method': "b0gUs",
- },
- },
- 'expected_output': "Unknown upload method: b0gUs",
- 'expected_exit_status': EXIT_STATUS_FAILURE,
- }),
- ]
- def test_emits_error_message_when_unknown_method(self):
- """ Should emit error message when unknown upload method. """
- try:
- self.function_to_test(**self.test_args)
- except FakeSystemExit:
- pass
- self.assertIn(self.expected_output, sys.stderr.getvalue())
- def test_calls_sys_exit_when_unknown_method(self):
- """ Should call `sys.exit` when unknown upload method. """
- with testtools.ExpectedException(FakeSystemExit):
- self.function_to_test(**self.test_args)
- sys.exit.assert_called_with(self.expected_exit_status)
- class upload_DiscoverLoginTestCase(
- testscenarios.WithScenarios,
- upload_TestCase):
- """ Test cases for `upload_stolen_from_dput_main` discovery of login. """
- fallback_login_scenarios = [
- ('login-from-environ', {
- 'os_environ': {
- 'USER': "login-from-environ",
- },
- 'expected_fallback_login': "login-from-environ",
- 'expected_system_uid_debug_message': "",
- }),
- ('login-from-pwd', {
- 'os_getuid_return_value': 42,
- 'pwd_getpwuid_return_value': PasswdEntry(
- *(["login-from-pwd"] + [object()] * 6)),
- 'expected_fallback_login': "login-from-pwd",
- 'expected_system_uid_debug_message': "D: User-ID: 42",
- }),
- ]
- config_login_scenarios = [
- ('config-default-login', {
- 'config_extras': {
- 'default': {
- 'login': "login-from-config-default",
- },
- },
- 'expected_login': "login-from-config-default",
- 'expected_output_template':
- "D: Login to use: {login}",
- }),
- ('config-host-login', {
- 'config_extras': {
- 'host': {
- 'login': "login-from-config-host",
- },
- },
- 'expected_login': "login-from-config-host",
- 'expected_output_template':
- "D: Login to use: {login}",
- }),
- ('config-default-login sentinel', {
- 'config_extras': {
- 'default': {
- 'login': "username",
- },
- },
- 'expected_output_template': (
- "D: Neither host {host} nor default login used."
- " Using {login}"),
- }),
- ('config-host-login sentinel', {
- 'config_extras': {
- 'host': {
- 'login': "username",
- },
- },
- 'expected_output_template': (
- "D: Neither host {host} nor default login used."
- " Using {login}"),
- }),
- ]
- scenarios = testscenarios.multiply_scenarios(
- fallback_login_scenarios, config_login_scenarios)
- for (scenario_name, scenario) in scenarios:
- if 'expected_login' not in scenario:
- scenario['expected_login'] = scenario['expected_fallback_login']
- del scenario_name, scenario
- def test_emits_debug_message_for_system_uid(self):
- """ Should emit a debug message for the system UID. """
- if self.expected_login != self.expected_fallback_login:
- self.skipTest("No fallback in this scenario")
- self.test_args['debug'] = True
- self.function_to_test(**self.test_args)
- expected_output = self.expected_system_uid_debug_message.format(
- uid=self.os_getuid_return_value)
- self.assertIn(expected_output, sys.stdout.getvalue())
- def test_emits_debug_message_for_discovered_login(self):
- """ Should emit a debug message for the discovered login. """
- self.test_args['debug'] = True
- self.function_to_test(**self.test_args)
- expected_output = self.expected_output_template.format(
- login=self.expected_login, host=self.test_host)
- self.assertIn(expected_output, sys.stdout.getvalue())
- def test_calls_upload_method_with_expected_login(self):
- """ Should call upload method function with expected login arg. """
- upload_method_func = get_upload_method_func(self)
- self.function_to_test(**self.test_args)
- upload_method_func.assert_called_with(
- mock.ANY, self.expected_login,
- mock.ANY, mock.ANY, mock.ANY, mock.ANY)
- class upload_SimulateTestCase(
- testscenarios.WithScenarios,
- upload_TestCase):
- """ Test cases for `upload_stolen_from_dput_main`, ‘simulate’ option. """
- scenarios = [
- ('simulate', {
- 'config_default_login': "login-from-config-default",
- 'test_args_extra': {
- 'simulate': True,
- },
- }),
- ('simulate three-files', {
- 'config_default_login': "login-from-config-default",
- 'test_args_extra': {
- 'simulate': True,
- },
- 'files_to_upload': [tempfile.mktemp() for __ in range(3)],
- }),
- ]
- def test_omits_upload_method(self):
- """ Should omit call to upload method function. """
- upload_method_func = get_upload_method_func(self)
- self.function_to_test(**self.test_args)
- self.assertFalse(upload_method_func.called)
- def test_emits_message_for_each_file_to_upload(self):
- """ Should emit a message for each file to upload. """
- self.function_to_test(**self.test_args)
- method = self.runtime_config_parser.get(self.test_host, 'method')
- fqdn = self.runtime_config_parser.get(self.test_host, 'fqdn')
- incoming = self.runtime_config_parser.get(self.test_host, 'incoming')
- expected_output = "\n".join(
- "Uploading with {method}: {path} to {fqdn}:{incoming}".format(
- method=method, path=path,
- fqdn=fqdn, incoming=incoming)
- for path in self.files_to_upload)
- self.assertIn(expected_output, sys.stderr.getvalue())
- def test_calls_cat_for_each_file_to_upload(self):
- """ Should call ‘cat’ for each file to upload. """
- self.function_to_test(**self.test_args)
- expected_calls = [
- mock.call("cat {path}".format(path=path))
- for path in self.files_to_upload]
- os.system.assert_has_calls(expected_calls, any_order=True)
- class upload_UploadMethodTestCase(
- testscenarios.WithScenarios,
- upload_TestCase):
- """ Test cases for `upload_stolen_from_dput_main`, invoking method. """
- method_scenarios = [
- ('method-local', {
- 'config_method': "local",
- 'config_progress_indicator': 23,
- 'expected_args': (
- "localhost", mock.ANY, mock.ANY, mock.ANY, mock.ANY,
- 0),
- }),
- ('method-ftp', {
- 'config_method': "ftp",
- 'config_fqdn': "foo.example.com",
- 'config_passive_ftp': False,
- 'config_progress_indicator': 23,
- 'expected_args': (
- "foo.example.com", mock.ANY, mock.ANY, mock.ANY, mock.ANY,
- False),
- 'expected_stdout_output': "",
- }),
- ('method-ftp port-custom', {
- 'config_method': "ftp",
- 'config_fqdn': "foo.example.com:42",
- 'config_passive_ftp': False,
- 'config_progress_indicator': 23,
- 'expected_args': (
- "foo.example.com:42",
- mock.ANY, mock.ANY, mock.ANY, mock.ANY,
- False),
- 'expected_stdout_output': "",
- }),
- ('method-ftp config-passive-mode', {
- 'config_method': "ftp",
- 'config_fqdn': "foo.example.com",
- 'config_passive_ftp': True,
- 'config_progress_indicator': 23,
- 'expected_args': (
- "foo.example.com", mock.ANY, mock.ANY, mock.ANY, mock.ANY,
- True),
- 'expected_stdout_output': "",
- }),
- ('method-ftp config-passive-mode arg-ftp-active-mode', {
- 'config_method': "ftp",
- 'config_fqdn': "foo.example.com",
- 'config_passive_ftp': True,
- 'config_progress_indicator': 23,
- 'test_args_extra': {
- 'ftp_passive_mode': False,
- },
- 'expected_args': (
- "foo.example.com", mock.ANY, mock.ANY, mock.ANY, mock.ANY,
- True),
- 'expected_stdout_output': "D: Using active ftp",
- }),
- ('method-ftp arg-ftp-passive-mode', {
- 'config_method': "ftp",
- 'config_fqdn': "foo.example.com",
- 'config_progress_indicator': 23,
- 'test_args_extra': {
- 'ftp_passive_mode': True,
- },
- 'expected_args': (
- "foo.example.com", mock.ANY, mock.ANY, mock.ANY, mock.ANY,
- True),
- 'expected_stdout_output': "D: Using passive ftp",
- }),
- ('method-ftp config-passive-mode arg-ftp-passive-mode', {
- 'config_method': "ftp",
- 'config_fqdn': "foo.example.com",
- 'config_passive_ftp': True,
- 'config_progress_indicator': 23,
- 'test_args_extra': {
- 'ftp_passive_mode': True,
- },
- 'expected_args': (
- "foo.example.com", mock.ANY, mock.ANY, mock.ANY, mock.ANY,
- True),
- 'expected_stdout_output': "D: Using passive ftp",
- }),
- ('method-scp', {
- 'config_method': "scp",
- 'config_fqdn': "foo.example.com",
- 'expected_args': (
- "foo.example.com", mock.ANY, mock.ANY, mock.ANY, mock.ANY,
- False, []),
- 'expected_stdout_output': "",
- }),
- ('method-scp scp-compress', {
- 'config_method': "scp",
- 'config_fqdn': "foo.example.com",
- 'config_extras': {
- 'host': {
- 'scp_compress': "True",
- 'ssh_config_options': "spam eggs beans",
- },
- },
- 'expected_args': (
- "foo.example.com", mock.ANY, mock.ANY, mock.ANY, mock.ANY,
- True, ["spam eggs beans"]),
- 'expected_stdout_output': "D: Setting compression for scp",
- }),
- ]
- login_scenarios = [
- ('default-login', {
- 'config_default_login': "login-from-config-default",
- }),
- ]
- commands_scenarios = [
- ('commands-from-changes', {
- 'getoptions_args': ["foo", "bar", "baz"],
- 'getoptions_opts': {
- 'filetocreate': None,
- 'filetoupload': tempfile.mktemp() + "commands",
- },
- }),
- ('commands-from-changes', {
- 'getoptions_args': ["foo", "bar", "baz"],
- 'getoptions_opts': {
- 'filetocreate': None,
- 'filetoupload': None,
- 'changes': tempfile.mktemp(),
- },
- }),
- ('commands-from-arguments', {
- 'getoptions_args': ["foo", "bar", "baz"],
- 'getoptions_opts': {
- 'filetocreate': None,
- 'filetoupload': None,
- 'changes': None,
- },
- }),
- ]
- files_scenarios = [
- ('no-files', {
- 'files_to_remove': [],
- }),
- ('three-files', {
- 'files_to_remove': [
- tempfile.mktemp() for __ in range(3)],
- }),
- ]
- scenarios = testscenarios.multiply_scenarios(
- method_scenarios, login_scenarios,
- commands_scenarios, files_scenarios)
- def test_emits_expected_debug_message(self):
- """ Should emit expected debug message. """
- self.test_args['debug'] = True
- self.function_to_test(**self.test_args)
- if hasattr(self, 'expected_stdout_output'):
- self.assertIn(self.expected_stdout_output, sys.stdout.getvalue())
- def test_calls_upload_method_with_expected_args(self):
- """ Should call upload method function with expected args. """
- upload_method_func = get_upload_method_func(self)
- self.function_to_test(**self.test_args)
- upload_method_func.assert_called_with(*self.expected_args)
- class dcut_TestCase(testtools.TestCase):
- """ Base for test cases for `dput` function. """
- def setUp(self):
- """ Set up test fixtures. """
- super(dcut_TestCase, self).setUp()
- patch_system_interfaces(self)
- patch_tempfile_mkdtemp(self)
- patch_os_unlink(self)
- patch_os_rmdir(self)
- patch_shutil_rmtree(self)
- set_config(
- self,
- getattr(self, 'config_scenario_name', 'exist-simple'))
- test_dput_main.patch_runtime_config_options(self)
- self.set_test_args()
- patch_getoptions(self)
- test_dput_main.patch_parse_changes(self)
- test_dput_main.patch_read_configs(self)
- test_dput_main.patch_upload_methods(self)
- test_dput_main.patch_import_upload_functions(self)
- self.patch_parse_queuecommands()
- self.patch_create_commands()
- self.patch_write_commands()
- self.patch_upload_stolen_from_dput_main()
- def set_test_args(self):
- """ Set the arguments for the test call to the function. """
- self.test_args = dict()
- def patch_parse_queuecommands(self):
- """ Patch the `parse_queuecommands` function for this test case. """
- func_patcher = mock.patch.object(dput.dcut, "parse_queuecommands")
- func_patcher.start()
- self.addCleanup(func_patcher.stop)
- def patch_create_commands(self):
- """ Patch the `create_commands` function for this test case. """
- func_patcher = mock.patch.object(dput.dcut, "create_commands")
- func_patcher.start()
- self.addCleanup(func_patcher.stop)
- def patch_write_commands(self):
- """ Patch the `write_commands` function for this test case. """
- func_patcher = mock.patch.object(dput.dcut, "write_commands")
- func_patcher.start()
- self.addCleanup(func_patcher.stop)
- def patch_upload_stolen_from_dput_main(self):
- """ Patch `upload_stolen_from_dput_main` for this test case. """
- func_patcher = mock.patch.object(
- dput.dcut, "upload_stolen_from_dput_main")
- func_patcher.start()
- self.addCleanup(func_patcher.stop)
- class dcut_DebugMessageTestCase(dcut_TestCase):
- """ Test cases for `dcut` debug messages. """
- def test_emits_debug_message_for_read_configs(self):
- """ Should emit debug message for `read_configs` call. """
- self.getoptions_opts['debug'] = True
- dput.dcut.dcut(**self.test_args)
- expected_output = textwrap.dedent("""\
- D: calling dput.read_configs
- """)
- self.assertIn(expected_output, sys.stdout.getvalue())
- class dcut_ConfigFileTestCase(
- testscenarios.WithScenarios,
- dcut_TestCase):
- """ Test cases for `main` specification of configuration file. """
- scenarios = [
- ('default', {
- 'expected_args': (None, mock.ANY),
- }),
- ('config-from-command-line', {
- 'getoptions_opts': {
- 'config': "lorem.conf",
- },
- 'expected_args': ("lorem.conf", mock.ANY),
- }),
- ]
- def test_calls_read_configs_with_expected_args(self):
- """ Should call `read_configs` with expected arguments. """
- dput.dcut.dcut(**self.test_args)
- dput.dput.read_configs.assert_called_with(*self.expected_args)
- class dcut_OptionsErrorTestCase(
- testscenarios.WithScenarios,
- dcut_TestCase):
- """ Test cases for `dcut` function, startup options cause error. """
- scenarios = [
- ('no-host-discovered', {
- 'config_default_default_host_main': None,
- 'getoptions_opts': {
- 'host': None,
- },
- 'expected_output': (
- "Error: No host specified"
- " and no default found in config"),
- 'expected_exit_status': EXIT_STATUS_FAILURE,
- }),
- ('host-not-in-config', {
- 'config_scenario_name': "exist-minimal",
- 'expected_output': "No host foo found in config",
- 'expected_exit_status': EXIT_STATUS_FAILURE,
- }),
- ('no-allow-dcut', {
- 'config_allow_dcut': False,
- 'expected_output': (
- "Error: dcut is not supported for this upload queue."),
- 'expected_exit_status': EXIT_STATUS_FAILURE,
- }),
- ('filetoupload arguments', {
- 'getoptions_opts': {
- 'filetoupload': tempfile.mktemp() + ".commands",
- },
- 'getoptions_args': ["lorem", "ipsum", "dolor", "sit", "amet"],
- 'expected_output': (
- "Error: cannot take commands"
- " when uploading existing file"),
- 'expected_exit_status': EXIT_STATUS_FAILURE,
- }),
- ]
- def test_emits_expected_error_message(self):
- """ Should emit expected error message. """
- try:
- dput.dcut.dcut(**self.test_args)
- except FakeSystemExit:
- pass
- self.assertIn(self.expected_output, sys.stdout.getvalue())
- def test_calls_sys_exit_with_failure_exit_status(self):
- """ Should call `sys.exit` with failure exit status. """
- with testtools.ExpectedException(FakeSystemExit):
- dput.dcut.dcut(**self.test_args)
- sys.exit.assert_called_with(self.expected_exit_status)
- class dcut_NamedHostTestCase(
- testscenarios.WithScenarios,
- dcut_TestCase):
- """ Test cases for `dcut` function, named host processing. """
- scenarios = [
- ('host-from-command-line', {
- 'config_scenario_name': "exist-simple-host-three",
- 'config_default_default_host_main': "quux",
- 'getoptions_opts': {
- 'host': "bar",
- },
- 'expected_host': "bar",
- 'expected_debug_output': "",
- }),
- ('host-from-config-default', {
- 'config_scenario_name': "exist-simple-host-three",
- 'config_default_default_host_main': "bar",
- 'getoptions_opts': {
- 'host': None,
- },
- 'expected_host': "bar",
- 'expected_debug_output': textwrap.dedent("""\
- D: Using host "bar" (default_host_main)
- """),
- }),
- ('host-from-hardcoded-default', {
- 'config_scenario_name': "exist-default-distribution-only",
- 'config_default_default_host_main': "",
- 'getoptions_opts': {
- 'host': None,
- },
- 'expected_host': "ftp-master",
- 'expected_debug_output': textwrap.dedent("""\
- D: Using host "" (default_host_main)
- D: Using host "ftp-master" (hardcoded)
- """),
- }),
- ]
- def test_emits_debug_message_for_discovered_host(self):
- """ Should emit debug message for discovered host values. """
- self.getoptions_opts['debug'] = True
- dput.dcut.dcut(**self.test_args)
- self.assertIn(self.expected_debug_output, sys.stdout.getvalue())
- def test_calls_write_commands_with_expected_host_option(self):
- """ Should call `write_commands` with expected `host` option. """
- dput.dcut.dcut(**self.test_args)
- self.assertEqual(1, len(dput.dcut.write_commands.mock_calls))
- (__, call_args, call_kwargs) = dput.dcut.write_commands.mock_calls[0]
- (__, options, __, __) = call_args
- self.assertEqual(self.expected_host, options['host'])
- class dcut_FileToUploadBadNameTestCase(
- testscenarios.WithScenarios,
- dcut_TestCase):
- """ Test cases for `dcut` function, file to upload with bad name. """
- scenarios = [
- ('filetoupload', {
- 'getoptions_args': [],
- 'getoptions_opts': {
- 'filetoupload': tempfile.mktemp(),
- },
- 'expected_output': (
- "Error: I'm insisting on the .commands extension"),
- }),
- ]
- def test_emits_error_message_for_bad_filename(self):
- """ Should emit error message for bad filename. """
- dput.dcut.dcut(**self.test_args)
- self.assertIn(self.expected_output, sys.stdout.getvalue())
- class dcut_ParseChangesTestCase(
- testscenarios.WithScenarios,
- dcut_TestCase):
- """ Test cases for `dcut` function, parse upload control file. """
- scenarios = [
- ('changes-file no-filetoupload', {
- 'getoptions_opts': {
- 'filetoupload': None,
- 'changes': tempfile.mktemp(),
- },
- }),
- ('changes-file no-filetoupload no-filetocreate', {
- 'getoptions_opts': {
- 'filetoupload': None,
- 'filetocreate': None,
- 'changes': tempfile.mktemp(),
- },
- }),
- ]
- def test_calls_create_commands_with_expected_args(self):
- """ Should call `create_commands` with expected args. """
- dput.dcut.dcut(**self.test_args)
- (expected_options, __) = dput.dcut.getoptions()
- expected_config = self.runtime_config_parser
- expected_parse_changes = dput.dput.parse_changes
- dput.dcut.create_commands.assert_called_with(
- expected_options, expected_config, expected_parse_changes)
- def test_calls_write_commands_with_expected_args(self):
- """ Should call `write_commands` with expected args. """
- expected_commands = object()
- dput.dcut.create_commands.return_value = expected_commands
- dput.dcut.dcut(**self.test_args)
- (expected_options, __) = dput.dcut.getoptions()
- expected_config = self.runtime_config_parser
- expected_tempdir = self.tempfile_mkdtemp_file_double.path
- dput.dcut.write_commands.assert_called_with(
- expected_commands, expected_options, expected_config,
- expected_tempdir)
- class dcut_ParseQueueCommandsTestCase(
- testscenarios.WithScenarios,
- dcut_TestCase):
- """ Test cases for `dcut` function, parse commands from arguments. """
- scenarios = [
- ('no-changes-file no-filetoupload', {
- 'getoptions_opts': {
- 'filetoupload': None,
- 'changes': None,
- },
- }),
- ]
- def test_calls_parse_queuecommands_with_expected_args(self):
- """ Should call `parse_queuecommands` with expected args. """
- dput.dcut.dcut(**self.test_args)
- (expected_options, expected_arguments) = dput.dcut.getoptions()
- expected_config = self.runtime_config_parser
- dput.dcut.parse_queuecommands.assert_called_with(
- expected_arguments, expected_options, expected_config)
- def test_calls_write_commands_with_expected_args(self):
- """ Should call `write_commands` with expected args. """
- expected_commands = object()
- dput.dcut.parse_queuecommands.return_value = expected_commands
- dput.dcut.dcut(**self.test_args)
- (expected_options, __) = dput.dcut.getoptions()
- expected_config = self.runtime_config_parser
- expected_tempdir = self.tempfile_mkdtemp_file_double.path
- dput.dcut.write_commands.assert_called_with(
- expected_commands, expected_options, expected_config,
- expected_tempdir)
- class dcut_CleanupTestCase(
- testscenarios.WithScenarios,
- dcut_TestCase):
- """ Test cases for `dcut` function, cleanup from exception. """
- commands_scenarios = [
- ('commands-from-arguments', {
- 'getoptions_args': ["foo", "bar", "baz"],
- 'getoptions_opts': {
- 'filetocreate': None,
- 'filetoupload': None,
- 'changes': None,
- },
- }),
- ]
- files_scenarios = upload_UploadMethodTestCase.files_scenarios
- scenarios = testscenarios.multiply_scenarios(
- commands_scenarios, files_scenarios)
- def setUp(self):
- """ Set up test fixtures. """
- super(dcut_CleanupTestCase, self).setUp()
- upload_method_func = get_upload_method_func(self)
- self.upload_error = RuntimeError("Bad stuff happened")
- upload_method_func.side_effect = self.upload_error
- def test_removes_temporary_directory_when_upload_raises_exception(self):
- """ Should remove directory `tempdir` when exception raised. """
- try:
- dput.dcut.dcut(**self.test_args)
- except self.upload_error.__class__:
- pass
- shutil.rmtree.assert_called_with(
- self.tempfile_mkdtemp_file_double.path)
- # Local variables:
- # coding: utf-8
- # mode: python
- # End:
- # vim: fileencoding=utf-8 filetype=python :
|