/
githubmirror
/
salt
Обзор
Документация
Войти
/
githubmirror
/
salt
Код
Запросы
0
Пакеты
0
Релизы
0
Аналитика
Безопасность
master
tests/pytests/unit/utils/test_versions.py
562 строки
18 KB
Daniel A. Wozniak
Port additional package and test build fixes for 3009.0
02 май 2026, 12:08
02 май 2026, 12:08
c2794a2
Код
Авторство
О чём код?
import datetime import os import sys import warnings import pytest from packaging.version import InvalidVersion import salt.modules.cmdmod import salt.utils.versions import salt.version from salt.utils.versions import LooseVersion, Version from tests.support.mock import patch skipunless_deprecation_runtime_errors = pytest.mark.skipif( os.environ.get("RAISE_DEPRECATIONS_RUNTIME_ERRORS", "0") != "1", reason="Set RAISE_DEPRECATIONS_RUNTIME_ERRORS=1 to assert warn_until raises RuntimeError", ) TEST_MOD = """ __version__ = (1, 2, 3) """ def test_prerelease(): version = Version("1.2.3a1") assert version.release == (1, 2, 3) assert version.pre == ("a", 1) assert str(version) == "1.2.3a1" version = Version("1.2.0") assert str(version) == "1.2.0" @pytest.mark.parametrize( "v1,v2,wanted", ( ("1.5.1", "1.5.2b2", -1), ("161", "3.10a", 1), ("8.02", "8.02", 0), ("3.4j", "1996.07.12", InvalidVersion), ("3.2.pl0", "3.1.1.6", InvalidVersion), ("2g6", "11g", InvalidVersion), ("0.9", "2.2", -1), ("1.2.1", "1.2", 1), ("1.1", "1.2.2", -1), ("1.2", "1.1", 1), ("1.2.1", "1.2.2", -1), ("1.2.2", "1.2", 1), ("1.2", "1.2.2", -1), ("0.4.0", "0.4", 0), ("1.13++", "5.5.kw", InvalidVersion), ("1.1.1a1", "1.1.1", -1), ), ) def test_cmp_strict(v1, v2, wanted): try: v1i = Version(v1) v2i = Version(v2) if v1i == v2i: res = 0 elif v1i < v2i: res = -1 elif v1i > v2i: res = 1 assert res == wanted, "cmp({}, {}) should be {}, got {}".format( v1, v2, wanted, res ) except InvalidVersion: if wanted is not InvalidVersion: raise AssertionError(f"cmp({v1}, {v2}) shouldn't raise InvalidVersion") @pytest.mark.parametrize( "v1,v2,wanted", ( ("1.5.1", "1.5.2b2", -1), ("161", "3.10a", 1), ("8.02", "8.02", 0), ("3.4j", "1996.07.12", -1), ("3.2.pl0", "3.1.1.6", 1), ("2g6", "11g", -1), ("0.960923", "2.2beta29", -1), ("1.13++", "5.5.kw", -1), # Added by us ("3.10.0-514.el7", "3.10.0-514.6.1.el7", 1), ("2.2.2", "2.12.1", -1), ("2.24.0", "2.23.0.windows.1", 1), ("2.23.0.windows.2", "2.23.0.windows.1", 1), ), ) def test_cmp(v1, v2, wanted): res = LooseVersion(v1)._cmp(LooseVersion(v2)) assert res == wanted, f"cmp({v1}, {v2}) should be {wanted}, got {res}" def test_compare(): ret = salt.utils.versions.compare("1.0", "==", "1.0") assert ret ret = salt.utils.versions.compare("1.0", "!=", "1.0") assert not ret with patch.object(salt.utils.versions, "log") as log_mock: ret = salt.utils.versions.compare( "1.0", "HAH I AM NOT A COMP OPERATOR! I AM YOUR FATHER!", "1.0" ) assert log_mock.error.called @pytest.mark.parametrize( "version", ( "Chlorine", 3007, (3007, 0), salt.version.SaltVersionsInfo.CHLORINE, ), ) @skipunless_deprecation_runtime_errors def test_warn_until_good_version_argument(version): with pytest.raises( RuntimeError, match=( r"The warning triggered on filename \'(.*)test_versions.py\', " r"line number ([\d]+), is supposed to be shown until version " r"3007.0 \(Chlorine\) is released. Current version is now 3009.0 \(Potassium\). " r"Please remove the warning." ), ): salt.utils.versions.warn_until( version, "Deprecation Message after {version}!", _version_info_=(3009, 0) ) def test_warn_until_bad_version_name_raises_runtime_error(): # Ensure proper behavior with warnings.catch_warnings(record=True) as recorded_warnings: salt.utils.versions.warn_until( "Sodium", "Deprecation Message!", _version_info_=(3000, 0) ) assert "Deprecation Message!" == str(recorded_warnings[0].message) with pytest.raises( RuntimeError, match="Incorrect spelling for the release name in .*" ): salt.utils.versions.warn_until( "Sudium", "Deprecation Message!", _version_info_=(3000, 0) ) def test_kwargs_warn_until(): # Test invalid version arg pytest.raises(RuntimeError, salt.utils.versions.kwargs_warn_until, {}, []) def test_warn_until_warning_raised(subtests): # We *always* want *all* warnings thrown on this module warnings.filterwarnings("always", "", DeprecationWarning, __name__) def raise_warning(_version_info_=(0, 16, 0)): salt.utils.versions.warn_until( (0, 17), "Deprecation Message!", _version_info_=_version_info_ ) def raise_named_version_warning(_version_info_=(0, 16, 0)): salt.utils.versions.warn_until( "hydrogen", "Deprecation Message!", _version_info_=_version_info_ ) with subtests.test( "raise_warning should show warning until version info is >= (0, 17)" ): with warnings.catch_warnings(record=True) as recorded_warnings: raise_warning() assert "Deprecation Message!" == str(recorded_warnings[0].message) with subtests.test( "raise_warning should show warning until version info is >= (0, 17)" ): with warnings.catch_warnings(record=True) as recorded_warnings: raise_named_version_warning() assert "Deprecation Message!" == str(recorded_warnings[0].message) with subtests.test( "the deprecation warning is not issued because we passed _dont_call_warning" ): with warnings.catch_warnings(record=True) as recorded_warnings: salt.utils.versions.warn_until( (0, 17), "Foo", _dont_call_warnings=True, _version_info_=(0, 16) ) assert 0 == len(recorded_warnings) with subtests.test("version on the deprecation message gets properly formatted"): with warnings.catch_warnings(record=True) as recorded_warnings: vrs = salt.version.SaltStackVersion.from_name("Helium") salt.utils.versions.warn_until( "Helium", "Deprecation Message until {version}!", _version_info_=(vrs.major - 1, 0), ) assert f"Deprecation Message until {vrs.formatted_version}!" == str( recorded_warnings[0].message ) @skipunless_deprecation_runtime_errors def test_warn_until_raises_when_deadline_passed(subtests): warnings.filterwarnings("always", "", DeprecationWarning, __name__) def raise_warning(_version_info_=(0, 16, 0)): salt.utils.versions.warn_until( (0, 17), "Deprecation Message!", _version_info_=_version_info_ ) def raise_named_version_warning(_version_info_=(0, 16, 0)): salt.utils.versions.warn_until( "hydrogen", "Deprecation Message!", _version_info_=_version_info_ ) with subtests.test( "Let's set version info to (0, 17), a RuntimeError should be raised" ): with pytest.raises( RuntimeError, match=( r"The warning triggered on filename \'(.*)test_versions.py\', " r"line number ([\d]+), is supposed to be shown until version " r"0.17.0 is released. Current version is now 0.17.0. " r"Please remove the warning." ), ): raise_warning(_version_info_=(0, 17, 0)) with subtests.test( "Let's set version info to (sys,maxsize, 16, 0), a RuntimeError should be raised" ): with pytest.raises( RuntimeError, match=( r"The warning triggered on filename \'(.*)test_versions.py\', " r"line number ([\d]+), is supposed to be shown until version " r"(.*) is released. Current version is now " r"([\d.]+). Please remove the warning." ), ): raise_named_version_warning(_version_info_=(sys.maxsize, 16, 0)) with subtests.test( "Even though we're calling warn_until, we pass _dont_call_warnings " "because we're only after the RuntimeError" ): with pytest.raises( RuntimeError, match=( r"The warning triggered on filename \'(.*)test_versions.py\', " r"line number ([\d]+), is supposed to be shown until version " r"0.17.0 is released. Current version is now " r"(.*). Please remove the warning." ), ): salt.utils.versions.warn_until((0, 17), "Foo", _dont_call_warnings=True) with pytest.raises( RuntimeError, match=( r"The warning triggered on filename \'(.*)test_versions.py\', " r"line number ([\d]+), is supposed to be shown until version " r"(.*) is released. Current version is now " r"(.*). Please remove the warning." ), ): salt.utils.versions.warn_until( "Hydrogen", "Foo", _dont_call_warnings=True, _version_info_=(sys.maxsize, 16, 0), ) def test_kwargs_warn_until_warning_raised(subtests): # We *always* want *all* warnings thrown on this module warnings.filterwarnings("always", "", DeprecationWarning, __name__) def raise_warning(**kwargs): _version_info_ = kwargs.pop("_version_info_", (0, 16, 0)) salt.utils.versions.kwargs_warn_until( kwargs, (0, 17), _version_info_=_version_info_ ) with subtests.test( "raise_warning({...}) should show warning until version info is >= (0, 17)" ): with warnings.catch_warnings(record=True) as recorded_warnings: raise_warning(foo=42) # with a kwarg expected = ( "The following parameter(s) have been deprecated and will be " "removed in '0.17.0': 'foo'." ) assert expected == str(recorded_warnings[0].message) with subtests.test( "With no **kwargs, should not show warning until version info is >= (0, 17)" ): with warnings.catch_warnings(record=True) as recorded_warnings: salt.utils.versions.kwargs_warn_until( {}, (0, 17), _version_info_=(0, 16, 0) # no kwargs ) assert 0 == len(recorded_warnings) @skipunless_deprecation_runtime_errors def test_kwargs_warn_until_raises_when_deadline_passed(subtests): warnings.filterwarnings("always", "", DeprecationWarning, __name__) def raise_warning(**kwargs): _version_info_ = kwargs.pop("_version_info_", (0, 16, 0)) salt.utils.versions.kwargs_warn_until( kwargs, (0, 17), _version_info_=_version_info_ ) with subtests.test( "Let's set version info to (0, 17), a RuntimeError should be raised " "regardless of whether or not we pass any **kwargs." ): with pytest.raises( RuntimeError, match=( r"The warning triggered on filename \'(.*)test_versions.py\', " r"line number ([\d]+), is supposed to be shown until version " r"0.17.0 is released. Current version is now 0.17.0. " r"Please remove the warning." ), ): raise_warning(_version_info_=(0, 17)) # no kwargs with pytest.raises( RuntimeError, match=( r"The warning triggered on filename \'(.*)test_versions.py\', " r"line number ([\d]+), is supposed to be shown until version " r"0.17.0 is released. Current version is now 0.17.0. " r"Please remove the warning." ), ): raise_warning(bar="baz", qux="quux", _version_info_=(0, 17)) # some kwargs def test_warn_until_date_warning_raised(): # We *always* want *all* warnings thrown on this module warnings.filterwarnings("always", "", DeprecationWarning, __name__) _current_date = datetime.date(2000, 1, 1) # Test warning with datetime.date instance with warnings.catch_warnings(record=True) as recorded_warnings: salt.utils.versions.warn_until_date( datetime.date(2000, 1, 2), "Deprecation Message!", _current_date=_current_date, ) assert "Deprecation Message!" == str(recorded_warnings[0].message) # Test warning with datetime.datetime instance with warnings.catch_warnings(record=True) as recorded_warnings: salt.utils.versions.warn_until_date( datetime.datetime(2000, 1, 2), "Deprecation Message!", _current_date=_current_date, ) assert "Deprecation Message!" == str(recorded_warnings[0].message) # Test warning with date as a string with warnings.catch_warnings(record=True) as recorded_warnings: salt.utils.versions.warn_until_date( "20000102", "Deprecation Message!", _current_date=_current_date ) assert "Deprecation Message!" == str(recorded_warnings[0].message) # the deprecation warning is not issued because we passed # _dont_call_warning with warnings.catch_warnings(record=True) as recorded_warnings: salt.utils.versions.warn_until_date( "20000102", "Deprecation Message!", _dont_call_warnings=True, _current_date=_current_date, ) assert len(recorded_warnings) == 0 @skipunless_deprecation_runtime_errors def test_warn_until_date_raises_when_past_deadline(): _current_date = datetime.date(2000, 1, 1) with pytest.raises( RuntimeError, match=( r"Deprecation Message! This warning\(now exception\) triggered on " r"filename \'(.*)test_versions.py\', line number ([\d]+), is " r"supposed to be shown until ([\d-]+). Today is ([\d-]+). " r"Please remove the warning." ), ): salt.utils.versions.warn_until_date("20000101", "Deprecation Message!") with pytest.raises( RuntimeError, match=( r"Deprecation Message! This warning\(now exception\) triggered on " r"filename \'(.*)test_versions.py\', line number ([\d]+), is " r"supposed to be shown until ([\d-]+). Today is ([\d-]+). " r"Please remove the warning." ), ): salt.utils.versions.warn_until_date( "20000101", "Deprecation Message!", _dont_call_warnings=True, _current_date=_current_date, ) def test_warn_until_date_bad_strptime_format(): # We *always* want *all* warnings thrown on this module warnings.filterwarnings("always", "", DeprecationWarning, __name__) # Let's test for RuntimeError raise with pytest.raises( ValueError, match="time data '0022' does not match format '%Y%m%d'" ): salt.utils.versions.warn_until_date("0022", "Deprecation Message!") def test_default_module_getter(): mod = salt.utils.versions.default_module_getter("socket") assert mod is not None assert mod.__name__ == "socket" def test_default_module_getter_noexist(): mod = salt.utils.versions.default_module_getter("this_module_does_not_exist") assert mod is None def test_default_version_getter_version(): class mock: version = "1.2.1" assert salt.utils.versions.default_version_getter(mock) == "1.2.1" def test_default_version_getter___version__(): class mock: __version__ = "1.2.1" assert salt.utils.versions.default_version_getter(mock) == "1.2.1" def test_default_version_getter_tuple(): class mock: __version__ = (1, 2, 1) assert salt.utils.versions.default_version_getter(mock) == "1.2.1" @pytest.fixture def module(tmp_path): mod_path = tmp_path / "test_module.py" with salt.utils.files.fopen(mod_path, "w") as fp: fp.write(TEST_MOD) orig_path = sys.path[:] try: sys.path.append(str(tmp_path)) yield finally: sys.path = orig_path def test_depenency_exists(module): dep = salt.utils.versions.Requirement("test_module") assert dep.has_depend is True assert dep def test_depenency_does_not_exist(module): dep = salt.utils.versions.Requirement("test_module_does_not_exit") assert dep.has_depend is False assert not dep def test_depenency_version_eq(module): dep = salt.utils.versions.Requirement("test_module") assert dep assert dep == "1.2.3" def test_depenency_version_ne(module): dep = salt.utils.versions.Requirement("test_module") assert bool(dep != "1.2.3") is False assert dep != "1.2.3.1" def test_depenency_version_lt(module): dep = salt.utils.versions.Requirement("test_module") assert dep < "1.2.3.1" def test_depenency_version_le(module): dep = salt.utils.versions.Requirement("test_module") assert dep <= "1.2.3" assert dep <= "1.2.3.1" def test_depenency_version_gt(module): dep = salt.utils.versions.Requirement("test_module") assert bool(dep > "1.2.3") is False assert dep > "1.2.2" assert dep > "1.2.2.1" def test_depenency_version_ge(module): dep = salt.utils.versions.Requirement("test_module") assert dep >= "1.2.3" assert bool(dep >= "1.2.3.1") is False assert bool(dep >= "1.2.4") is False def test_depends_custom_getters(): def my_module_getter(name): return True def my_version_getter(mod): return "1.2.3" deps_map = { "foobar": salt.utils.versions.Getters(my_module_getter, my_version_getter) } reqs = salt.utils.versions.Requirements(deps_map) assert reqs.foobar assert reqs.foobar == "1.2.3" def test_depends_not_registered(): reqs = salt.utils.versions.Requirements() with pytest.raises(salt.utils.versions.RequirementNotRegistered): reqs.module_not_registered == "0.0.0" # pylint: disable=pointless-statement def test_dependency_missing_comparisons(): def my_module_getter(name): return None deps_map = {"missing": salt.utils.versions.Getters(my_module_getter, None)} reqs = salt.utils.versions.Requirements(deps_map) assert bool(reqs.missing) is False assert (reqs.missing == "1.0.0") is False assert (reqs.missing != "1.0.0") is True assert (reqs.missing < "1.0.0") is False assert (reqs.missing <= "1.0.0") is False assert (reqs.missing > "1.0.0") is False assert (reqs.missing >= "1.0.0") is False