test_decorators.py 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377
  1. # -*- coding: utf-8 -*-
  2. '''
  3. :codeauthor: Bo Maryniuk (bo@suse.de)
  4. unit.utils.decorators_test
  5. '''
  6. # Import Python libs
  7. from __future__ import absolute_import, print_function, unicode_literals
  8. # Import Salt libs
  9. import salt.utils.decorators as decorators
  10. from salt.version import SaltStackVersion
  11. from salt.exceptions import CommandExecutionError, SaltConfigurationError
  12. from tests.support.unit import TestCase
  13. from tests.support.mock import patch
  14. class DummyLogger(object):
  15. '''
  16. Dummy logger accepts everything and simply logs
  17. '''
  18. def __init__(self, messages):
  19. self._messages = messages
  20. def __getattr__(self, item):
  21. return self._log
  22. def _log(self, msg):
  23. self._messages.append(msg)
  24. class DecoratorsTest(TestCase):
  25. '''
  26. Testing decorators.
  27. '''
  28. def old_function(self):
  29. return "old"
  30. def new_function(self):
  31. return "new"
  32. def _new_function(self):
  33. return "old"
  34. def _mk_version(self, name):
  35. '''
  36. Make a version
  37. :return:
  38. '''
  39. return name, SaltStackVersion.from_name(name)
  40. def setUp(self):
  41. '''
  42. Setup a test
  43. :return:
  44. '''
  45. self.globs = {
  46. '__virtualname__': 'test',
  47. '__opts__': {},
  48. '__pillar__': {},
  49. 'old_function': self.old_function,
  50. 'new_function': self.new_function,
  51. '_new_function': self._new_function,
  52. }
  53. self.addCleanup(delattr, self, 'globs')
  54. self.messages = list()
  55. self.addCleanup(delattr, self, 'messages')
  56. patcher = patch.object(decorators, 'log', DummyLogger(self.messages))
  57. patcher.start()
  58. self.addCleanup(patcher.stop)
  59. def test_is_deprecated_version_eol(self):
  60. '''
  61. Use of is_deprecated will result to the exception,
  62. if the expiration version is lower than the current version.
  63. A successor function is not pointed out.
  64. :return:
  65. '''
  66. depr = decorators.is_deprecated(self.globs, "Helium")
  67. depr._curr_version = self._mk_version("Beryllium")[1]
  68. with self.assertRaises(CommandExecutionError):
  69. depr(self.old_function)()
  70. self.assertEqual(self.messages,
  71. ['The lifetime of the function "old_function" expired.'])
  72. def test_is_deprecated_with_successor_eol(self):
  73. '''
  74. Use of is_deprecated will result to the exception,
  75. if the expiration version is lower than the current version.
  76. A successor function is pointed out.
  77. :return:
  78. '''
  79. depr = decorators.is_deprecated(self.globs, "Helium", with_successor="new_function")
  80. depr._curr_version = self._mk_version("Beryllium")[1]
  81. with self.assertRaises(CommandExecutionError):
  82. depr(self.old_function)()
  83. self.assertEqual(self.messages,
  84. ['The lifetime of the function "old_function" expired. '
  85. 'Please use its successor "new_function" instead.'])
  86. def test_is_deprecated(self):
  87. '''
  88. Use of is_deprecated will result to the log message,
  89. if the expiration version is higher than the current version.
  90. A successor function is not pointed out.
  91. :return:
  92. '''
  93. depr = decorators.is_deprecated(self.globs, "Beryllium")
  94. depr._curr_version = self._mk_version("Helium")[1]
  95. self.assertEqual(depr(self.old_function)(), self.old_function())
  96. self.assertEqual(self.messages,
  97. ['The function "old_function" is deprecated '
  98. 'and will expire in version "Beryllium".'])
  99. def test_is_deprecated_with_successor(self):
  100. '''
  101. Use of is_deprecated will result to the log message,
  102. if the expiration version is higher than the current version.
  103. A successor function is pointed out.
  104. :return:
  105. '''
  106. depr = decorators.is_deprecated(self.globs, "Beryllium", with_successor="old_function")
  107. depr._curr_version = self._mk_version("Helium")[1]
  108. self.assertEqual(depr(self.old_function)(), self.old_function())
  109. self.assertEqual(self.messages,
  110. ['The function "old_function" is deprecated '
  111. 'and will expire in version "Beryllium". '
  112. 'Use successor "old_function" instead.'])
  113. def test_with_deprecated_notfound(self):
  114. '''
  115. Test with_deprecated should raise an exception, if a same name
  116. function with the "_" prefix not implemented.
  117. :return:
  118. '''
  119. del self.globs['_new_function']
  120. self.globs['__opts__']['use_deprecated'] = ['test.new_function']
  121. depr = decorators.with_deprecated(self.globs, "Beryllium")
  122. depr._curr_version = self._mk_version("Helium")[1]
  123. with self.assertRaises(CommandExecutionError):
  124. depr(self.new_function)()
  125. self.assertEqual(self.messages,
  126. ['The function "test.new_function" is using its deprecated '
  127. 'version and will expire in version "Beryllium".'])
  128. def test_with_deprecated_notfound_in_pillar(self):
  129. '''
  130. Test with_deprecated should raise an exception, if a same name
  131. function with the "_" prefix not implemented.
  132. :return:
  133. '''
  134. del self.globs['_new_function']
  135. self.globs['__pillar__']['use_deprecated'] = ['test.new_function']
  136. depr = decorators.with_deprecated(self.globs, "Beryllium")
  137. depr._curr_version = self._mk_version("Helium")[1]
  138. with self.assertRaises(CommandExecutionError):
  139. depr(self.new_function)()
  140. self.assertEqual(self.messages,
  141. ['The function "test.new_function" is using its deprecated '
  142. 'version and will expire in version "Beryllium".'])
  143. def test_with_deprecated_found(self):
  144. '''
  145. Test with_deprecated should not raise an exception, if a same name
  146. function with the "_" prefix is implemented, but should use
  147. an old version instead, if "use_deprecated" is requested.
  148. :return:
  149. '''
  150. self.globs['__opts__']['use_deprecated'] = ['test.new_function']
  151. self.globs['_new_function'] = self.old_function
  152. depr = decorators.with_deprecated(self.globs, "Beryllium")
  153. depr._curr_version = self._mk_version("Helium")[1]
  154. self.assertEqual(depr(self.new_function)(), self.old_function())
  155. log_msg = ['The function "test.new_function" is using its deprecated version '
  156. 'and will expire in version "Beryllium".']
  157. self.assertEqual(self.messages, log_msg)
  158. def test_with_deprecated_found_in_pillar(self):
  159. '''
  160. Test with_deprecated should not raise an exception, if a same name
  161. function with the "_" prefix is implemented, but should use
  162. an old version instead, if "use_deprecated" is requested.
  163. :return:
  164. '''
  165. self.globs['__pillar__']['use_deprecated'] = ['test.new_function']
  166. self.globs['_new_function'] = self.old_function
  167. depr = decorators.with_deprecated(self.globs, "Beryllium")
  168. depr._curr_version = self._mk_version("Helium")[1]
  169. self.assertEqual(depr(self.new_function)(), self.old_function())
  170. log_msg = ['The function "test.new_function" is using its deprecated version '
  171. 'and will expire in version "Beryllium".']
  172. self.assertEqual(self.messages, log_msg)
  173. def test_with_deprecated_found_eol(self):
  174. '''
  175. Test with_deprecated should raise an exception, if a same name
  176. function with the "_" prefix is implemented, "use_deprecated" is requested
  177. and EOL is reached.
  178. :return:
  179. '''
  180. self.globs['__opts__']['use_deprecated'] = ['test.new_function']
  181. self.globs['_new_function'] = self.old_function
  182. depr = decorators.with_deprecated(self.globs, "Helium")
  183. depr._curr_version = self._mk_version("Beryllium")[1]
  184. with self.assertRaises(CommandExecutionError):
  185. depr(self.new_function)()
  186. self.assertEqual(self.messages,
  187. ['Although function "new_function" is called, an alias "new_function" '
  188. 'is configured as its deprecated version. The lifetime of the function '
  189. '"new_function" expired. Please use its successor "new_function" instead.'])
  190. def test_with_deprecated_found_eol_in_pillar(self):
  191. '''
  192. Test with_deprecated should raise an exception, if a same name
  193. function with the "_" prefix is implemented, "use_deprecated" is requested
  194. and EOL is reached.
  195. :return:
  196. '''
  197. self.globs['__pillar__']['use_deprecated'] = ['test.new_function']
  198. self.globs['_new_function'] = self.old_function
  199. depr = decorators.with_deprecated(self.globs, "Helium")
  200. depr._curr_version = self._mk_version("Beryllium")[1]
  201. with self.assertRaises(CommandExecutionError):
  202. depr(self.new_function)()
  203. self.assertEqual(self.messages,
  204. ['Although function "new_function" is called, an alias "new_function" '
  205. 'is configured as its deprecated version. The lifetime of the function '
  206. '"new_function" expired. Please use its successor "new_function" instead.'])
  207. def test_with_deprecated_no_conf(self):
  208. '''
  209. Test with_deprecated should not raise an exception, if a same name
  210. function with the "_" prefix is implemented, but should use
  211. a new version instead, if "use_deprecated" is not requested.
  212. :return:
  213. '''
  214. self.globs['_new_function'] = self.old_function
  215. depr = decorators.with_deprecated(self.globs, "Beryllium")
  216. depr._curr_version = self._mk_version("Helium")[1]
  217. self.assertEqual(depr(self.new_function)(), self.new_function())
  218. self.assertFalse(self.messages)
  219. def test_with_deprecated_with_name(self):
  220. '''
  221. Test with_deprecated should not raise an exception, if a different name
  222. function is implemented and specified with the "with_name" parameter,
  223. but should use an old version instead and log a warning log message.
  224. :return:
  225. '''
  226. self.globs['__opts__']['use_deprecated'] = ['test.new_function']
  227. depr = decorators.with_deprecated(self.globs, "Beryllium", with_name="old_function")
  228. depr._curr_version = self._mk_version("Helium")[1]
  229. self.assertEqual(depr(self.new_function)(), self.old_function())
  230. self.assertEqual(self.messages,
  231. ['The function "old_function" is deprecated and will expire in version "Beryllium". '
  232. 'Use its successor "new_function" instead.'])
  233. def test_with_deprecated_with_name_eol(self):
  234. '''
  235. Test with_deprecated should raise an exception, if a different name
  236. function is implemented and specified with the "with_name" parameter
  237. and EOL is reached.
  238. :return:
  239. '''
  240. self.globs['__opts__']['use_deprecated'] = ['test.new_function']
  241. depr = decorators.with_deprecated(self.globs, "Helium", with_name="old_function")
  242. depr._curr_version = self._mk_version("Beryllium")[1]
  243. with self.assertRaises(CommandExecutionError):
  244. depr(self.new_function)()
  245. self.assertEqual(self.messages,
  246. ['Although function "new_function" is called, '
  247. 'an alias "old_function" is configured as its deprecated version. '
  248. 'The lifetime of the function "old_function" expired. '
  249. 'Please use its successor "new_function" instead.'])
  250. def test_with_deprecated_opt_in_default(self):
  251. '''
  252. Test with_deprecated using opt-in policy,
  253. where newer function is not used, unless configured.
  254. :return:
  255. '''
  256. depr = decorators.with_deprecated(self.globs, "Beryllium", policy=decorators._DeprecationDecorator.OPT_IN)
  257. depr._curr_version = self._mk_version("Helium")[1]
  258. assert depr(self.new_function)() == self.old_function()
  259. assert self.messages == ['The function "test.new_function" is using its '
  260. 'deprecated version and will expire in version "Beryllium".']
  261. def test_with_deprecated_opt_in_use_superseded(self):
  262. '''
  263. Test with_deprecated using opt-in policy,
  264. where newer function is used as per configuration.
  265. :return:
  266. '''
  267. self.globs['__opts__']['use_superseded'] = ['test.new_function']
  268. depr = decorators.with_deprecated(self.globs, "Beryllium", policy=decorators._DeprecationDecorator.OPT_IN)
  269. depr._curr_version = self._mk_version("Helium")[1]
  270. assert depr(self.new_function)() == self.new_function()
  271. assert not self.messages
  272. def test_with_deprecated_opt_in_use_superseded_in_pillar(self):
  273. '''
  274. Test with_deprecated using opt-in policy,
  275. where newer function is used as per configuration.
  276. :return:
  277. '''
  278. self.globs['__pillar__']['use_superseded'] = ['test.new_function']
  279. depr = decorators.with_deprecated(self.globs, "Beryllium", policy=decorators._DeprecationDecorator.OPT_IN)
  280. depr._curr_version = self._mk_version("Helium")[1]
  281. assert depr(self.new_function)() == self.new_function()
  282. assert not self.messages
  283. def test_with_deprecated_opt_in_use_superseded_and_deprecated(self):
  284. '''
  285. Test with_deprecated misconfiguration.
  286. :return:
  287. '''
  288. self.globs['__opts__']['use_deprecated'] = ['test.new_function']
  289. self.globs['__opts__']['use_superseded'] = ['test.new_function']
  290. depr = decorators.with_deprecated(self.globs, "Beryllium")
  291. depr._curr_version = self._mk_version("Helium")[1]
  292. with self.assertRaises(SaltConfigurationError):
  293. assert depr(self.new_function)() == self.new_function()
  294. def test_with_deprecated_opt_in_use_superseded_and_deprecated_in_pillar(self):
  295. '''
  296. Test with_deprecated misconfiguration.
  297. :return:
  298. '''
  299. self.globs['__pillar__']['use_deprecated'] = ['test.new_function']
  300. self.globs['__pillar__']['use_superseded'] = ['test.new_function']
  301. depr = decorators.with_deprecated(self.globs, "Beryllium")
  302. depr._curr_version = self._mk_version("Helium")[1]
  303. with self.assertRaises(SaltConfigurationError):
  304. assert depr(self.new_function)() == self.new_function()
  305. def test_with_depreciated_should_wrap_function(self):
  306. wrapped = decorators.with_deprecated({}, "Beryllium")(self.old_function)
  307. assert wrapped.__module__ == self.old_function.__module__
  308. def test_is_deprecated_should_wrap_function(self):
  309. wrapped = decorators.is_deprecated({}, "Beryllium")(self.old_function)
  310. assert wrapped.__module__ == self.old_function.__module__
  311. def test_ensure_unicode_args_should_wrap_function(self):
  312. wrapped = decorators.ensure_unicode_args(self.old_function)
  313. assert wrapped.__module__ == self.old_function.__module__
  314. def test_ignores_kwargs_should_wrap_function(self):
  315. wrapped = decorators.ignores_kwargs('foo', 'bar')(self.old_function)
  316. assert wrapped.__module__ == self.old_function.__module__
  317. def test_memoize_should_wrap_function(self):
  318. wrapped = decorators.memoize(self.old_function)
  319. assert wrapped.__module__ == self.old_function.__module__
  320. def timing_should_wrap_function(self):
  321. wrapped = decorators.timing(self.old_function)
  322. assert wrapped.__module__ == self.old_function.__module__