From 8b855517dc1ce0cc010a60a719a4ad1e912faca6 Mon Sep 17 00:00:00 2001 From: Ben Finney Date: Thu, 27 Aug 2015 16:19:13 +1000 Subject: [PATCH] =?utf8?q?Add=20unit=20tests=20for=20=E2=80=98dput.dput.ve?= =?utf8?q?rsion=5Fcheck=E2=80=99.?= MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit Signed-off-by: Ben Finney --- test/test_dput.py | 395 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 395 insertions(+) diff --git a/test/test_dput.py b/test/test_dput.py index c228360..0544f5c 100644 --- a/test/test_dput.py +++ b/test/test_dput.py @@ -49,6 +49,7 @@ from .helper import ( FileDouble, ARG_ANY, SubprocessDouble, + patch_subprocess_popen, patch_os_system, patch_os_popen, patch_os_spawnv, @@ -58,6 +59,10 @@ from .test_configfile import ( ) from .test_changesfile import ( set_fake_upload_file_paths, + setup_upload_file_fixtures, + make_upload_files_params, + make_changes_document, + make_changes_file_scenarios, setup_changes_file_fixtures, set_changes_file_scenario, ) @@ -975,6 +980,396 @@ class execute_command_TestCase( dput.dput.execute_command(**self.test_args) +class version_check_TestCase( + testscenarios.WithScenarios, + testtools.TestCase): + """ Base for test cases for `version_check` function. """ + + package_file_scenarios = [ + ('package-arch {arch}'.format(arch=arch), { + 'file_double': FileDouble(), + 'field_output': textwrap.dedent("""\ + Architecture: {arch} + """).format(arch=arch), + }) + for arch in ["all", "foo", "bar", "baz"]] + + version_scenarios = [ + ('version none', { + 'upload_version': "lorem", + }), + ('version equal', { + 'upload_version': "lorem", + 'installed_version': "lorem", + }), + ('version unequal', { + 'upload_version': "lorem", + 'installed_version': "ipsum", + }), + ] + + scenarios = NotImplemented + + def setUp(self): + """ Set up test fixtures. """ + super(version_check_TestCase, self).setUp() + patch_system_interfaces(self) + + self.patch_dpkg_program_file() + + if not hasattr(self, 'test_architectures'): + self.test_architectures = [] + + setup_changes_file_fixtures(self) + self.set_changes_file_scenario('no-format') + + self.set_upload_files() + self.set_changes_document() + + self.set_test_args() + + patch_subprocess_popen(self) + self.set_dpkg_print_architecture_scenario('simple') + self.set_dpkg_field_scenario('simple') + self.set_dpkg_status_scenario('simple') + + def set_changes_file_scenario(self, name): + """ Set the package changes document based on scenario name. """ + scenarios = make_changes_file_scenarios() + scenario = dict(scenarios)[name] + self.changes_file_double = scenario['file_double'] + + def set_upload_files(self): + """ Set the files marked for upload in this scenario. """ + package_file_suffix = ".deb" + file_suffix_by_arch = { + arch: "_{arch}{suffix}".format( + arch=arch, suffix=package_file_suffix) + for arch in self.test_architectures} + self.additional_file_suffixes = list(file_suffix_by_arch.values()) + + setup_upload_file_fixtures(self) + + registry = FileDouble.get_registry_for_testcase(self) + for path in self.fake_upload_file_paths: + for (arch, suffix) in file_suffix_by_arch.items(): + if path.endswith(suffix): + file_double = registry[path] + package_file_scenario = dict(self.package_file_scenarios)[ + "package-arch {arch}".format(arch=arch)] + package_file_scenario['file_double'] = file_double + + def set_changes_document(self): + """ Set the changes document for this test case. """ + upload_params_by_name = make_upload_files_params( + self.fake_checksum_by_file, self.fake_size_by_file) + self.changes_document = make_changes_document( + fields={ + 'architecture': " ".join(self.test_architectures), + }, + upload_params_by_name=upload_params_by_name) + + def set_test_args(self): + """ Set the arguments for the test call to the function. """ + self.test_args = dict( + path=os.path.dirname(self.changes_file_double.path), + changes=self.changes_document, + debug=False, + ) + + def patch_dpkg_program_file(self): + """ Patch the Dpkg program file for this test case. """ + file_path = '/usr/bin/dpkg' + file_double = FileDouble(file_path) + file_double.register_for_testcase(self) + self.dpkg_program_file_double = file_double + + def set_dpkg_print_architecture_scenario(self, name): + """ Set the scenario for ‘dpkg --print-architecture’ behaviour. """ + argv = ( + os.path.basename(self.dpkg_program_file_double.path), + "--print-architecture") + double = SubprocessDouble( + self.dpkg_program_file_double.path, argv=argv) + double.register_for_testcase(self) + double.set_subprocess_popen_scenario('success') + double.set_stdout_content(self.host_architecture) + self.dpkg_print_architecture_subprocess_double = double + + def set_dpkg_field_scenario(self, name): + """ Set the scenario for ‘dpkg --field’ behaviour. """ + for arch in self.test_architectures: + package_file_scenario = dict(self.package_file_scenarios)[ + 'package-arch {arch}'.format(arch=arch)] + upload_file_path = package_file_scenario['file_double'].path + argv = ( + os.path.basename(self.dpkg_program_file_double.path), + "--field", upload_file_path) + double = SubprocessDouble( + self.dpkg_program_file_double.path, argv=argv) + double.register_for_testcase(self) + double.set_subprocess_popen_scenario('success') + + package_file_scenario['package_name'] = make_unique_slug(self) + package_file_scenario['package_version'] = self.upload_version + field_output = package_file_scenario['field_output'] + field_output += textwrap.dedent("""\ + Package: {name} + Version: {version} + """).format( + name=package_file_scenario['package_name'], + version=package_file_scenario['package_version']) + double.set_stdout_content(field_output) + + def set_dpkg_status_scenario(self, name): + """ Set the scenario for ‘dpkg -s’ behaviour. """ + for arch in self.test_architectures: + package_file_scenario = dict(self.package_file_scenarios)[ + 'package-arch {arch}'.format(arch=arch)] + argv = ( + os.path.basename(self.dpkg_program_file_double.path), + "-s", package_file_scenario['package_name']) + double = SubprocessDouble( + self.dpkg_program_file_double.path, argv=argv) + double.register_for_testcase(self) + double.set_subprocess_popen_scenario('success') + + version_field = "" + if hasattr(self, 'installed_version'): + version_field = "Version: {version}\n".format( + version=self.installed_version) + status_output = textwrap.dedent("""\ + {version}""").format(version=version_field) + double.set_stdout_content(status_output) + + def get_subprocess_doubles_matching_argv_prefix(self, argv_prefix): + """ Get subprocess doubles for this test case matching the argv. """ + subprocess_registry = SubprocessDouble.get_registry_for_testcase(self) + subprocess_doubles = [ + double for double in subprocess_registry.values() + if double.argv[:len(argv_prefix)] == argv_prefix + ] + return subprocess_doubles + + +class version_check_ArchitectureMatchTestCase(version_check_TestCase): + """ Test cases for `version_check` when host architecture matches. """ + + host_architecture_scenarios = [ + ('host-arch foo', { + 'host_architecture': "foo", + }), + ] + + package_architecture_scenarios = [ + ('one binary', { + 'test_architectures': ["foo"], + }), + ('three binaries', { + 'test_architectures': ["foo", "bar", "baz"], + }), + ('all-arch binary', { + 'test_architectures': ["all"], + }), + ] + + scenarios = testscenarios.multiply_scenarios( + host_architecture_scenarios, + package_architecture_scenarios, + version_check_TestCase.version_scenarios) + + def test_emits_debug_message_showing_architecture(self): + """ Should emit a debug message for the specified architecture. """ + test_architecture = self.getUniqueString() + subprocess_double = self.dpkg_print_architecture_subprocess_double + subprocess_double.stdout_double.fake_file = StringIO( + "{arch}\n".format(arch=test_architecture)) + self.test_args['debug'] = True + try: + dput.dput.version_check(**self.test_args) + except FakeSystemExit: + pass + expected_output = textwrap.dedent("""\ + D: detected architecture: '{arch}' + """).format(arch=test_architecture) + self.assertIn(expected_output, sys.stdout.getvalue()) + + def test_omits_stderr_output_message_when_not_debug(self): + """ Should omit any debug messages for `stderr` output. """ + doubles = self.get_subprocess_doubles_matching_argv_prefix( + ("dpkg", "--print-architecture")) + if self.test_architectures: + doubles.extend( + self.get_subprocess_doubles_matching_argv_prefix( + ("dpkg", "--field"))) + doubles.extend( + self.get_subprocess_doubles_matching_argv_prefix( + ("dpkg", "-s"))) + for double in doubles: + double.set_stderr_content(self.getUniqueString()) + self.test_args['debug'] = False + try: + dput.dput.version_check(**self.test_args) + except FakeSystemExit: + pass + message_snippet = " stderr output:" + self.assertNotIn(message_snippet, sys.stdout.getvalue()) + + def test_emits_debug_message_for_architecture_stderr_output(self): + """ Should emit debug message for Dpkg architecture `stderr`. """ + subprocess_double = self.dpkg_print_architecture_subprocess_double + test_output = self.getUniqueString() + subprocess_double.stderr_double.fake_file = StringIO(test_output) + self.test_args['debug'] = True + try: + dput.dput.version_check(**self.test_args) + except FakeSystemExit: + pass + expected_output = textwrap.dedent("""\ + D: dpkg-architecture stderr output: {output!r} + """).format(output=test_output) + self.assertIn(expected_output, sys.stdout.getvalue()) + + def test_emits_debug_message_for_field_stderr_output(self): + """ Should emit debug message for Dpkg fields `stderr`. """ + test_output = self.getUniqueString() + for double in self.get_subprocess_doubles_matching_argv_prefix( + ("dpkg", "--field")): + double.set_stderr_content(test_output) + self.test_args['debug'] = True + try: + dput.dput.version_check(**self.test_args) + except FakeSystemExit: + pass + expected_output = textwrap.dedent("""\ + D: dpkg stderr output: {output!r} + """).format(output=test_output) + self.assertIn(expected_output, sys.stdout.getvalue()) + + def test_emits_debug_message_for_status_stderr_output(self): + """ Should emit debug message for Dpkg status `stderr`. """ + test_output = self.getUniqueString() + for double in self.get_subprocess_doubles_matching_argv_prefix( + ("dpkg", "-s")): + double.set_stderr_content(test_output) + self.test_args['debug'] = True + try: + dput.dput.version_check(**self.test_args) + except FakeSystemExit: + pass + expected_output = textwrap.dedent("""\ + D: dpkg stderr output: {output!r} + """).format(output=test_output) + self.assertIn(expected_output, sys.stdout.getvalue()) + + def test_emits_expected_debug_message_for_installed_version(self): + """ Should emit debug message for package installed version. """ + self.test_args['debug'] = True + message_lead = "D: Installed-Version:" + if hasattr(self, 'installed_version'): + dput.dput.version_check(**self.test_args) + expected_output = "{lead} {version}".format( + lead=message_lead, version=self.installed_version) + self.assertIn(expected_output, sys.stdout.getvalue()) + else: + with testtools.ExpectedException(FakeSystemExit): + dput.dput.version_check(**self.test_args) + self.assertNotIn(message_lead, sys.stdout.getvalue()) + + def test_emits_expected_debug_message_for_upload_version(self): + """ Should emit debug message for package upload version. """ + self.test_args['debug'] = True + message_lead = "D: Check-Version:" + if hasattr(self, 'installed_version'): + dput.dput.version_check(**self.test_args) + expected_output = "{lead} {version}".format( + lead=message_lead, version=self.upload_version) + self.assertIn(expected_output, sys.stdout.getvalue()) + else: + with testtools.ExpectedException(FakeSystemExit): + dput.dput.version_check(**self.test_args) + self.assertNotIn(message_lead, sys.stdout.getvalue()) + + +class version_check_ArchitectureMismatchTestCase(version_check_TestCase): + """ Test cases for `version_check` when no match to host architecture. """ + + host_architecture_scenarios = [ + ('host-arch spam', { + 'host_architecture': "spam", + }), + ] + + package_architecture_scenarios = [ + ('one binary', { + 'test_architectures': ["foo"], + }), + ] + + scenarios = testscenarios.multiply_scenarios( + host_architecture_scenarios, + package_architecture_scenarios, + version_check_TestCase.version_scenarios) + + def test_emits_debug_message_stating_arch_mismatch(self): + """ Should emit a debug message stating the architecture mismatch. """ + self.test_args['debug'] = True + dput.dput.version_check(**self.test_args) + file_scenarios = dict(self.package_file_scenarios) + for arch in self.test_architectures: + file_scenario_name = "package-arch {arch}".format(arch=arch) + file_scenario = file_scenarios[file_scenario_name] + file_double = file_scenario['file_double'] + expected_output = textwrap.dedent("""\ + D: not install-checking {path} due to arch mismatch + """).format(path=file_double.path) + self.expectThat( + sys.stdout.getvalue(), + testtools.matchers.Contains(expected_output)) + + +class version_check_PackageNotInstalledTestCase(version_check_TestCase): + """ Test cases for `version_check` when package is not installed. """ + + host_architecture_scenarios = [ + ('host-arch foo', { + 'host_architecture': "foo", + }), + ] + + package_architecture_scenarios = [ + ('one binary', { + 'test_architectures': ["foo"], + }), + ] + + version_scenarios = [ + ('version none', { + 'upload_version': "lorem", + }), + ] + + scenarios = testscenarios.multiply_scenarios( + host_architecture_scenarios, + package_architecture_scenarios, + version_scenarios) + + def test_emits_message_stating_package_not_installed(self): + """ Should emit message stating package is not installed. """ + with testtools.ExpectedException(FakeSystemExit): + dput.dput.version_check(**self.test_args) + expected_output = textwrap.dedent("""\ + Uninstalled Package. Test it before uploading it. + """) + self.assertIn(expected_output, sys.stdout.getvalue()) + + def test_calls_sys_exit_with_error_status(self): + """ Should call `sys.exit` with exit status indicating error. """ + with testtools.ExpectedException(FakeSystemExit): + dput.dput.version_check(**self.test_args) + sys.exit.assert_called_with(EXIT_STATUS_FAILURE) + + # Local variables: # coding: utf-8 # mode: python -- 2.11.4.GIT