test_version.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429
  1. # -*- coding: utf-8 -*-
  2. """
  3. :codeauthor: Pedro Algarvio (pedro@algarvio.me)
  4. tests.unit.version_test
  5. ~~~~~~~~~~~~~~~~~~~~~~~
  6. Test salt's regex git describe version parsing
  7. """
  8. # Import python libs
  9. from __future__ import absolute_import
  10. import re
  11. import salt.version
  12. # Import Salt libs
  13. from salt.version import SaltStackVersion, system_information, versions_report
  14. from tests.support.mock import MagicMock, patch
  15. # Import Salt Testing libs
  16. from tests.support.unit import TestCase, skipIf
  17. class VersionTestCase(TestCase):
  18. def test_version_parsing(self):
  19. strip_initial_non_numbers_regex = re.compile(r"(?:[^\d]+)?(?P<vs>.*)")
  20. expect = (
  21. ("v0.12.0-19-g767d4f9", (0, 12, 0, 0, "", 0, 19, "g767d4f9"), None),
  22. ("v0.12.0-85-g2880105", (0, 12, 0, 0, "", 0, 85, "g2880105"), None),
  23. (
  24. "debian/0.11.1+ds-1-3-ga0afcbd",
  25. (0, 11, 1, 0, "", 0, 3, "ga0afcbd"),
  26. "0.11.1-3-ga0afcbd",
  27. ),
  28. ("0.12.1", (0, 12, 1, 0, "", 0, 0, None), None),
  29. ("0.12.1", (0, 12, 1, 0, "", 0, 0, None), None),
  30. ("0.17.0rc1", (0, 17, 0, 0, "rc", 1, 0, None), None),
  31. ("v0.17.0rc1-1-g52ebdfd", (0, 17, 0, 0, "rc", 1, 1, "g52ebdfd"), None),
  32. ("v2014.1.4.1", (2014, 1, 4, 1, "", 0, 0, None), None),
  33. (
  34. "v2014.1.4.1rc3-n/a-abcdefff",
  35. (2014, 1, 4, 1, "rc", 3, -1, "abcdefff"),
  36. None,
  37. ),
  38. ("v3.4.1.1", (3, 4, 1, 1, "", 0, 0, None), None),
  39. ("v3000", (3000, "", 0, 0, None), "3000"),
  40. ("v3000.0", (3000, "", 0, 0, None), "3000"),
  41. ("v4518.1", (4518, 1, "", 0, 0, None), "4518.1"),
  42. ("v3000rc1", (3000, "rc", 1, 0, None), "3000rc1"),
  43. ("v3000rc1-n/a-abcdefff", (3000, "rc", 1, -1, "abcdefff"), None),
  44. ("3000-n/a-1e7bc8f", (3000, "", 0, -1, "1e7bc8f"), None),
  45. ("3000.1-n/a-1e7bc8f", (3000, 1, "", 0, -1, "1e7bc8f"), None),
  46. )
  47. for vstr, full_info, version in expect:
  48. saltstack_version = SaltStackVersion.parse(vstr)
  49. self.assertEqual(saltstack_version.full_info, full_info)
  50. if version is None:
  51. version = strip_initial_non_numbers_regex.search(vstr).group("vs")
  52. self.assertEqual(saltstack_version.string, version)
  53. def test_version_comparison(self):
  54. examples = (
  55. ("debian/0.11.1+ds-1-3-ga0afcbd", "0.11.1+ds-2"),
  56. ("v0.12.0-85-g2880105", "v0.12.0-19-g767d4f9"),
  57. ("v0.17.0rc1-1-g52ebdfd", "0.17.0rc1"),
  58. ("v0.17.0", "v0.17.0rc1"),
  59. ("Hydrogen", "0.17.0"),
  60. ("Helium", "Hydrogen"),
  61. ("v2014.1.4.1-n/a-abcdefff", "v2014.1.4.1rc3-n/a-abcdefff"),
  62. ("v2014.1.4.1-1-abcdefff", "v2014.1.4.1-n/a-abcdefff"),
  63. ("v2016.12.0rc1", "v2016.12.0b1"),
  64. ("v2016.12.0beta1", "v2016.12.0alpha1"),
  65. ("v2016.12.0alpha1", "v2016.12.0alpha0"),
  66. ("v3000.1", "v3000"),
  67. ("v3000rc2", "v3000rc1"),
  68. ("v3001", "v3000"),
  69. ("v4023rc1", "v4022rc1"),
  70. ("v3000", "v3000rc1"),
  71. ("v3000", "v2019.2.1"),
  72. ("v3000.1", "v2019.2.1"),
  73. # we created v3000.0rc1 tag on repo
  74. # but we should not be using this
  75. # version scheme in the future
  76. # but still adding test for it
  77. ("v3000", "v3000.0rc1"),
  78. ("v3000.1rc1", "v3000.0rc1"),
  79. ("v3000", "v2019.2.1rc1"),
  80. ("v3001rc1", "v2019.2.1rc1"),
  81. )
  82. for higher_version, lower_version in examples:
  83. self.assertTrue(SaltStackVersion.parse(higher_version) > lower_version)
  84. self.assertTrue(SaltStackVersion.parse(lower_version) < higher_version)
  85. assert SaltStackVersion.parse(lower_version) != higher_version
  86. def test_unparsable_version(self):
  87. with self.assertRaises(ValueError):
  88. SaltStackVersion.from_name("Drunk")
  89. with self.assertRaises(ValueError):
  90. SaltStackVersion.parse("Drunk")
  91. def test_sha(self):
  92. """
  93. test matching sha's
  94. """
  95. exp_ret = (
  96. ("d6cd1e2bd19e03a81132a23b2025920577f84e37", True),
  97. ("2880105", True),
  98. ("v3000.0.1", False),
  99. ("v0.12.0-85-g2880105", False),
  100. )
  101. for commit, exp in exp_ret:
  102. ret = SaltStackVersion.git_sha_regex.match(commit)
  103. if exp:
  104. assert ret
  105. else:
  106. assert not ret
  107. def test_version_report_lines(self):
  108. """
  109. Validate padding in versions report is correct
  110. """
  111. # Get a set of all version report name lenghts including padding
  112. line_lengths = set(
  113. [
  114. len(line.split(":")[0])
  115. for line in list(versions_report())[4:]
  116. if line != " " and line != "System Versions:"
  117. ]
  118. )
  119. # Check that they are all the same size (only one element in the set)
  120. assert len(line_lengths) == 1
  121. def test_string_new_version(self):
  122. """
  123. Validate string property method
  124. using new versioning scheme
  125. """
  126. maj_ver = "3000"
  127. ver = SaltStackVersion(major=maj_ver)
  128. assert not ver.minor
  129. assert not ver.bugfix
  130. assert maj_ver == ver.string
  131. def test_string_new_version_minor(self):
  132. """
  133. Validate string property method
  134. using new versioning scheme alongside
  135. minor version
  136. """
  137. maj_ver = 3000
  138. min_ver = 1
  139. ver = SaltStackVersion(major=maj_ver, minor=min_ver)
  140. assert ver.minor == min_ver
  141. assert not ver.bugfix
  142. assert ver.string == "{0}.{1}".format(maj_ver, min_ver)
  143. def test_string_new_version_minor_as_string(self):
  144. """
  145. Validate string property method
  146. using new versioning scheme alongside
  147. minor version
  148. """
  149. maj_ver = "3000"
  150. min_ver = "1"
  151. ver = SaltStackVersion(major=maj_ver, minor=min_ver)
  152. assert ver.minor == int(min_ver)
  153. assert not ver.bugfix
  154. assert ver.string == "{0}.{1}".format(maj_ver, min_ver)
  155. # This only seems to happen on a cloned repo without its tags
  156. maj_ver = "3000"
  157. min_ver = ""
  158. ver = SaltStackVersion(major=maj_ver, minor=min_ver)
  159. assert ver.minor is None, "{!r} is not {!r}".format(ver.minor, min_ver)
  160. assert not ver.bugfix
  161. assert ver.string == maj_ver
  162. def test_string_old_version(self):
  163. """
  164. Validate string property method
  165. using old versioning scheme alongside
  166. minor version
  167. """
  168. maj_ver = "2019"
  169. min_ver = "2"
  170. ver = SaltStackVersion(major=maj_ver, minor=min_ver)
  171. assert ver.bugfix == 0
  172. assert ver.string == "{0}.{1}.0".format(maj_ver, min_ver)
  173. def test_noc_info(self):
  174. """
  175. Test noc_info property method
  176. """
  177. expect = (
  178. ("v2014.1.4.1rc3-n/a-abcdefff", (2014, 1, 4, 1, "rc", 3, -1)),
  179. ("v3.4.1.1", (3, 4, 1, 1, "", 0, 0)),
  180. ("v3000", (3000, "", 0, 0)),
  181. ("v3000.0", (3000, "", 0, 0)),
  182. ("v4518.1", (4518, 1, "", 0, 0)),
  183. ("v3000rc1", (3000, "rc", 1, 0)),
  184. ("v3000rc1-n/a-abcdefff", (3000, "rc", 1, -1)),
  185. )
  186. for vstr, noc_info in expect:
  187. saltstack_version = SaltStackVersion.parse(vstr)
  188. assert saltstack_version.noc_info, noc_info
  189. assert len(saltstack_version.noc_info) == len(noc_info)
  190. def test_full_info(self):
  191. """
  192. Test full_Info property method
  193. """
  194. expect = (
  195. ("v2014.1.4.1rc3-n/a-abcdefff", (2014, 1, 4, 1, "rc", 3, -1, "abcdefff")),
  196. ("v3.4.1.1", (3, 4, 1, 1, "", 0, 0, None)),
  197. ("v3000", (3000, "", 0, 0, None)),
  198. ("v3000.0", (3000, "", 0, 0, None)),
  199. ("v4518.1", (4518, 1, "", 0, 0, None)),
  200. ("v3000rc1", (3000, "rc", 1, 0, None)),
  201. ("v3000rc1-n/a-abcdefff", (3000, "rc", 1, -1, "abcdefff")),
  202. )
  203. for vstr, full_info in expect:
  204. saltstack_version = SaltStackVersion.parse(vstr)
  205. assert saltstack_version.full_info, full_info
  206. assert len(saltstack_version.full_info) == len(full_info)
  207. def test_full_info_all_versions(self):
  208. """
  209. Test full_info_all_versions property method
  210. """
  211. expect = (
  212. ("v2014.1.4.1rc3-n/a-abcdefff", (2014, 1, 4, 1, "rc", 3, -1, "abcdefff")),
  213. ("v3.4.1.1", (3, 4, 1, 1, "", 0, 0, None)),
  214. ("v3000", (3000, None, None, 0, "", 0, 0, None)),
  215. ("v3000.0", (3000, 0, None, 0, "", 0, 0, None)),
  216. ("v4518.1", (4518, 1, None, 0, "", 0, 0, None)),
  217. ("v3000rc1", (3000, None, None, 0, "rc", 2, 0, None)),
  218. ("v3000rc1-n/a-abcdefff", (3000, None, None, 0, "rc", 1, -1, "abcdefff")),
  219. )
  220. for vstr, full_info in expect:
  221. saltstack_version = SaltStackVersion.parse(vstr)
  222. assert saltstack_version.full_info_all_versions, full_info
  223. assert len(saltstack_version.full_info_all_versions) == len(full_info)
  224. def test_discover_version(self):
  225. """
  226. Test call to __discover_version
  227. when using different versions
  228. """
  229. version = {
  230. ("3000", None): {
  231. (b"v3000.0rc2-12-g44fe283a77\n", "3000rc2-12-g44fe283a77"),
  232. (b"v3000", "3000"),
  233. (b"1234567", "3000-n/a-1234567"),
  234. },
  235. (2019, 2): {
  236. (b"v2019.2.0rc2-12-g44fe283a77\n", "2019.2.0rc2-12-g44fe283a77"),
  237. (b"v2019.2.0", "2019.2.0"),
  238. (b"afc9830198dj", "2019.2.0-n/a-afc9830198dj"),
  239. },
  240. }
  241. for maj_min, test_v in version.items():
  242. for tag_ver, exp in version[maj_min]:
  243. salt_ver = SaltStackVersion(
  244. major=maj_min[0], minor=maj_min[1], bugfix=None
  245. )
  246. attrs = {
  247. "communicate.return_value": (tag_ver, b""),
  248. "returncode.return_value": 0,
  249. }
  250. proc_ret = MagicMock(**attrs)
  251. proc_mock = patch("subprocess.Popen", return_value=proc_ret)
  252. patch_os = patch("os.path.exists", return_value=True)
  253. with proc_mock, patch_os:
  254. ret = getattr(salt.version, "__discover_version")(salt_ver)
  255. assert ret == exp
  256. def test_info_new_version(self):
  257. """
  258. test info property method with new versioning scheme
  259. """
  260. vers = ((3000, None, None), (3000, 1, None), (3001, 0, None))
  261. for maj_ver, min_ver, bug_fix in vers:
  262. ver = SaltStackVersion(major=maj_ver, minor=min_ver, bugfix=bug_fix)
  263. if min_ver:
  264. assert ver.info == (maj_ver, min_ver)
  265. else:
  266. assert ver.info == (maj_ver,)
  267. def test_info_old_version(self):
  268. """
  269. test info property method with old versioning scheme
  270. """
  271. vers = ((2019, 2, 1), (2018, 3, 0), (2017, 7, None))
  272. for maj_ver, min_ver, bug_fix in vers:
  273. ver = SaltStackVersion(major=maj_ver, minor=min_ver, bugfix=bug_fix)
  274. if bug_fix is None:
  275. assert ver.info == (maj_ver, min_ver, 0, 0)
  276. else:
  277. assert ver.info == (maj_ver, min_ver, bug_fix, 0)
  278. def test_bugfix_string(self):
  279. """
  280. test when bugfix is an empty string
  281. """
  282. ret = SaltStackVersion(3000, 1, "", 0, 0, None)
  283. assert ret.info == (3000, 1)
  284. assert ret.minor == 1
  285. assert ret.bugfix is None
  286. def test_version_repr(self):
  287. """
  288. Test SaltStackVersion repr for both date
  289. and new versioning scheme
  290. """
  291. expect = (
  292. (
  293. (3000, 1, None, None, "", 0, 0, None),
  294. "<SaltStackVersion name='Neon' major=3000 minor=1>",
  295. ),
  296. (
  297. (3000, 0, None, None, "", 0, 0, None),
  298. "<SaltStackVersion name='Neon' major=3000>",
  299. ),
  300. (
  301. (2019, 2, 3, None, "", 0, 0, None),
  302. "<SaltStackVersion name='Fluorine' major=2019 minor=2 bugfix=3>",
  303. ),
  304. (
  305. (2019, 2, 3, None, "rc", 1, 0, None),
  306. "<SaltStackVersion name='Fluorine' major=2019 minor=2 bugfix=3 rc=1>",
  307. ),
  308. )
  309. for ver, repr_ret in expect:
  310. assert repr(SaltStackVersion(*ver)) == repr_ret
  311. @skipIf(not salt.utils.platform.is_linux(), "Linux test only")
  312. def test_system_version_linux(self):
  313. """
  314. version.system_version on Linux
  315. """
  316. with patch(
  317. "distro.linux_distribution",
  318. MagicMock(return_value=("Manjaro Linux", "20.0.2", "Lysia")),
  319. ):
  320. versions = [item for item in system_information()]
  321. version = ("version", "Manjaro Linux 20.0.2 Lysia")
  322. self.assertIn(version, versions)
  323. with patch(
  324. "distro.linux_distribution",
  325. MagicMock(return_value=("Debian GNU/Linux", "9", "stretch")),
  326. ):
  327. versions = [item for item in system_information()]
  328. version = ("version", "Debian GNU/Linux 9 stretch")
  329. self.assertIn(version, versions)
  330. with patch(
  331. "distro.linux_distribution",
  332. MagicMock(return_value=("Debian GNU/Linux", "10", "buster")),
  333. ):
  334. versions = [item for item in system_information()]
  335. version = ("version", "Debian GNU/Linux 10 buster")
  336. self.assertIn(version, versions)
  337. with patch(
  338. "distro.linux_distribution",
  339. MagicMock(return_value=("CentOS Linux", "7", "Core")),
  340. ):
  341. versions = [item for item in system_information()]
  342. version = ("version", "CentOS Linux 7 Core")
  343. self.assertIn(version, versions)
  344. with patch(
  345. "distro.linux_distribution",
  346. MagicMock(return_value=("CentOS Linux", "8", "Core")),
  347. ):
  348. versions = [item for item in system_information()]
  349. version = ("version", "CentOS Linux 8 Core")
  350. self.assertIn(version, versions)
  351. with patch(
  352. "distro.linux_distribution",
  353. MagicMock(return_value=("OpenSUSE Leap", "15.1", "")),
  354. ):
  355. versions = [item for item in system_information()]
  356. version = ("version", "OpenSUSE Leap 15.1 ")
  357. self.assertIn(version, versions)
  358. @skipIf(not salt.utils.platform.is_darwin(), "OS X test only")
  359. def test_system_version_osx(self):
  360. """
  361. version.system_version on OS X
  362. """
  363. with patch(
  364. "platform.mac_ver",
  365. MagicMock(return_value=("10.15.2", ("", "", ""), "x86_64")),
  366. ):
  367. versions = [item for item in system_information()]
  368. version = ("version", "10.15.2 x86_64")
  369. self.assertIn(version, versions)
  370. @skipIf(not salt.utils.platform.is_windows(), "Windows test only")
  371. def test_system_version_windows(self):
  372. """
  373. version.system_version on Windows
  374. """
  375. with patch(
  376. "platform.win32_ver",
  377. return_value=("10", "10.0.14393", "SP0", "Multiprocessor Free"),
  378. ), patch("win32api.RegOpenKey", MagicMock()), patch(
  379. "win32api.RegQueryValueEx",
  380. MagicMock(return_value=("Windows Server 2016 Datacenter", 1)),
  381. ):
  382. versions = [item for item in system_information()]
  383. version = ("version", "2016Server 10.0.14393 SP0 Multiprocessor Free")
  384. self.assertIn(version, versions)