1
0

test_args.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. # -*- coding: utf-8 -*-
  2. # Import python libs
  3. from __future__ import absolute_import, print_function, unicode_literals
  4. from collections import namedtuple
  5. import logging
  6. # Import Salt Libs
  7. from salt.exceptions import SaltInvocationError
  8. from salt.ext import six
  9. import salt.utils.args
  10. # Import Salt Testing Libs
  11. from tests.support.unit import TestCase, skipIf
  12. from tests.support.mock import (
  13. DEFAULT,
  14. NO_MOCK,
  15. NO_MOCK_REASON,
  16. patch
  17. )
  18. log = logging.getLogger(__name__)
  19. class ArgsTestCase(TestCase):
  20. '''
  21. TestCase for salt.utils.args module
  22. '''
  23. def test_condition_input_string(self):
  24. '''
  25. Test passing a jid on the command line
  26. '''
  27. cmd = salt.utils.args.condition_input(['*', 'foo.bar', 20141020201325675584], None)
  28. self.assertIsInstance(cmd[2], six.text_type)
  29. def test_clean_kwargs(self):
  30. self.assertDictEqual(salt.utils.args.clean_kwargs(foo='bar'), {'foo': 'bar'})
  31. self.assertDictEqual(salt.utils.args.clean_kwargs(__pub_foo='bar'), {})
  32. self.assertDictEqual(salt.utils.args.clean_kwargs(__foo_bar='gwar'), {})
  33. self.assertDictEqual(salt.utils.args.clean_kwargs(foo_bar='gwar'), {'foo_bar': 'gwar'})
  34. def test_get_function_argspec(self):
  35. def dummy_func(first, second, third, fourth='fifth'):
  36. pass
  37. expected_argspec = namedtuple('ArgSpec', 'args varargs keywords defaults')(
  38. args=['first', 'second', 'third', 'fourth'], varargs=None, keywords=None, defaults=('fifth',))
  39. ret = salt.utils.args.get_function_argspec(dummy_func)
  40. self.assertEqual(ret, expected_argspec)
  41. def test_parse_kwarg(self):
  42. ret = salt.utils.args.parse_kwarg('foo=bar')
  43. self.assertEqual(ret, ('foo', 'bar'))
  44. ret = salt.utils.args.parse_kwarg('foobar')
  45. self.assertEqual(ret, (None, None))
  46. def test_arg_lookup(self):
  47. def dummy_func(first, second, third, fourth='fifth'):
  48. pass
  49. expected_dict = {'args': ['first', 'second', 'third'], 'kwargs': {'fourth': 'fifth'}}
  50. ret = salt.utils.args.arg_lookup(dummy_func)
  51. self.assertEqual(expected_dict, ret)
  52. @skipIf(NO_MOCK, NO_MOCK_REASON)
  53. def test_format_call(self):
  54. with patch('salt.utils.args.arg_lookup') as arg_lookup:
  55. def dummy_func(first=None, second=None, third=None):
  56. pass
  57. arg_lookup.return_value = {'args': ['first', 'second', 'third'], 'kwargs': {}}
  58. get_function_argspec = DEFAULT
  59. get_function_argspec.return_value = namedtuple('ArgSpec', 'args varargs keywords defaults')(
  60. args=['first', 'second', 'third', 'fourth'], varargs=None, keywords=None, defaults=('fifth',))
  61. # Make sure we raise an error if we don't pass in the requisite number of arguments
  62. self.assertRaises(SaltInvocationError, salt.utils.args.format_call, dummy_func, {'1': 2})
  63. # Make sure we warn on invalid kwargs
  64. self.assertRaises(SaltInvocationError, salt.utils.args.format_call, dummy_func, {'first': 2, 'seconds': 2, 'third': 3})
  65. ret = salt.utils.args.format_call(dummy_func, {'first': 2, 'second': 2, 'third': 3},
  66. expected_extra_kws=('first', 'second', 'third'))
  67. self.assertDictEqual(ret, {'args': [], 'kwargs': {}})
  68. def test_format_call_simple_args(self):
  69. def foo(one, two=2, three=3):
  70. pass
  71. self.assertEqual(
  72. salt.utils.args.format_call(foo, dict(one=10, two=20, three=30)),
  73. {'args': [10], 'kwargs': dict(two=20, three=30)}
  74. )
  75. self.assertEqual(
  76. salt.utils.args.format_call(foo, dict(one=10, two=20)),
  77. {'args': [10], 'kwargs': dict(two=20, three=3)}
  78. )
  79. self.assertEqual(
  80. salt.utils.args.format_call(foo, dict(one=2)),
  81. {'args': [2], 'kwargs': dict(two=2, three=3)}
  82. )
  83. def test_format_call_mimic_typeerror_exceptions(self):
  84. def foo(one, two=2, three=3):
  85. pass
  86. def foo2(one, two, three=3):
  87. pass
  88. with self.assertRaisesRegex(
  89. SaltInvocationError,
  90. r'foo takes at least 1 argument \(0 given\)'):
  91. salt.utils.args.format_call(foo, dict(two=3))
  92. with self.assertRaisesRegex(
  93. TypeError,
  94. r'foo2 takes at least 2 arguments \(1 given\)'):
  95. salt.utils.args.format_call(foo2, dict(one=1))
  96. @skipIf(NO_MOCK, NO_MOCK_REASON)
  97. def test_argspec_report(self):
  98. def _test_spec(arg1, arg2, kwarg1=None):
  99. pass
  100. test_functions = {'test_module.test_spec': _test_spec}
  101. ret = salt.utils.args.argspec_report(test_functions, 'test_module.test_spec')
  102. self.assertDictEqual(ret, {'test_module.test_spec':
  103. {'kwargs': None,
  104. 'args': ['arg1', 'arg2', 'kwarg1'],
  105. 'defaults': (None, ),
  106. 'varargs': None}})
  107. def test_test_mode(self):
  108. self.assertTrue(salt.utils.args.test_mode(test=True))
  109. self.assertTrue(salt.utils.args.test_mode(Test=True))
  110. self.assertTrue(salt.utils.args.test_mode(tEsT=True))
  111. def test_parse_function_no_args(self):
  112. fun, args, kwargs = salt.utils.args.parse_function('amod.afunc()')
  113. self.assertEqual(fun, 'amod.afunc')
  114. self.assertEqual(args, [])
  115. self.assertEqual(kwargs, {})
  116. def test_parse_function_args_only(self):
  117. fun, args, kwargs = salt.utils.args.parse_function('amod.afunc(str1, str2)')
  118. self.assertEqual(fun, 'amod.afunc')
  119. self.assertEqual(args, ['str1', 'str2'])
  120. self.assertEqual(kwargs, {})
  121. def test_parse_function_kwargs_only(self):
  122. fun, args, kwargs = salt.utils.args.parse_function('amod.afunc(kw1=val1, kw2=val2)')
  123. self.assertEqual(fun, 'amod.afunc')
  124. self.assertEqual(args, [])
  125. self.assertEqual(kwargs, {'kw1': 'val1', 'kw2': 'val2'})
  126. def test_parse_function_args_kwargs(self):
  127. fun, args, kwargs = salt.utils.args.parse_function('amod.afunc(str1, str2, kw1=val1, kw2=val2)')
  128. self.assertEqual(fun, 'amod.afunc')
  129. self.assertEqual(args, ['str1', 'str2'])
  130. self.assertEqual(kwargs, {'kw1': 'val1', 'kw2': 'val2'})
  131. def test_parse_function_malformed_no_name(self):
  132. fun, args, kwargs = salt.utils.args.parse_function('(str1, str2, kw1=val1, kw2=val2)')
  133. self.assertIsNone(fun)
  134. self.assertIsNone(args)
  135. self.assertIsNone(kwargs)
  136. def test_parse_function_malformed_not_fun_def(self):
  137. fun, args, kwargs = salt.utils.args.parse_function('foo bar, some=text')
  138. self.assertIsNone(fun)
  139. self.assertIsNone(args)
  140. self.assertIsNone(kwargs)
  141. def test_parse_function_wrong_bracket_style(self):
  142. fun, args, kwargs = salt.utils.args.parse_function('amod.afunc[str1, str2, kw1=val1, kw2=val2]')
  143. self.assertIsNone(fun)
  144. self.assertIsNone(args)
  145. self.assertIsNone(kwargs)
  146. def test_parse_function_brackets_unballanced(self):
  147. fun, args, kwargs = salt.utils.args.parse_function('amod.afunc(str1, str2, kw1=val1, kw2=val2')
  148. self.assertIsNone(fun)
  149. self.assertIsNone(args)
  150. self.assertIsNone(kwargs)
  151. fun, args, kwargs = salt.utils.args.parse_function('amod.afunc(str1, str2, kw1=val1, kw2=val2]')
  152. self.assertIsNone(fun)
  153. self.assertIsNone(args)
  154. self.assertIsNone(kwargs)
  155. fun, args, kwargs = salt.utils.args.parse_function('amod.afunc(str1, str2, kw1=(val1[val2)], kw2=val2)')
  156. self.assertIsNone(fun)
  157. self.assertIsNone(args)
  158. self.assertIsNone(kwargs)
  159. def test_parse_function_brackets_in_quotes(self):
  160. fun, args, kwargs = salt.utils.args.parse_function('amod.afunc(str1, str2, kw1="(val1[val2)]", kw2=val2)')
  161. self.assertEqual(fun, 'amod.afunc')
  162. self.assertEqual(args, ['str1', 'str2'])
  163. self.assertEqual(kwargs, {'kw1': '(val1[val2)]', 'kw2': 'val2'})
  164. def test_parse_function_quotes(self):
  165. fun, args, kwargs = salt.utils.args.parse_function('amod.afunc("double \\" single \'", \'double " single \\\'\', kw1="equal=equal", kw2=val2)')
  166. self.assertEqual(fun, 'amod.afunc')
  167. self.assertEqual(args, ['double " single \'', 'double " single \''])
  168. self.assertEqual(kwargs, {'kw1': 'equal=equal', 'kw2': 'val2'})
  169. def test_yamlify_arg(self):
  170. '''
  171. Test that we properly yamlify CLI input. In several of the tests below
  172. assertIs is used instead of assertEqual. This is because we want to
  173. confirm that the return value is not a copy of the original, but the
  174. same instance as the original.
  175. '''
  176. def _yamlify_arg(item):
  177. log.debug('Testing yamlify_arg with %r', item)
  178. return salt.utils.args.yamlify_arg(item)
  179. # Make sure non-strings are just returned back
  180. for item in (True, False, None, 123, 45.67, ['foo'], {'foo': 'bar'}):
  181. self.assertIs(_yamlify_arg(item), item)
  182. # Make sure whitespace-only isn't loaded as None
  183. for item in ('', '\t', ' '):
  184. self.assertIs(_yamlify_arg(item), item)
  185. # This value would be loaded as an int (123), the underscores would be
  186. # ignored. Test that we identify this case and return the original
  187. # value.
  188. item = '1_2_3'
  189. self.assertIs(_yamlify_arg(item), item)
  190. # The '#' is treated as a comment when not part of a data structure, we
  191. # don't want that behavior
  192. for item in ('# hash at beginning', 'Hello world! # hash elsewhere'):
  193. self.assertIs(_yamlify_arg(item), item)
  194. # However we _do_ want the # to be intact if it _is_ within a data
  195. # structure.
  196. item = '["foo", "bar", "###"]'
  197. self.assertEqual(_yamlify_arg(item), ["foo", "bar", "###"])
  198. item = '{"foo": "###"}'
  199. self.assertEqual(_yamlify_arg(item), {"foo": "###"})
  200. # The string "None" should load _as_ None
  201. self.assertIs(_yamlify_arg('None'), None)
  202. # Leading dashes, or strings containing colons, will result in lists
  203. # and dicts, and we only want to load lists and dicts when the strings
  204. # look like data structures.
  205. for item in ('- foo', 'foo: bar'):
  206. self.assertIs(_yamlify_arg(item), item)
  207. # Make sure we don't load '|' as ''
  208. item = '|'
  209. self.assertIs(_yamlify_arg(item), item)
  210. # Make sure we don't load '!' as something else (None in 2018.3, '' in newer)
  211. item = '!'
  212. self.assertIs(_yamlify_arg(item), item)
  213. # Make sure we load ints, floats, and strings correctly
  214. self.assertEqual(_yamlify_arg('123'), 123)
  215. self.assertEqual(_yamlify_arg('45.67'), 45.67)
  216. self.assertEqual(_yamlify_arg('foo'), 'foo')
  217. # We tested list/dict loading above, but there is separate logic when
  218. # the string contains a '#', so we need to test again here.
  219. self.assertEqual(_yamlify_arg('["foo", "bar"]'), ["foo", "bar"])
  220. self.assertEqual(_yamlify_arg('{"foo": "bar"}'), {"foo": "bar"})
  221. class KwargRegexTest(TestCase):
  222. def test_arguments_regex(self):
  223. argument_matches = (
  224. ('pip=1.1', ('pip', '1.1')),
  225. ('pip==1.1', None),
  226. ('pip=1.2=1', ('pip', '1.2=1')),
  227. )
  228. for argument, match in argument_matches:
  229. if match is None:
  230. self.assertIsNone(salt.utils.args.KWARG_REGEX.match(argument))
  231. else:
  232. self.assertEqual(
  233. salt.utils.args.KWARG_REGEX.match(argument).groups(), match
  234. )