test_versions.py 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. """
  2. tests.unit.version_test
  3. ~~~~~~~~~~~~~~~~~~~~~~~
  4. These tests are copied from python's source `Lib/distutils/tests/test_version.py`
  5. Some new examples were added and some adjustments were made to run tests in python 2 and 3
  6. """
  7. # pylint: disable=string-substitution-usage-error
  8. import datetime
  9. import os
  10. import sys
  11. import warnings
  12. import salt.modules.cmdmod
  13. import salt.utils.platform
  14. import salt.utils.versions
  15. import salt.version
  16. from salt.utils.versions import LooseVersion, StrictVersion
  17. from tests.support.helpers import slowTest
  18. from tests.support.mock import patch
  19. from tests.support.paths import SALT_CODE_DIR
  20. from tests.support.unit import TestCase, skipIf
  21. class VersionTestCase(TestCase):
  22. def test_prerelease(self):
  23. version = StrictVersion("1.2.3a1")
  24. self.assertEqual(version.version, (1, 2, 3))
  25. self.assertEqual(version.prerelease, ("a", 1))
  26. self.assertEqual(str(version), "1.2.3a1")
  27. version = StrictVersion("1.2.0")
  28. self.assertEqual(str(version), "1.2")
  29. def test_cmp_strict(self):
  30. versions = (
  31. ("1.5.1", "1.5.2b2", -1),
  32. ("161", "3.10a", ValueError),
  33. ("8.02", "8.02", 0),
  34. ("3.4j", "1996.07.12", ValueError),
  35. ("3.2.pl0", "3.1.1.6", ValueError),
  36. ("2g6", "11g", ValueError),
  37. ("0.9", "2.2", -1),
  38. ("1.2.1", "1.2", 1),
  39. ("1.1", "1.2.2", -1),
  40. ("1.2", "1.1", 1),
  41. ("1.2.1", "1.2.2", -1),
  42. ("1.2.2", "1.2", 1),
  43. ("1.2", "1.2.2", -1),
  44. ("0.4.0", "0.4", 0),
  45. ("1.13++", "5.5.kw", ValueError),
  46. # Added by us
  47. ("1.1.1a1", "1.1.1", -1),
  48. )
  49. for v1, v2, wanted in versions:
  50. try:
  51. res = StrictVersion(v1)._cmp(StrictVersion(v2))
  52. except ValueError:
  53. if wanted is ValueError:
  54. continue
  55. else:
  56. raise AssertionError(
  57. ("cmp(%s, %s) " "shouldn't raise ValueError") % (v1, v2)
  58. )
  59. self.assertEqual(
  60. res,
  61. wanted,
  62. "cmp({}, {}) should be {}, got {}".format(v1, v2, wanted, res),
  63. )
  64. def test_cmp(self):
  65. versions = (
  66. ("1.5.1", "1.5.2b2", -1),
  67. ("161", "3.10a", 1),
  68. ("8.02", "8.02", 0),
  69. ("3.4j", "1996.07.12", -1),
  70. ("3.2.pl0", "3.1.1.6", 1),
  71. ("2g6", "11g", -1),
  72. ("0.960923", "2.2beta29", -1),
  73. ("1.13++", "5.5.kw", -1),
  74. # Added by us
  75. ("3.10.0-514.el7", "3.10.0-514.6.1.el7", 1),
  76. ("2.2.2", "2.12.1", -1),
  77. )
  78. for v1, v2, wanted in versions:
  79. res = LooseVersion(v1)._cmp(LooseVersion(v2))
  80. self.assertEqual(
  81. res,
  82. wanted,
  83. "cmp({}, {}) should be {}, got {}".format(v1, v2, wanted, res),
  84. )
  85. @skipIf(not salt.utils.platform.is_linux(), "only need to run on linux")
  86. @slowTest
  87. def test_spelling_version_name(self):
  88. """
  89. check the spelling of the version name for the release
  90. names in the salt.utils.versions.warn_until call
  91. """
  92. query = "salt.utils.versions.warn_until("
  93. names = salt.version.SaltStackVersion.NAMES
  94. cmd = ["grep", "-lr", query, "-A", "1", SALT_CODE_DIR]
  95. grep_call = salt.modules.cmdmod.run_stdout(cmd=cmd).split(os.linesep)
  96. for line in grep_call:
  97. num_cmd = salt.modules.cmdmod.run_stdout(
  98. "grep -c {} {}".format(query, line)
  99. )
  100. ver_cmd = salt.modules.cmdmod.run_stdout(
  101. "grep {} {} -A 1".format(query, line)
  102. )
  103. if "pyc" in line:
  104. break
  105. match = 0
  106. for key in names:
  107. if key in ver_cmd:
  108. match = match + (ver_cmd.count(key))
  109. if "utils/__init__.py" in line:
  110. # work around for utils/__init__.py because
  111. # it includes the warn_utils function
  112. match = match + 1
  113. self.assertEqual(
  114. match,
  115. int(num_cmd),
  116. msg="The file: {} has an "
  117. "incorrect spelling for the release name in the warn_utils "
  118. "call: {}. Expecting one of these release names: "
  119. "{}".format(line, ver_cmd, names),
  120. )
  121. class VersionFuncsTestCase(TestCase):
  122. def test_compare(self):
  123. ret = salt.utils.versions.compare("1.0", "==", "1.0")
  124. self.assertTrue(ret)
  125. ret = salt.utils.versions.compare("1.0", "!=", "1.0")
  126. self.assertFalse(ret)
  127. with patch.object(salt.utils.versions, "log") as log_mock:
  128. ret = salt.utils.versions.compare(
  129. "1.0", "HAH I AM NOT A COMP OPERATOR! I AM YOUR FATHER!", "1.0"
  130. )
  131. self.assertTrue(log_mock.error.called)
  132. def test_kwargs_warn_until(self):
  133. # Test invalid version arg
  134. self.assertRaises(RuntimeError, salt.utils.versions.kwargs_warn_until, {}, [])
  135. def test_warn_until_warning_raised(self):
  136. # We *always* want *all* warnings thrown on this module
  137. warnings.filterwarnings("always", "", DeprecationWarning, __name__)
  138. def raise_warning(_version_info_=(0, 16, 0)):
  139. salt.utils.versions.warn_until(
  140. (0, 17), "Deprecation Message!", _version_info_=_version_info_
  141. )
  142. def raise_named_version_warning(_version_info_=(0, 16, 0)):
  143. salt.utils.versions.warn_until(
  144. "Hydrogen", "Deprecation Message!", _version_info_=_version_info_
  145. )
  146. # raise_warning should show warning until version info is >= (0, 17)
  147. with warnings.catch_warnings(record=True) as recorded_warnings:
  148. raise_warning()
  149. self.assertEqual("Deprecation Message!", str(recorded_warnings[0].message))
  150. # raise_warning should show warning until version info is >= (0, 17)
  151. with warnings.catch_warnings(record=True) as recorded_warnings:
  152. raise_named_version_warning()
  153. self.assertEqual("Deprecation Message!", str(recorded_warnings[0].message))
  154. # the deprecation warning is not issued because we passed
  155. # _dont_call_warning
  156. with warnings.catch_warnings(record=True) as recorded_warnings:
  157. salt.utils.versions.warn_until(
  158. (0, 17), "Foo", _dont_call_warnings=True, _version_info_=(0, 16)
  159. )
  160. self.assertEqual(0, len(recorded_warnings))
  161. # Let's set version info to (0, 17), a RuntimeError should be raised
  162. with self.assertRaisesRegex(
  163. RuntimeError,
  164. r"The warning triggered on filename \'(.*)test_versions.py\', "
  165. r"line number ([\d]+), is supposed to be shown until version "
  166. r"0.17.0 is released. Current version is now 0.17.0. "
  167. r"Please remove the warning.",
  168. ):
  169. raise_warning(_version_info_=(0, 17, 0))
  170. # Let's set version info to (0, 17), a RuntimeError should be raised
  171. with self.assertRaisesRegex(
  172. RuntimeError,
  173. r"The warning triggered on filename \'(.*)test_versions.py\', "
  174. r"line number ([\d]+), is supposed to be shown until version "
  175. r"(.*) is released. Current version is now "
  176. r"([\d.]+). Please remove the warning.",
  177. ):
  178. raise_named_version_warning(
  179. _version_info_=(
  180. getattr(sys, "maxint", None) or getattr(sys, "maxsize"),
  181. 16,
  182. 0,
  183. )
  184. )
  185. # Even though we're calling warn_until, we pass _dont_call_warnings
  186. # because we're only after the RuntimeError
  187. with self.assertRaisesRegex(
  188. RuntimeError,
  189. r"The warning triggered on filename \'(.*)test_versions.py\', "
  190. r"line number ([\d]+), is supposed to be shown until version "
  191. r"0.17.0 is released. Current version is now "
  192. r"(.*). Please remove the warning.",
  193. ):
  194. salt.utils.versions.warn_until((0, 17), "Foo", _dont_call_warnings=True)
  195. with self.assertRaisesRegex(
  196. RuntimeError,
  197. r"The warning triggered on filename \'(.*)test_versions.py\', "
  198. r"line number ([\d]+), is supposed to be shown until version "
  199. r"(.*) is released. Current version is now "
  200. r"(.*). Please remove the warning.",
  201. ):
  202. salt.utils.versions.warn_until(
  203. "Hydrogen",
  204. "Foo",
  205. _dont_call_warnings=True,
  206. _version_info_=(
  207. getattr(sys, "maxint", None) or getattr(sys, "maxsize"),
  208. 16,
  209. 0,
  210. ),
  211. )
  212. # version on the deprecation message gets properly formatted
  213. with warnings.catch_warnings(record=True) as recorded_warnings:
  214. vrs = salt.version.SaltStackVersion.from_name("Helium")
  215. salt.utils.versions.warn_until(
  216. "Helium",
  217. "Deprecation Message until {version}!",
  218. _version_info_=(vrs.major - 1, 0),
  219. )
  220. self.assertEqual(
  221. "Deprecation Message until {}!".format(vrs.formatted_version),
  222. str(recorded_warnings[0].message),
  223. )
  224. def test_kwargs_warn_until_warning_raised(self):
  225. # We *always* want *all* warnings thrown on this module
  226. warnings.filterwarnings("always", "", DeprecationWarning, __name__)
  227. def raise_warning(**kwargs):
  228. _version_info_ = kwargs.pop("_version_info_", (0, 16, 0))
  229. salt.utils.versions.kwargs_warn_until(
  230. kwargs, (0, 17), _version_info_=_version_info_
  231. )
  232. # raise_warning({...}) should show warning until version info is >= (0, 17)
  233. with warnings.catch_warnings(record=True) as recorded_warnings:
  234. raise_warning(foo=42) # with a kwarg
  235. self.assertEqual(
  236. "The following parameter(s) have been deprecated and "
  237. "will be removed in '0.17.0': 'foo'.",
  238. str(recorded_warnings[0].message),
  239. )
  240. # With no **kwargs, should not show warning until version info is >= (0, 17)
  241. with warnings.catch_warnings(record=True) as recorded_warnings:
  242. salt.utils.versions.kwargs_warn_until(
  243. {}, (0, 17), _version_info_=(0, 16, 0) # no kwargs
  244. )
  245. self.assertEqual(0, len(recorded_warnings))
  246. # Let's set version info to (0, 17), a RuntimeError should be raised
  247. # regardless of whether or not we pass any **kwargs.
  248. with self.assertRaisesRegex(
  249. RuntimeError,
  250. r"The warning triggered on filename \'(.*)test_versions.py\', "
  251. r"line number ([\d]+), is supposed to be shown until version "
  252. r"0.17.0 is released. Current version is now 0.17.0. "
  253. r"Please remove the warning.",
  254. ):
  255. raise_warning(_version_info_=(0, 17)) # no kwargs
  256. with self.assertRaisesRegex(
  257. RuntimeError,
  258. r"The warning triggered on filename \'(.*)test_versions.py\', "
  259. r"line number ([\d]+), is supposed to be shown until version "
  260. r"0.17.0 is released. Current version is now 0.17.0. "
  261. r"Please remove the warning.",
  262. ):
  263. raise_warning(bar="baz", qux="quux", _version_info_=(0, 17)) # some kwargs
  264. def test_warn_until_date_warning_raised(self):
  265. # We *always* want *all* warnings thrown on this module
  266. warnings.filterwarnings("always", "", DeprecationWarning, __name__)
  267. _current_date = datetime.date(2000, 1, 1)
  268. # Test warning with datetime.date instance
  269. with warnings.catch_warnings(record=True) as recorded_warnings:
  270. salt.utils.versions.warn_until_date(
  271. datetime.date(2000, 1, 2),
  272. "Deprecation Message!",
  273. _current_date=_current_date,
  274. )
  275. self.assertEqual("Deprecation Message!", str(recorded_warnings[0].message))
  276. # Test warning with datetime.datetime instance
  277. with warnings.catch_warnings(record=True) as recorded_warnings:
  278. salt.utils.versions.warn_until_date(
  279. datetime.datetime(2000, 1, 2),
  280. "Deprecation Message!",
  281. _current_date=_current_date,
  282. )
  283. self.assertEqual("Deprecation Message!", str(recorded_warnings[0].message))
  284. # Test warning with date as a string
  285. with warnings.catch_warnings(record=True) as recorded_warnings:
  286. salt.utils.versions.warn_until_date(
  287. "20000102", "Deprecation Message!", _current_date=_current_date
  288. )
  289. self.assertEqual("Deprecation Message!", str(recorded_warnings[0].message))
  290. # the deprecation warning is not issued because we passed
  291. # _dont_call_warning
  292. with warnings.catch_warnings(record=True) as recorded_warnings:
  293. salt.utils.versions.warn_until_date(
  294. "20000102",
  295. "Deprecation Message!",
  296. _dont_call_warnings=True,
  297. _current_date=_current_date,
  298. )
  299. self.assertEqual(0, len(recorded_warnings))
  300. # Let's test for RuntimeError raise
  301. with self.assertRaisesRegex(
  302. RuntimeError,
  303. r"Deprecation Message! This warning\(now exception\) triggered on "
  304. r"filename \'(.*)test_versions.py\', line number ([\d]+), is "
  305. r"supposed to be shown until ([\d-]+). Today is ([\d-]+). "
  306. r"Please remove the warning.",
  307. ):
  308. salt.utils.versions.warn_until_date("20000101", "Deprecation Message!")
  309. # Even though we're calling warn_until_date, we pass _dont_call_warnings
  310. # because we're only after the RuntimeError
  311. with self.assertRaisesRegex(
  312. RuntimeError,
  313. r"Deprecation Message! This warning\(now exception\) triggered on "
  314. r"filename \'(.*)test_versions.py\', line number ([\d]+), is "
  315. r"supposed to be shown until ([\d-]+). Today is ([\d-]+). "
  316. r"Please remove the warning.",
  317. ):
  318. salt.utils.versions.warn_until_date(
  319. "20000101",
  320. "Deprecation Message!",
  321. _dont_call_warnings=True,
  322. _current_date=_current_date,
  323. )
  324. def test_warn_until_date_bad_strptime_format(self):
  325. # We *always* want *all* warnings thrown on this module
  326. warnings.filterwarnings("always", "", DeprecationWarning, __name__)
  327. # Let's test for RuntimeError raise
  328. with self.assertRaisesRegex(
  329. ValueError, "time data '0022' does not match format '%Y%m%d'"
  330. ):
  331. salt.utils.versions.warn_until_date("0022", "Deprecation Message!")