diff --git a/test/sanity/code-smell/mypy.json b/test/sanity/code-smell/mypy.json index 57a6ad6c15b..c8cd6fb8f5e 100644 --- a/test/sanity/code-smell/mypy.json +++ b/test/sanity/code-smell/mypy.json @@ -3,11 +3,13 @@ "lib/ansible/", "test/lib/ansible_test/_internal/", "packaging/", + "test/units", "test/lib/ansible_test/_util/target/sanity/import/" ], "extensions": [ ".py" ], "multi_version": "controller", + "error_code": "ansible-test", "output": "path-line-column-code-message" } diff --git a/test/sanity/code-smell/mypy.py b/test/sanity/code-smell/mypy.py index fda83e8b0da..b7feffc5032 100644 --- a/test/sanity/code-smell/mypy.py +++ b/test/sanity/code-smell/mypy.py @@ -36,6 +36,8 @@ def main() -> None: MyPyContext('ansible-core', ['lib/ansible/'], controller_python_versions), MyPyContext('modules', ['lib/ansible/modules/', 'lib/ansible/module_utils/'], remote_only_python_versions), MyPyContext('packaging', ['packaging/'], controller_python_versions), + MyPyContext('modules', ['test/units/modules/', 'test/units/module_utils/'], remote_only_python_versions), + MyPyContext('ansible-core', ['test/units/'], controller_python_versions), ) unfiltered_messages: list[SanityMessage] = [] diff --git a/test/sanity/code-smell/mypy.requirements.in b/test/sanity/code-smell/mypy.requirements.in index 073513bdf8b..2c6dee12676 100644 --- a/test/sanity/code-smell/mypy.requirements.in +++ b/test/sanity/code-smell/mypy.requirements.in @@ -2,6 +2,7 @@ mypy cryptography # type stubs not published separately jinja2 # type stubs not published separately packaging # type stubs not published separately +pytest # type stubs not published separately tomli # type stubs not published separately, required for toml inventory plugin types-backports types-paramiko diff --git a/test/sanity/code-smell/mypy.requirements.txt b/test/sanity/code-smell/mypy.requirements.txt index c5a3ece72cd..59a253d9f9e 100644 --- a/test/sanity/code-smell/mypy.requirements.txt +++ b/test/sanity/code-smell/mypy.requirements.txt @@ -1,12 +1,15 @@ # edit "mypy.requirements.in" and generate with: hacking/update-sanity-requirements.py --test mypy cffi==1.17.1 cryptography==43.0.1 +iniconfig==2.0.0 Jinja2==3.1.4 MarkupSafe==2.1.5 mypy==1.11.2 mypy-extensions==1.0.0 packaging==24.1 +pluggy==1.5.0 pycparser==2.22 +pytest==8.3.3 tomli==2.0.2 types-backports==0.1.3 types-paramiko==3.5.0.20240928 diff --git a/test/sanity/code-smell/mypy/ansible-core.ini b/test/sanity/code-smell/mypy/ansible-core.ini index 0d2208be2dd..83b63e52eea 100644 --- a/test/sanity/code-smell/mypy/ansible-core.ini +++ b/test/sanity/code-smell/mypy/ansible-core.ini @@ -11,6 +11,16 @@ strict_optional = False # The safe-super rule is disabled because it reports false positives on methods which return None. disable_error_code = attr-defined,safe-super +# Some controller unit tests use ansible_collections imports, both real and test-specific. +# The real imports are not currently visible to mypy. +# There's little point in exposing the test-specific imports. +[mypy-ansible_collections.*] +ignore_missing_imports = True + +# Some unit tests for ansible-test use ansible_test imports, which are not currently visible to mypy. +[mypy-ansible_test.*] +ignore_missing_imports = True + [mypy-ansible.module_utils.six.moves.*] ignore_missing_imports = True diff --git a/test/sanity/ignore.txt b/test/sanity/ignore.txt index 17f893aabf0..926fb4bf929 100644 --- a/test/sanity/ignore.txt +++ b/test/sanity/ignore.txt @@ -164,3 +164,72 @@ lib/ansible/module_utils/pycompat24.py pylint:ansible-deprecated-version lib/ansible/plugins/connection/__init__.py pylint:ansible-deprecated-version lib/ansible/plugins/filter/core.py pylint:ansible-deprecated-version lib/ansible/vars/manager.py pylint:ansible-deprecated-version +test/units/module_utils/basic/test_exit_json.py mypy-3.13:assignment +test/units/module_utils/basic/test_exit_json.py mypy-3.13:misc +test/units/module_utils/common/text/converters/test_json_encode_fallback.py mypy-3.13:abstract +test/units/module_utils/facts/other/test_facter.py mypy-3.13:assignment +test/units/module_utils/facts/other/test_ohai.py mypy-3.13:assignment +test/units/module_utils/facts/system/test_lsb.py mypy-3.13:assignment +test/units/module_utils/facts/test_collectors.py mypy-3.13:assignment +test/units/module_utils/facts/test_facts.py mypy-3.13:assignment +test/units/modules/mount_facts_data.py mypy-3.13:arg-type +test/units/modules/test_apt.py mypy-3.13:name-match +test/units/modules/test_mount_facts.py mypy-3.13:index +test/units/playbook/test_base.py mypy-3.13:assignment +test/units/module_utils/basic/test_exit_json.py mypy-3.12:assignment +test/units/module_utils/basic/test_exit_json.py mypy-3.12:misc +test/units/module_utils/common/text/converters/test_json_encode_fallback.py mypy-3.12:abstract +test/units/module_utils/facts/other/test_facter.py mypy-3.12:assignment +test/units/module_utils/facts/other/test_ohai.py mypy-3.12:assignment +test/units/module_utils/facts/system/test_lsb.py mypy-3.12:assignment +test/units/module_utils/facts/test_collectors.py mypy-3.12:assignment +test/units/module_utils/facts/test_facts.py mypy-3.12:assignment +test/units/modules/mount_facts_data.py mypy-3.12:arg-type +test/units/modules/test_apt.py mypy-3.12:name-match +test/units/modules/test_mount_facts.py mypy-3.12:index +test/units/playbook/test_base.py mypy-3.12:assignment +test/units/module_utils/basic/test_exit_json.py mypy-3.11:assignment +test/units/module_utils/basic/test_exit_json.py mypy-3.11:misc +test/units/module_utils/common/text/converters/test_json_encode_fallback.py mypy-3.11:abstract +test/units/module_utils/facts/other/test_facter.py mypy-3.11:assignment +test/units/module_utils/facts/other/test_ohai.py mypy-3.11:assignment +test/units/module_utils/facts/system/test_lsb.py mypy-3.11:assignment +test/units/module_utils/facts/test_collectors.py mypy-3.11:assignment +test/units/module_utils/facts/test_facts.py mypy-3.11:assignment +test/units/modules/mount_facts_data.py mypy-3.11:arg-type +test/units/modules/test_apt.py mypy-3.11:name-match +test/units/modules/test_mount_facts.py mypy-3.11:index +test/units/playbook/test_base.py mypy-3.11:assignment +test/units/module_utils/basic/test_exit_json.py mypy-3.10:assignment +test/units/module_utils/basic/test_exit_json.py mypy-3.10:misc +test/units/module_utils/common/text/converters/test_json_encode_fallback.py mypy-3.10:abstract +test/units/module_utils/facts/other/test_facter.py mypy-3.10:assignment +test/units/module_utils/facts/other/test_ohai.py mypy-3.10:assignment +test/units/module_utils/facts/system/test_lsb.py mypy-3.10:assignment +test/units/module_utils/facts/test_collectors.py mypy-3.10:assignment +test/units/module_utils/facts/test_facts.py mypy-3.10:assignment +test/units/modules/mount_facts_data.py mypy-3.10:arg-type +test/units/modules/test_apt.py mypy-3.10:name-match +test/units/modules/test_mount_facts.py mypy-3.10:index +test/units/module_utils/basic/test_exit_json.py mypy-3.9:assignment +test/units/module_utils/basic/test_exit_json.py mypy-3.9:misc +test/units/module_utils/common/text/converters/test_json_encode_fallback.py mypy-3.9:abstract +test/units/module_utils/facts/other/test_facter.py mypy-3.9:assignment +test/units/module_utils/facts/other/test_ohai.py mypy-3.9:assignment +test/units/module_utils/facts/system/test_lsb.py mypy-3.9:assignment +test/units/module_utils/facts/test_collectors.py mypy-3.9:assignment +test/units/module_utils/facts/test_facts.py mypy-3.9:assignment +test/units/modules/mount_facts_data.py mypy-3.9:arg-type +test/units/modules/test_apt.py mypy-3.9:name-match +test/units/modules/test_mount_facts.py mypy-3.9:index +test/units/module_utils/basic/test_exit_json.py mypy-3.8:assignment +test/units/module_utils/basic/test_exit_json.py mypy-3.8:misc +test/units/module_utils/common/text/converters/test_json_encode_fallback.py mypy-3.8:abstract +test/units/module_utils/facts/other/test_facter.py mypy-3.8:assignment +test/units/module_utils/facts/other/test_ohai.py mypy-3.8:assignment +test/units/module_utils/facts/system/test_lsb.py mypy-3.8:assignment +test/units/module_utils/facts/test_collectors.py mypy-3.8:assignment +test/units/module_utils/facts/test_facts.py mypy-3.8:assignment +test/units/modules/mount_facts_data.py mypy-3.8:arg-type +test/units/modules/test_apt.py mypy-3.8:name-match +test/units/modules/test_mount_facts.py mypy-3.8:index diff --git a/test/units/cli/test_galaxy.py b/test/units/cli/test_galaxy.py index 5444d148d06..9d6d32bb2a5 100644 --- a/test/units/cli/test_galaxy.py +++ b/test/units/cli/test_galaxy.py @@ -45,7 +45,7 @@ import unittest from unittest.mock import patch, MagicMock -@pytest.fixture(autouse='function') +@pytest.fixture(autouse=True) def reset_cli_args(): co.GlobalCLIArgs._Singleton__instance = None yield diff --git a/test/units/cli/test_vault.py b/test/units/cli/test_vault.py index c6f41c5b912..6305a02350a 100644 --- a/test/units/cli/test_vault.py +++ b/test/units/cli/test_vault.py @@ -35,7 +35,7 @@ from ansible.utils import context_objects as co # mock calls -@pytest.fixture(autouse='function') +@pytest.fixture(autouse=True) def reset_cli_args(): co.GlobalCLIArgs._Singleton__instance = None yield diff --git a/test/units/galaxy/test_api.py b/test/units/galaxy/test_api.py index 6acd165b1df..2ef1d07325f 100644 --- a/test/units/galaxy/test_api.py +++ b/test/units/galaxy/test_api.py @@ -29,7 +29,7 @@ from ansible.utils import context_objects as co from ansible.utils.display import Display -@pytest.fixture(autouse='function') +@pytest.fixture(autouse=True) def reset_cli_args(): co.GlobalCLIArgs._Singleton__instance = None # Required to initialise the GalaxyAPI object diff --git a/test/units/galaxy/test_collection.py b/test/units/galaxy/test_collection.py index bfabd81d9ee..73e915dee65 100644 --- a/test/units/galaxy/test_collection.py +++ b/test/units/galaxy/test_collection.py @@ -31,7 +31,7 @@ from ansible.utils.display import Display from ansible.utils.hashing import secure_hash_s -@pytest.fixture(autouse='function') +@pytest.fixture(autouse=True) def reset_cli_args(): co.GlobalCLIArgs._Singleton__instance = None yield diff --git a/test/units/galaxy/test_collection_install.py b/test/units/galaxy/test_collection_install.py index 6559535f931..dc6dbe5b6f3 100644 --- a/test/units/galaxy/test_collection_install.py +++ b/test/units/galaxy/test_collection_install.py @@ -53,7 +53,7 @@ def call_galaxy_cli(args): co.GlobalCLIArgs._Singleton__instance = orig -@pytest.fixture(autouse='function') +@pytest.fixture(autouse=True) def reset_cli_args(): co.GlobalCLIArgs._Singleton__instance = None yield @@ -992,9 +992,7 @@ def test_install_collection_with_no_dependency(collection_artifact, monkeypatch) (["good_signature", "good_signature"], '2', [], True), ] ) -def test_verify_file_signatures(signatures, required_successful_count, ignore_errors, expected_success): - # type: (List[bool], int, bool, bool) -> None - +def test_verify_file_signatures(signatures: list[str], required_successful_count: str, ignore_errors: list[str], expected_success: bool) -> None: def gpg_error_generator(results): for result in results: if isinstance(result, collection.gpg.GpgBaseError): diff --git a/test/units/galaxy/test_role_install.py b/test/units/galaxy/test_role_install.py index 25dff80cd63..8e77352a30f 100644 --- a/test/units/galaxy/test_role_install.py +++ b/test/units/galaxy/test_role_install.py @@ -28,7 +28,7 @@ def call_galaxy_cli(args): co.GlobalCLIArgs._Singleton__instance = orig -@pytest.fixture(autouse='function') +@pytest.fixture(autouse=True) def reset_cli_args(): co.GlobalCLIArgs._Singleton__instance = None yield diff --git a/test/units/module_utils/basic/test_argument_spec.py b/test/units/module_utils/basic/test_argument_spec.py index 46a3e9d9f31..4fea4745053 100644 --- a/test/units/module_utils/basic/test_argument_spec.py +++ b/test/units/module_utils/basic/test_argument_spec.py @@ -8,6 +8,7 @@ from __future__ import annotations import json import os +import typing as t import pytest @@ -69,7 +70,7 @@ VALID_SPECS = ( ({'arg': {'type': 'int'}}, {'arg': 1, 'invalid': True, '_ansible_ignore_unknown_opts': True}, 1), ) -INVALID_SPECS = ( +INVALID_SPECS: tuple[tuple[dict[str, t.Any], dict[str, t.Any], str], ...] = ( # Type is int; unable to convert this string ({'arg': {'type': 'int'}}, {'arg': "wolf"}, f"is of type {type('wolf')} and we were unable to convert to int:"), # Type is list elements is int; unable to convert this string @@ -495,7 +496,7 @@ class TestComplexOptions: ) # (Parameters, failure message) - FAILING_PARAMS_LIST = ( + FAILING_PARAMS_LIST: tuple[tuple[dict[str, list[dict[str, t.Any]]], str], ...] = ( # Missing required option ({'foobar': [{}]}, 'missing required arguments: foo found in foobar'), # Invalid option @@ -518,7 +519,7 @@ class TestComplexOptions: ) # (Parameters, failure message) - FAILING_PARAMS_DICT = ( + FAILING_PARAMS_DICT: tuple[tuple[dict[str, dict[str, t.Any]], str], ...] = ( # Missing required option ({'foobar': {}}, 'missing required arguments: foo found in foobar'), # Invalid option diff --git a/test/units/module_utils/basic/test_exit_json.py b/test/units/module_utils/basic/test_exit_json.py index 795e9c1eb6e..0f6844b24a1 100644 --- a/test/units/module_utils/basic/test_exit_json.py +++ b/test/units/module_utils/basic/test_exit_json.py @@ -7,12 +7,13 @@ from __future__ import annotations import json import sys import datetime +import typing as t import pytest from ansible.module_utils.common import warnings -EMPTY_INVOCATION = {u'module_args': {}} +EMPTY_INVOCATION: dict[str, dict[str, t.Any]] = {u'module_args': {}} DATETIME = datetime.datetime.strptime('2020-07-13 12:50:00', '%Y-%m-%d %H:%M:%S') @@ -20,7 +21,7 @@ class TestAnsibleModuleExitJson: """ Test that various means of calling exitJson and FailJson return the messages they've been given """ - DATA = ( + DATA: tuple[tuple[dict[str, t.Any]], ...] = ( ({}, {'invocation': EMPTY_INVOCATION}), ({'msg': 'message'}, {'msg': 'message', 'invocation': EMPTY_INVOCATION}), ({'msg': 'success', 'changed': True}, diff --git a/test/units/module_utils/basic/test_no_log.py b/test/units/module_utils/basic/test_no_log.py index c8c3ecea688..409d919164f 100644 --- a/test/units/module_utils/basic/test_no_log.py +++ b/test/units/module_utils/basic/test_no_log.py @@ -5,6 +5,7 @@ from __future__ import annotations +import typing as t import unittest from ansible.module_utils.basic import remove_values @@ -12,7 +13,7 @@ from ansible.module_utils.common.parameters import _return_datastructure_name class TestReturnValues(unittest.TestCase): - dataset = ( + dataset: tuple[tuple[t.Any, frozenset[str]], ...] = ( ('string', frozenset(['string'])), ('', frozenset()), (1, frozenset(['1'])), diff --git a/test/units/module_utils/basic/test_safe_eval.py b/test/units/module_utils/basic/test_safe_eval.py index 62403e81e6c..3df214c3562 100644 --- a/test/units/module_utils/basic/test_safe_eval.py +++ b/test/units/module_utils/basic/test_safe_eval.py @@ -5,11 +5,14 @@ from __future__ import annotations from itertools import chain + +import typing as t + import pytest # Strings that should be converted into a typed value -VALID_STRINGS = ( +VALID_STRINGS: tuple[tuple[str, t.Any], ...] = ( ("'a'", 'a'), ("'1'", '1'), ("1", 1), diff --git a/test/units/module_utils/common/arg_spec/test_aliases.py b/test/units/module_utils/common/arg_spec/test_aliases.py index a7c82b03b49..34726465b84 100644 --- a/test/units/module_utils/common/arg_spec/test_aliases.py +++ b/test/units/module_utils/common/arg_spec/test_aliases.py @@ -67,7 +67,7 @@ ALIAS_TEST_CASES = [ # id, argument spec, parameters, expected parameters, error -ALIAS_TEST_CASES_INVALID = [ +ALIAS_TEST_CASES_INVALID: list[tuple[str, dict, dict, dict, str]] = [ ( "alias-invalid", {'path': {'aliases': 'bad'}}, diff --git a/test/units/module_utils/common/test_collections.py b/test/units/module_utils/common/test_collections.py index 93cf3245ba4..381d583004c 100644 --- a/test/units/module_utils/common/test_collections.py +++ b/test/units/module_utils/common/test_collections.py @@ -39,16 +39,14 @@ class FakeAnsibleVaultEncryptedUnicode(Sequence): TEST_STRINGS = u'he', u'Україна', u'Česká republika' TEST_STRINGS = TEST_STRINGS + tuple(s.encode('utf-8') for s in TEST_STRINGS) + (FakeAnsibleVaultEncryptedUnicode(u'foo'),) -TEST_ITEMS_NON_SEQUENCES = ( +TEST_ITEMS_NON_SEQUENCES: tuple = ( {}, object(), frozenset(), 4, 0., ) + TEST_STRINGS -TEST_ITEMS_SEQUENCES = ( +TEST_ITEMS_SEQUENCES: tuple = ( [], (), SeqStub(), -) -TEST_ITEMS_SEQUENCES = TEST_ITEMS_SEQUENCES + ( # Iterable effectively containing nested random data: TEST_ITEMS_NON_SEQUENCES, ) diff --git a/test/units/module_utils/facts/hardware/test_linux.py b/test/units/module_utils/facts/hardware/test_linux.py index 0a546f1516b..74e8f20cd5d 100644 --- a/test/units/module_utils/facts/hardware/test_linux.py +++ b/test/units/module_utils/facts/hardware/test_linux.py @@ -29,8 +29,6 @@ from . linux_data import LSBLK_OUTPUT, LSBLK_OUTPUT_2, LSBLK_UUIDS, MTAB, MTAB_E with open(os.path.join(os.path.dirname(__file__), '../fixtures/findmount_output.txt')) as f: FINDMNT_OUTPUT = f.read() -GET_MOUNT_SIZE = {} - def mock_get_mount_size(mountpoint): return STATVFS_INFO.get(mountpoint, {}) diff --git a/test/units/module_utils/facts/test_ansible_collector.py b/test/units/module_utils/facts/test_ansible_collector.py index badfddd9d8c..3293960d9f7 100644 --- a/test/units/module_utils/facts/test_ansible_collector.py +++ b/test/units/module_utils/facts/test_ansible_collector.py @@ -200,7 +200,7 @@ class TestCollectedFacts(unittest.TestCase): 'env'] not_expected_facts = ['facter', 'ohai'] - collected_facts = {} + collected_facts: dict[str, str] = {} def _mock_module(self, gather_subset=None): return mock_module(gather_subset=self.gather_subset) diff --git a/test/units/modules/test_copy.py b/test/units/modules/test_copy.py index 49e332429a1..6f15bed1223 100644 --- a/test/units/modules/test_copy.py +++ b/test/units/modules/test_copy.py @@ -11,102 +11,109 @@ from ansible.modules.copy import AnsibleModuleError, split_pre_existing_dir from ansible.module_utils.basic import AnsibleModule +THREE_DIRS_DATA: tuple[tuple[str, tuple[str, list[str]] | None, tuple[str, list[str]], tuple[str, list[str]], tuple[str, list[str]]], ...] = ( + ('/dir1/dir2', + # 0 existing dirs: error (because / should always exist) + None, + # 1 existing dir: + ('/', ['dir1', 'dir2']), + # 2 existing dirs: + ('/dir1', ['dir2']), + # 3 existing dirs: + ('/dir1/dir2', []) + ), + ('/dir1/dir2/', + # 0 existing dirs: error (because / should always exist) + None, + # 1 existing dir: + ('/', ['dir1', 'dir2']), + # 2 existing dirs: + ('/dir1', ['dir2']), + # 3 existing dirs: + ('/dir1/dir2', []) + ), +) + -THREE_DIRS_DATA = (('/dir1/dir2', - # 0 existing dirs: error (because / should always exist) - None, - # 1 existing dir: - ('/', ['dir1', 'dir2']), - # 2 existing dirs: - ('/dir1', ['dir2']), - # 3 existing dirs: - ('/dir1/dir2', []) - ), - ('/dir1/dir2/', - # 0 existing dirs: error (because / should always exist) - None, - # 1 existing dir: - ('/', ['dir1', 'dir2']), - # 2 existing dirs: - ('/dir1', ['dir2']), - # 3 existing dirs: - ('/dir1/dir2', []) - ), - ) - - -TWO_DIRS_DATA = (('dir1/dir2', - # 0 existing dirs: - ('.', ['dir1', 'dir2']), - # 1 existing dir: - ('dir1', ['dir2']), - # 2 existing dirs: - ('dir1/dir2', []), - # 3 existing dirs: Same as 2 because we never get to the third - ), - ('dir1/dir2/', - # 0 existing dirs: - ('.', ['dir1', 'dir2']), - # 1 existing dir: - ('dir1', ['dir2']), - # 2 existing dirs: - ('dir1/dir2', []), - # 3 existing dirs: Same as 2 because we never get to the third - ), - ('/dir1', - # 0 existing dirs: error (because / should always exist) - None, - # 1 existing dir: - ('/', ['dir1']), - # 2 existing dirs: - ('/dir1', []), - # 3 existing dirs: Same as 2 because we never get to the third - ), - ('/dir1/', - # 0 existing dirs: error (because / should always exist) - None, - # 1 existing dir: - ('/', ['dir1']), - # 2 existing dirs: - ('/dir1', []), - # 3 existing dirs: Same as 2 because we never get to the third - ), - ) + THREE_DIRS_DATA - - -ONE_DIR_DATA = (('dir1', - # 0 existing dirs: - ('.', ['dir1']), - # 1 existing dir: - ('dir1', []), - # 2 existing dirs: Same as 1 because we never get to the third - ), - ('dir1/', - # 0 existing dirs: - ('.', ['dir1']), - # 1 existing dir: - ('dir1', []), - # 2 existing dirs: Same as 1 because we never get to the third - ), - ) + TWO_DIRS_DATA +TWO_DIRS_DATA: tuple[tuple[str, tuple[str, list[str]] | None, tuple[str, list[str]], tuple[str, list[str]]], ...] = ( + ('dir1/dir2', + # 0 existing dirs: + ('.', ['dir1', 'dir2']), + # 1 existing dir: + ('dir1', ['dir2']), + # 2 existing dirs: + ('dir1/dir2', []), + # 3 existing dirs: Same as 2 because we never get to the third + ), + ('dir1/dir2/', + # 0 existing dirs: + ('.', ['dir1', 'dir2']), + # 1 existing dir: + ('dir1', ['dir2']), + # 2 existing dirs: + ('dir1/dir2', []), + # 3 existing dirs: Same as 2 because we never get to the third + ), + ('/dir1', + # 0 existing dirs: error (because / should always exist) + None, + # 1 existing dir: + ('/', ['dir1']), + # 2 existing dirs: + ('/dir1', []), + # 3 existing dirs: Same as 2 because we never get to the third + ), + ('/dir1/', + # 0 existing dirs: error (because / should always exist) + None, + # 1 existing dir: + ('/', ['dir1']), + # 2 existing dirs: + ('/dir1', []), + # 3 existing dirs: Same as 2 because we never get to the third + ), +) +TWO_DIRS_DATA += tuple(item[:4] for item in THREE_DIRS_DATA) + + +ONE_DIR_DATA: tuple[tuple[str, tuple[str, list[str]] | None, tuple[str, list[str]]], ...] = ( + ('dir1', + # 0 existing dirs: + ('.', ['dir1']), + # 1 existing dir: + ('dir1', []), + # 2 existing dirs: Same as 1 because we never get to the third + ), + ('dir1/', + # 0 existing dirs: + ('.', ['dir1']), + # 1 existing dir: + ('dir1', []), + # 2 existing dirs: Same as 1 because we never get to the third + ), +) +ONE_DIR_DATA += tuple(item[:3] for item in TWO_DIRS_DATA) @pytest.mark.parametrize('directory, expected', ((d[0], d[4]) for d in THREE_DIRS_DATA)) +@pytest.mark.xfail(reason='broken test and/or code, original test missing assert', strict=False) def test_split_pre_existing_dir_three_levels_exist(directory, expected, mocker): mocker.patch('os.path.exists', side_effect=[True, True, True]) - split_pre_existing_dir(directory) == expected + assert split_pre_existing_dir(directory) == expected @pytest.mark.parametrize('directory, expected', ((d[0], d[3]) for d in TWO_DIRS_DATA)) +@pytest.mark.xfail(reason='broken test and/or code, original test missing assert', strict=False) def test_split_pre_existing_dir_two_levels_exist(directory, expected, mocker): mocker.patch('os.path.exists', side_effect=[True, True, False]) - split_pre_existing_dir(directory) == expected + assert split_pre_existing_dir(directory) == expected @pytest.mark.parametrize('directory, expected', ((d[0], d[2]) for d in ONE_DIR_DATA)) +@pytest.mark.xfail(reason='broken test and/or code, original test missing assert', strict=False) def test_split_pre_existing_dir_one_level_exists(directory, expected, mocker): mocker.patch('os.path.exists', side_effect=[True, False, False]) - split_pre_existing_dir(directory) == expected + assert split_pre_existing_dir(directory) == expected @pytest.mark.parametrize('directory', (d[0] for d in ONE_DIR_DATA if d[1] is None)) @@ -118,9 +125,10 @@ def test_split_pre_existing_dir_root_does_not_exist(directory, mocker): @pytest.mark.parametrize('directory, expected', ((d[0], d[1]) for d in ONE_DIR_DATA if not d[0].startswith('/'))) +@pytest.mark.xfail(reason='broken test and/or code, original test missing assert', strict=False) def test_split_pre_existing_dir_working_dir_exists(directory, expected, mocker): mocker.patch('os.path.exists', return_value=False) - split_pre_existing_dir(directory) == expected + assert split_pre_existing_dir(directory) == expected # diff --git a/test/units/parsing/test_splitter.py b/test/units/parsing/test_splitter.py index 9eb957d6969..402ee74dcb7 100644 --- a/test/units/parsing/test_splitter.py +++ b/test/units/parsing/test_splitter.py @@ -23,7 +23,7 @@ from ansible.errors import AnsibleParserError import pytest -SPLIT_DATA = ( +SPLIT_DATA: tuple[tuple[str | None, list[str], dict[str, str]], ...] = ( (None, [], {}), diff --git a/test/units/plugins/connection/test_psrp.py b/test/units/plugins/connection/test_psrp.py index 10902a15819..fcc5648d0fd 100644 --- a/test/units/plugins/connection/test_psrp.py +++ b/test/units/plugins/connection/test_psrp.py @@ -6,6 +6,7 @@ from __future__ import annotations import pytest import sys +import typing as t from io import StringIO from unittest.mock import MagicMock @@ -46,7 +47,7 @@ def psrp_connection(): class TestConnectionPSRP(object): - OPTIONS_DATA = ( + OPTIONS_DATA: tuple[tuple[dict[str, t.Any], dict[str, t.Any]], ...] = ( # default options ( {}, diff --git a/test/units/plugins/connection/test_winrm.py b/test/units/plugins/connection/test_winrm.py index 00a29c97286..d5b76ca8f26 100644 --- a/test/units/plugins/connection/test_winrm.py +++ b/test/units/plugins/connection/test_winrm.py @@ -5,6 +5,7 @@ from __future__ import annotations import os +import typing as t import pytest @@ -22,7 +23,7 @@ pytest.importorskip("winrm") class TestConnectionWinRM(object): - OPTIONS_DATA = ( + OPTIONS_DATA: tuple[tuple[dict[str, t.Any], dict[str, t.Any], dict[str, t.Any], bool], ...] = ( # default options ( {'_extras': {}}, diff --git a/test/units/plugins/filter/test_mathstuff.py b/test/units/plugins/filter/test_mathstuff.py index 4ac5487fa23..b473e305fda 100644 --- a/test/units/plugins/filter/test_mathstuff.py +++ b/test/units/plugins/filter/test_mathstuff.py @@ -11,7 +11,7 @@ import ansible.plugins.filter.mathstuff as ms from ansible.errors import AnsibleFilterError, AnsibleFilterTypeError -UNIQUE_DATA = [ +UNIQUE_DATA: list[tuple[list, list]] = [ ([], []), ([1, 3, 4, 2], [1, 3, 4, 2]), ([1, 3, 2, 4, 2, 3], [1, 3, 2, 4]), @@ -19,7 +19,7 @@ UNIQUE_DATA = [ ([1, 1, 4, 2, 1, 4, 3, 2], [1, 4, 2, 3]), ] -TWO_SETS_DATA = [ +TWO_SETS_DATA: list[tuple[list, list, tuple]] = [ ([], [], ([], [], [])), ([1, 2], [1, 2], ([1, 2], [], [])), ([1, 2], [3, 4], ([], [1, 2], [1, 2, 3, 4])), diff --git a/test/units/plugins/lookup/test_password.py b/test/units/plugins/lookup/test_password.py index d615be998ec..46c63db6032 100644 --- a/test/units/plugins/lookup/test_password.py +++ b/test/units/plugins/lookup/test_password.py @@ -387,8 +387,11 @@ class TestWritePasswordFile(unittest.TestCase): def setUp(self): self.makedirs_safe = password.makedirs_safe self.os_chmod = password.os.chmod - password.makedirs_safe = lambda path, mode: None - password.os.chmod = lambda path, mode: None + password.makedirs_safe = self.noop + password.os.chmod = self.noop + + def noop(self, *args, **kwargs): + pass def tearDown(self): password.makedirs_safe = self.makedirs_safe @@ -410,11 +413,14 @@ class BaseTestLookupModule(unittest.TestCase): self.password_lookup._loader = self.fake_loader self.os_path_exists = password.os.path.exists self.os_open = password.os.open - password.os.open = lambda path, flag: None + password.os.open = self.noop self.os_close = password.os.close - password.os.close = lambda fd: None + password.os.close = self.noop self.makedirs_safe = password.makedirs_safe - password.makedirs_safe = lambda path, mode: None + password.makedirs_safe = self.noop + + def noop(self, *args, **kwargs): + pass def tearDown(self): password.os.path.exists = self.os_path_exists