# -*- coding: utf-8 -*- """ :codeauthor: Pedro Algarvio (pedro@algarvio.me) tests.unit.version_test ~~~~~~~~~~~~~~~~~~~~~~~ Test salt's regex git describe version parsing """ # Import python libs from __future__ import absolute_import import re import salt.version # Import Salt libs from salt.version import SaltStackVersion, system_information, versions_report from tests.support.mock import MagicMock, patch # Import Salt Testing libs from tests.support.unit import TestCase, skipIf class VersionTestCase(TestCase): def test_version_parsing(self): strip_initial_non_numbers_regex = re.compile(r"(?:[^\d]+)?(?P.*)") expect = ( ("v0.12.0-19-g767d4f9", (0, 12, 0, 0, "", 0, 19, "g767d4f9"), None), ("v0.12.0-85-g2880105", (0, 12, 0, 0, "", 0, 85, "g2880105"), None), ( "debian/0.11.1+ds-1-3-ga0afcbd", (0, 11, 1, 0, "", 0, 3, "ga0afcbd"), "0.11.1-3-ga0afcbd", ), ("0.12.1", (0, 12, 1, 0, "", 0, 0, None), None), ("0.12.1", (0, 12, 1, 0, "", 0, 0, None), None), ("0.17.0rc1", (0, 17, 0, 0, "rc", 1, 0, None), None), ("v0.17.0rc1-1-g52ebdfd", (0, 17, 0, 0, "rc", 1, 1, "g52ebdfd"), None), ("v2014.1.4.1", (2014, 1, 4, 1, "", 0, 0, None), None), ( "v2014.1.4.1rc3-n/a-abcdefff", (2014, 1, 4, 1, "rc", 3, -1, "abcdefff"), None, ), ("v3.4.1.1", (3, 4, 1, 1, "", 0, 0, None), None), ("v3000", (3000, "", 0, 0, None), "3000"), ("v3000.0", (3000, "", 0, 0, None), "3000"), ("v4518.1", (4518, 1, "", 0, 0, None), "4518.1"), ("v3000rc1", (3000, "rc", 1, 0, None), "3000rc1"), ("v3000rc1-n/a-abcdefff", (3000, "rc", 1, -1, "abcdefff"), None), ("3000-n/a-1e7bc8f", (3000, "", 0, -1, "1e7bc8f"), None), ("3000.1-n/a-1e7bc8f", (3000, 1, "", 0, -1, "1e7bc8f"), None), ) for vstr, full_info, version in expect: saltstack_version = SaltStackVersion.parse(vstr) self.assertEqual(saltstack_version.full_info, full_info) if version is None: version = strip_initial_non_numbers_regex.search(vstr).group("vs") self.assertEqual(saltstack_version.string, version) def test_version_comparison(self): examples = ( ("debian/0.11.1+ds-1-3-ga0afcbd", "0.11.1+ds-2"), ("v0.12.0-85-g2880105", "v0.12.0-19-g767d4f9"), ("v0.17.0rc1-1-g52ebdfd", "0.17.0rc1"), ("v0.17.0", "v0.17.0rc1"), ("Hydrogen", "0.17.0"), ("Helium", "Hydrogen"), ("v2014.1.4.1-n/a-abcdefff", "v2014.1.4.1rc3-n/a-abcdefff"), ("v2014.1.4.1-1-abcdefff", "v2014.1.4.1-n/a-abcdefff"), ("v2016.12.0rc1", "v2016.12.0b1"), ("v2016.12.0beta1", "v2016.12.0alpha1"), ("v2016.12.0alpha1", "v2016.12.0alpha0"), ("v3000.1", "v3000"), ("v3000rc2", "v3000rc1"), ("v3001", "v3000"), ("v4023rc1", "v4022rc1"), ("v3000", "v3000rc1"), ("v3000", "v2019.2.1"), ("v3000.1", "v2019.2.1"), # we created v3000.0rc1 tag on repo # but we should not be using this # version scheme in the future # but still adding test for it ("v3000", "v3000.0rc1"), ("v3000.1rc1", "v3000.0rc1"), ("v3000", "v2019.2.1rc1"), ("v3001rc1", "v2019.2.1rc1"), ) for higher_version, lower_version in examples: self.assertTrue(SaltStackVersion.parse(higher_version) > lower_version) self.assertTrue(SaltStackVersion.parse(lower_version) < higher_version) assert SaltStackVersion.parse(lower_version) != higher_version def test_unparsable_version(self): with self.assertRaises(ValueError): SaltStackVersion.from_name("Drunk") with self.assertRaises(ValueError): SaltStackVersion.parse("Drunk") def test_sha(self): """ test matching sha's """ exp_ret = ( ("d6cd1e2bd19e03a81132a23b2025920577f84e37", True), ("2880105", True), ("v3000.0.1", False), ("v0.12.0-85-g2880105", False), ) for commit, exp in exp_ret: ret = SaltStackVersion.git_sha_regex.match(commit) if exp: assert ret else: assert not ret def test_version_report_lines(self): """ Validate padding in versions report is correct """ # Get a set of all version report name lenghts including padding line_lengths = set( [ len(line.split(":")[0]) for line in list(versions_report())[4:] if line != " " and line != "System Versions:" ] ) # Check that they are all the same size (only one element in the set) assert len(line_lengths) == 1 def test_string_new_version(self): """ Validate string property method using new versioning scheme """ maj_ver = "3000" ver = SaltStackVersion(major=maj_ver) assert not ver.minor assert not ver.bugfix assert maj_ver == ver.string def test_string_new_version_minor(self): """ Validate string property method using new versioning scheme alongside minor version """ maj_ver = 3000 min_ver = 1 ver = SaltStackVersion(major=maj_ver, minor=min_ver) assert ver.minor == min_ver assert not ver.bugfix assert ver.string == "{0}.{1}".format(maj_ver, min_ver) def test_string_new_version_minor_as_string(self): """ Validate string property method using new versioning scheme alongside minor version """ maj_ver = "3000" min_ver = "1" ver = SaltStackVersion(major=maj_ver, minor=min_ver) assert ver.minor == int(min_ver) assert not ver.bugfix assert ver.string == "{0}.{1}".format(maj_ver, min_ver) # This only seems to happen on a cloned repo without its tags maj_ver = "3000" min_ver = "" ver = SaltStackVersion(major=maj_ver, minor=min_ver) assert ver.minor is None, "{!r} is not {!r}".format(ver.minor, min_ver) assert not ver.bugfix assert ver.string == maj_ver def test_string_old_version(self): """ Validate string property method using old versioning scheme alongside minor version """ maj_ver = "2019" min_ver = "2" ver = SaltStackVersion(major=maj_ver, minor=min_ver) assert ver.bugfix == 0 assert ver.string == "{0}.{1}.0".format(maj_ver, min_ver) def test_noc_info(self): """ Test noc_info property method """ expect = ( ("v2014.1.4.1rc3-n/a-abcdefff", (2014, 1, 4, 1, "rc", 3, -1)), ("v3.4.1.1", (3, 4, 1, 1, "", 0, 0)), ("v3000", (3000, "", 0, 0)), ("v3000.0", (3000, "", 0, 0)), ("v4518.1", (4518, 1, "", 0, 0)), ("v3000rc1", (3000, "rc", 1, 0)), ("v3000rc1-n/a-abcdefff", (3000, "rc", 1, -1)), ) for vstr, noc_info in expect: saltstack_version = SaltStackVersion.parse(vstr) assert saltstack_version.noc_info, noc_info assert len(saltstack_version.noc_info) == len(noc_info) def test_full_info(self): """ Test full_Info property method """ expect = ( ("v2014.1.4.1rc3-n/a-abcdefff", (2014, 1, 4, 1, "rc", 3, -1, "abcdefff")), ("v3.4.1.1", (3, 4, 1, 1, "", 0, 0, None)), ("v3000", (3000, "", 0, 0, None)), ("v3000.0", (3000, "", 0, 0, None)), ("v4518.1", (4518, 1, "", 0, 0, None)), ("v3000rc1", (3000, "rc", 1, 0, None)), ("v3000rc1-n/a-abcdefff", (3000, "rc", 1, -1, "abcdefff")), ) for vstr, full_info in expect: saltstack_version = SaltStackVersion.parse(vstr) assert saltstack_version.full_info, full_info assert len(saltstack_version.full_info) == len(full_info) def test_full_info_all_versions(self): """ Test full_info_all_versions property method """ expect = ( ("v2014.1.4.1rc3-n/a-abcdefff", (2014, 1, 4, 1, "rc", 3, -1, "abcdefff")), ("v3.4.1.1", (3, 4, 1, 1, "", 0, 0, None)), ("v3000", (3000, None, None, 0, "", 0, 0, None)), ("v3000.0", (3000, 0, None, 0, "", 0, 0, None)), ("v4518.1", (4518, 1, None, 0, "", 0, 0, None)), ("v3000rc1", (3000, None, None, 0, "rc", 2, 0, None)), ("v3000rc1-n/a-abcdefff", (3000, None, None, 0, "rc", 1, -1, "abcdefff")), ) for vstr, full_info in expect: saltstack_version = SaltStackVersion.parse(vstr) assert saltstack_version.full_info_all_versions, full_info assert len(saltstack_version.full_info_all_versions) == len(full_info) def test_discover_version(self): """ Test call to __discover_version when using different versions """ version = { ("3000", None): { (b"v3000.0rc2-12-g44fe283a77\n", "3000rc2-12-g44fe283a77"), (b"v3000", "3000"), (b"1234567", "3000-n/a-1234567"), }, (2019, 2): { (b"v2019.2.0rc2-12-g44fe283a77\n", "2019.2.0rc2-12-g44fe283a77"), (b"v2019.2.0", "2019.2.0"), (b"afc9830198dj", "2019.2.0-n/a-afc9830198dj"), }, } for maj_min, test_v in version.items(): for tag_ver, exp in version[maj_min]: salt_ver = SaltStackVersion( major=maj_min[0], minor=maj_min[1], bugfix=None ) attrs = { "communicate.return_value": (tag_ver, b""), "returncode.return_value": 0, } proc_ret = MagicMock(**attrs) proc_mock = patch("subprocess.Popen", return_value=proc_ret) patch_os = patch("os.path.exists", return_value=True) with proc_mock, patch_os: ret = getattr(salt.version, "__discover_version")(salt_ver) assert ret == exp def test_info_new_version(self): """ test info property method with new versioning scheme """ vers = ((3000, None, None), (3000, 1, None), (3001, 0, None)) for maj_ver, min_ver, bug_fix in vers: ver = SaltStackVersion(major=maj_ver, minor=min_ver, bugfix=bug_fix) if min_ver: assert ver.info == (maj_ver, min_ver) else: assert ver.info == (maj_ver,) def test_info_old_version(self): """ test info property method with old versioning scheme """ vers = ((2019, 2, 1), (2018, 3, 0), (2017, 7, None)) for maj_ver, min_ver, bug_fix in vers: ver = SaltStackVersion(major=maj_ver, minor=min_ver, bugfix=bug_fix) if bug_fix is None: assert ver.info == (maj_ver, min_ver, 0, 0) else: assert ver.info == (maj_ver, min_ver, bug_fix, 0) def test_bugfix_string(self): """ test when bugfix is an empty string """ ret = SaltStackVersion(3000, 1, "", 0, 0, None) assert ret.info == (3000, 1) assert ret.minor == 1 assert ret.bugfix is None def test_version_repr(self): """ Test SaltStackVersion repr for both date and new versioning scheme """ expect = ( ( (3000, 1, None, None, "", 0, 0, None), "", ), ( (3000, 0, None, None, "", 0, 0, None), "", ), ( (2019, 2, 3, None, "", 0, 0, None), "", ), ( (2019, 2, 3, None, "rc", 1, 0, None), "", ), ) for ver, repr_ret in expect: assert repr(SaltStackVersion(*ver)) == repr_ret @skipIf(not salt.utils.platform.is_linux(), "Linux test only") def test_system_version_linux(self): """ version.system_version on Linux """ with patch( "distro.linux_distribution", MagicMock(return_value=("Manjaro Linux", "20.0.2", "Lysia")), ): versions = [item for item in system_information()] version = ("version", "Manjaro Linux 20.0.2 Lysia") self.assertIn(version, versions) with patch( "distro.linux_distribution", MagicMock(return_value=("Debian GNU/Linux", "9", "stretch")), ): versions = [item for item in system_information()] version = ("version", "Debian GNU/Linux 9 stretch") self.assertIn(version, versions) with patch( "distro.linux_distribution", MagicMock(return_value=("Debian GNU/Linux", "10", "buster")), ): versions = [item for item in system_information()] version = ("version", "Debian GNU/Linux 10 buster") self.assertIn(version, versions) with patch( "distro.linux_distribution", MagicMock(return_value=("CentOS Linux", "7", "Core")), ): versions = [item for item in system_information()] version = ("version", "CentOS Linux 7 Core") self.assertIn(version, versions) with patch( "distro.linux_distribution", MagicMock(return_value=("CentOS Linux", "8", "Core")), ): versions = [item for item in system_information()] version = ("version", "CentOS Linux 8 Core") self.assertIn(version, versions) with patch( "distro.linux_distribution", MagicMock(return_value=("OpenSUSE Leap", "15.1", "")), ): versions = [item for item in system_information()] version = ("version", "OpenSUSE Leap 15.1 ") self.assertIn(version, versions) @skipIf(not salt.utils.platform.is_darwin(), "OS X test only") def test_system_version_osx(self): """ version.system_version on OS X """ with patch( "platform.mac_ver", MagicMock(return_value=("10.15.2", ("", "", ""), "x86_64")), ): versions = [item for item in system_information()] version = ("version", "10.15.2 x86_64") self.assertIn(version, versions) @skipIf(not salt.utils.platform.is_windows(), "Windows test only") def test_system_version_windows(self): """ version.system_version on Windows """ with patch( "platform.win32_ver", return_value=("10", "10.0.14393", "SP0", "Multiprocessor Free"), ), patch("win32api.RegOpenKey", MagicMock()), patch( "win32api.RegQueryValueEx", MagicMock(return_value=("Windows Server 2016 Datacenter", 1)), ): versions = [item for item in system_information()] version = ("version", "2016Server 10.0.14393 SP0 Multiprocessor Free") self.assertIn(version, versions)