test_decorators.py 16 KB

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