test_data.py 51 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286
  1. # -*- coding: utf-8 -*-
  2. '''
  3. Tests for salt.utils.data
  4. '''
  5. # Import Python libs
  6. from __future__ import absolute_import, print_function, unicode_literals
  7. import logging
  8. # Import Salt libs
  9. import salt.utils.data
  10. import salt.utils.stringutils
  11. from salt.utils.odict import OrderedDict
  12. from tests.support.unit import TestCase, LOREM_IPSUM
  13. from tests.support.mock import patch
  14. # Import 3rd party libs
  15. from salt.ext.six.moves import builtins # pylint: disable=import-error,redefined-builtin
  16. from salt.ext import six
  17. log = logging.getLogger(__name__)
  18. _b = lambda x: x.encode('utf-8')
  19. _s = lambda x: salt.utils.stringutils.to_str(x, normalize=True)
  20. # Some randomized data that will not decode
  21. BYTES = b'1\x814\x10'
  22. # This is an example of a unicode string with й constructed using two separate
  23. # code points. Do not modify it.
  24. EGGS = '\u044f\u0438\u0306\u0446\u0430'
  25. class DataTestCase(TestCase):
  26. test_data = [
  27. 'unicode_str',
  28. _b('питон'),
  29. 123,
  30. 456.789,
  31. True,
  32. False,
  33. None,
  34. EGGS,
  35. BYTES,
  36. [123, 456.789, _b('спам'), True, False, None, EGGS, BYTES],
  37. (987, 654.321, _b('яйца'), EGGS, None, (True, EGGS, BYTES)),
  38. {_b('str_key'): _b('str_val'),
  39. None: True,
  40. 123: 456.789,
  41. EGGS: BYTES,
  42. _b('subdict'): {'unicode_key': EGGS,
  43. _b('tuple'): (123, 'hello', _b('world'), True, EGGS, BYTES),
  44. _b('list'): [456, _b('спам'), False, EGGS, BYTES]}},
  45. OrderedDict([(_b('foo'), 'bar'), (123, 456), (EGGS, BYTES)])
  46. ]
  47. def test_sorted_ignorecase(self):
  48. test_list = ['foo', 'Foo', 'bar', 'Bar']
  49. expected_list = ['bar', 'Bar', 'foo', 'Foo']
  50. self.assertEqual(
  51. salt.utils.data.sorted_ignorecase(test_list), expected_list)
  52. def test_mysql_to_dict(self):
  53. test_mysql_output = ['+----+------+-----------+------+---------+------+-------+------------------+',
  54. '| Id | User | Host | db | Command | Time | State | Info |',
  55. '+----+------+-----------+------+---------+------+-------+------------------+',
  56. '| 7 | root | localhost | NULL | Query | 0 | init | show processlist |',
  57. '+----+------+-----------+------+---------+------+-------+------------------+']
  58. ret = salt.utils.data.mysql_to_dict(test_mysql_output, 'Info')
  59. expected_dict = {
  60. 'show processlist': {'Info': 'show processlist', 'db': 'NULL', 'State': 'init', 'Host': 'localhost',
  61. 'Command': 'Query', 'User': 'root', 'Time': 0, 'Id': 7}}
  62. self.assertDictEqual(ret, expected_dict)
  63. def test_subdict_match(self):
  64. test_two_level_dict = {'foo': {'bar': 'baz'}}
  65. test_two_level_comb_dict = {'foo': {'bar': 'baz:woz'}}
  66. test_two_level_dict_and_list = {
  67. 'abc': ['def', 'ghi', {'lorem': {'ipsum': [{'dolor': 'sit'}]}}],
  68. }
  69. test_three_level_dict = {'a': {'b': {'c': 'v'}}}
  70. self.assertTrue(
  71. salt.utils.data.subdict_match(
  72. test_two_level_dict, 'foo:bar:baz'
  73. )
  74. )
  75. # In test_two_level_comb_dict, 'foo:bar' corresponds to 'baz:woz', not
  76. # 'baz'. This match should return False.
  77. self.assertFalse(
  78. salt.utils.data.subdict_match(
  79. test_two_level_comb_dict, 'foo:bar:baz'
  80. )
  81. )
  82. # This tests matching with the delimiter in the value part (in other
  83. # words, that the path 'foo:bar' corresponds to the string 'baz:woz').
  84. self.assertTrue(
  85. salt.utils.data.subdict_match(
  86. test_two_level_comb_dict, 'foo:bar:baz:woz'
  87. )
  88. )
  89. # This would match if test_two_level_comb_dict['foo']['bar'] was equal
  90. # to 'baz:woz:wiz', or if there was more deep nesting. But it does not,
  91. # so this should return False.
  92. self.assertFalse(
  93. salt.utils.data.subdict_match(
  94. test_two_level_comb_dict, 'foo:bar:baz:woz:wiz'
  95. )
  96. )
  97. # This tests for cases when a key path corresponds to a list. The
  98. # value part 'ghi' should be successfully matched as it is a member of
  99. # the list corresponding to key path 'abc'. It is somewhat a
  100. # duplication of a test within test_traverse_dict_and_list, but
  101. # salt.utils.data.subdict_match() does more than just invoke
  102. # salt.utils.traverse_list_and_dict() so this particular assertion is a
  103. # sanity check.
  104. self.assertTrue(
  105. salt.utils.data.subdict_match(
  106. test_two_level_dict_and_list, 'abc:ghi'
  107. )
  108. )
  109. # This tests the use case of a dict embedded in a list, embedded in a
  110. # list, embedded in a dict. This is a rather absurd case, but it
  111. # confirms that match recursion works properly.
  112. self.assertTrue(
  113. salt.utils.data.subdict_match(
  114. test_two_level_dict_and_list, 'abc:lorem:ipsum:dolor:sit'
  115. )
  116. )
  117. # Test four level dict match for reference
  118. self.assertTrue(
  119. salt.utils.data.subdict_match(
  120. test_three_level_dict, 'a:b:c:v'
  121. )
  122. )
  123. # Test regression in 2015.8 where 'a:c:v' would match 'a:b:c:v'
  124. self.assertFalse(
  125. salt.utils.data.subdict_match(
  126. test_three_level_dict, 'a:c:v'
  127. )
  128. )
  129. # Test wildcard match
  130. self.assertTrue(
  131. salt.utils.data.subdict_match(
  132. test_three_level_dict, 'a:*:c:v'
  133. )
  134. )
  135. def test_subdict_match_with_wildcards(self):
  136. '''
  137. Tests subdict matching when wildcards are used in the expression
  138. '''
  139. data = {
  140. 'a': {
  141. 'b': {
  142. 'ç': 'd',
  143. 'é': ['eff', 'gee', '8ch'],
  144. 'ĩ': {'j': 'k'}
  145. }
  146. }
  147. }
  148. assert salt.utils.data.subdict_match(data, '*:*:*:*')
  149. assert salt.utils.data.subdict_match(data, 'a:*:*:*')
  150. assert salt.utils.data.subdict_match(data, 'a:b:*:*')
  151. assert salt.utils.data.subdict_match(data, 'a:b:ç:*')
  152. assert salt.utils.data.subdict_match(data, 'a:b:*:d')
  153. assert salt.utils.data.subdict_match(data, 'a:*:ç:d')
  154. assert salt.utils.data.subdict_match(data, '*:b:ç:d')
  155. assert salt.utils.data.subdict_match(data, '*:*:ç:d')
  156. assert salt.utils.data.subdict_match(data, '*:*:*:d')
  157. assert salt.utils.data.subdict_match(data, 'a:*:*:d')
  158. assert salt.utils.data.subdict_match(data, 'a:b:*:ef*')
  159. assert salt.utils.data.subdict_match(data, 'a:b:*:g*')
  160. assert salt.utils.data.subdict_match(data, 'a:b:*:j:*')
  161. assert salt.utils.data.subdict_match(data, 'a:b:*:j:k')
  162. assert salt.utils.data.subdict_match(data, 'a:b:*:*:k')
  163. assert salt.utils.data.subdict_match(data, 'a:b:*:*:*')
  164. def test_traverse_dict(self):
  165. test_two_level_dict = {'foo': {'bar': 'baz'}}
  166. self.assertDictEqual(
  167. {'not_found': 'nope'},
  168. salt.utils.data.traverse_dict(
  169. test_two_level_dict, 'foo:bar:baz', {'not_found': 'nope'}
  170. )
  171. )
  172. self.assertEqual(
  173. 'baz',
  174. salt.utils.data.traverse_dict(
  175. test_two_level_dict, 'foo:bar', {'not_found': 'not_found'}
  176. )
  177. )
  178. def test_traverse_dict_and_list(self):
  179. test_two_level_dict = {'foo': {'bar': 'baz'}}
  180. test_two_level_dict_and_list = {
  181. 'foo': ['bar', 'baz', {'lorem': {'ipsum': [{'dolor': 'sit'}]}}]
  182. }
  183. # Check traversing too far: salt.utils.data.traverse_dict_and_list() returns
  184. # the value corresponding to a given key path, and baz is a value
  185. # corresponding to the key path foo:bar.
  186. self.assertDictEqual(
  187. {'not_found': 'nope'},
  188. salt.utils.data.traverse_dict_and_list(
  189. test_two_level_dict, 'foo:bar:baz', {'not_found': 'nope'}
  190. )
  191. )
  192. # Now check to ensure that foo:bar corresponds to baz
  193. self.assertEqual(
  194. 'baz',
  195. salt.utils.data.traverse_dict_and_list(
  196. test_two_level_dict, 'foo:bar', {'not_found': 'not_found'}
  197. )
  198. )
  199. # Check traversing too far
  200. self.assertDictEqual(
  201. {'not_found': 'nope'},
  202. salt.utils.data.traverse_dict_and_list(
  203. test_two_level_dict_and_list, 'foo:bar', {'not_found': 'nope'}
  204. )
  205. )
  206. # Check index 1 (2nd element) of list corresponding to path 'foo'
  207. self.assertEqual(
  208. 'baz',
  209. salt.utils.data.traverse_dict_and_list(
  210. test_two_level_dict_and_list, 'foo:1', {'not_found': 'not_found'}
  211. )
  212. )
  213. # Traverse a couple times into dicts embedded in lists
  214. self.assertEqual(
  215. 'sit',
  216. salt.utils.data.traverse_dict_and_list(
  217. test_two_level_dict_and_list,
  218. 'foo:lorem:ipsum:dolor',
  219. {'not_found': 'not_found'}
  220. )
  221. )
  222. def test_compare_dicts(self):
  223. ret = salt.utils.data.compare_dicts(old={'foo': 'bar'}, new={'foo': 'bar'})
  224. self.assertEqual(ret, {})
  225. ret = salt.utils.data.compare_dicts(old={'foo': 'bar'}, new={'foo': 'woz'})
  226. expected_ret = {'foo': {'new': 'woz', 'old': 'bar'}}
  227. self.assertDictEqual(ret, expected_ret)
  228. def test_compare_lists_no_change(self):
  229. ret = salt.utils.data.compare_lists(old=[1, 2, 3, 'a', 'b', 'c'],
  230. new=[1, 2, 3, 'a', 'b', 'c'])
  231. expected = {}
  232. self.assertDictEqual(ret, expected)
  233. def test_compare_lists_changes(self):
  234. ret = salt.utils.data.compare_lists(old=[1, 2, 3, 'a', 'b', 'c'],
  235. new=[1, 2, 4, 'x', 'y', 'z'])
  236. expected = {'new': [4, 'x', 'y', 'z'], 'old': [3, 'a', 'b', 'c']}
  237. self.assertDictEqual(ret, expected)
  238. def test_compare_lists_changes_new(self):
  239. ret = salt.utils.data.compare_lists(old=[1, 2, 3],
  240. new=[1, 2, 3, 'x', 'y', 'z'])
  241. expected = {'new': ['x', 'y', 'z']}
  242. self.assertDictEqual(ret, expected)
  243. def test_compare_lists_changes_old(self):
  244. ret = salt.utils.data.compare_lists(old=[1, 2, 3, 'a', 'b', 'c'],
  245. new=[1, 2, 3])
  246. expected = {'old': ['a', 'b', 'c']}
  247. self.assertDictEqual(ret, expected)
  248. def test_decode(self):
  249. '''
  250. Companion to test_decode_to_str, they should both be kept up-to-date
  251. with one another.
  252. NOTE: This uses the lambda "_b" defined above in the global scope,
  253. which encodes a string to a bytestring, assuming utf-8.
  254. '''
  255. expected = [
  256. 'unicode_str',
  257. 'питон',
  258. 123,
  259. 456.789,
  260. True,
  261. False,
  262. None,
  263. 'яйца',
  264. BYTES,
  265. [123, 456.789, 'спам', True, False, None, 'яйца', BYTES],
  266. (987, 654.321, 'яйца', 'яйца', None, (True, 'яйца', BYTES)),
  267. {'str_key': 'str_val',
  268. None: True,
  269. 123: 456.789,
  270. 'яйца': BYTES,
  271. 'subdict': {'unicode_key': 'яйца',
  272. 'tuple': (123, 'hello', 'world', True, 'яйца', BYTES),
  273. 'list': [456, 'спам', False, 'яйца', BYTES]}},
  274. OrderedDict([('foo', 'bar'), (123, 456), ('яйца', BYTES)])
  275. ]
  276. ret = salt.utils.data.decode(
  277. self.test_data,
  278. keep=True,
  279. normalize=True,
  280. preserve_dict_class=True,
  281. preserve_tuples=True)
  282. self.assertEqual(ret, expected)
  283. # The binary data in the data structure should fail to decode, even
  284. # using the fallback, and raise an exception.
  285. self.assertRaises(
  286. UnicodeDecodeError,
  287. salt.utils.data.decode,
  288. self.test_data,
  289. keep=False,
  290. normalize=True,
  291. preserve_dict_class=True,
  292. preserve_tuples=True)
  293. # Now munge the expected data so that we get what we would expect if we
  294. # disable preservation of dict class and tuples
  295. expected[10] = [987, 654.321, 'яйца', 'яйца', None, [True, 'яйца', BYTES]]
  296. expected[11]['subdict']['tuple'] = [123, 'hello', 'world', True, 'яйца', BYTES]
  297. expected[12] = {'foo': 'bar', 123: 456, 'яйца': BYTES}
  298. ret = salt.utils.data.decode(
  299. self.test_data,
  300. keep=True,
  301. normalize=True,
  302. preserve_dict_class=False,
  303. preserve_tuples=False)
  304. self.assertEqual(ret, expected)
  305. # Now test single non-string, non-data-structure items, these should
  306. # return the same value when passed to this function
  307. for item in (123, 4.56, True, False, None):
  308. log.debug('Testing decode of %s', item)
  309. self.assertEqual(salt.utils.data.decode(item), item)
  310. # Test single strings (not in a data structure)
  311. self.assertEqual(salt.utils.data.decode('foo'), 'foo')
  312. self.assertEqual(salt.utils.data.decode(_b('bar')), 'bar')
  313. self.assertEqual(salt.utils.data.decode(EGGS, normalize=True), 'яйца')
  314. self.assertEqual(salt.utils.data.decode(EGGS, normalize=False), EGGS)
  315. # Test binary blob
  316. self.assertEqual(salt.utils.data.decode(BYTES, keep=True), BYTES)
  317. self.assertRaises(
  318. UnicodeDecodeError,
  319. salt.utils.data.decode,
  320. BYTES,
  321. keep=False)
  322. def test_decode_to_str(self):
  323. '''
  324. Companion to test_decode, they should both be kept up-to-date with one
  325. another.
  326. NOTE: This uses the lambda "_s" defined above in the global scope,
  327. which converts the string/bytestring to a str type.
  328. '''
  329. expected = [
  330. _s('unicode_str'),
  331. _s('питон'),
  332. 123,
  333. 456.789,
  334. True,
  335. False,
  336. None,
  337. _s('яйца'),
  338. BYTES,
  339. [123, 456.789, _s('спам'), True, False, None, _s('яйца'), BYTES],
  340. (987, 654.321, _s('яйца'), _s('яйца'), None, (True, _s('яйца'), BYTES)),
  341. {
  342. _s('str_key'): _s('str_val'),
  343. None: True,
  344. 123: 456.789,
  345. _s('яйца'): BYTES,
  346. _s('subdict'): {
  347. _s('unicode_key'): _s('яйца'),
  348. _s('tuple'): (123, _s('hello'), _s('world'), True, _s('яйца'), BYTES),
  349. _s('list'): [456, _s('спам'), False, _s('яйца'), BYTES]
  350. }
  351. },
  352. OrderedDict([(_s('foo'), _s('bar')), (123, 456), (_s('яйца'), BYTES)])
  353. ]
  354. ret = salt.utils.data.decode(
  355. self.test_data,
  356. keep=True,
  357. normalize=True,
  358. preserve_dict_class=True,
  359. preserve_tuples=True,
  360. to_str=True)
  361. self.assertEqual(ret, expected)
  362. if six.PY3:
  363. # The binary data in the data structure should fail to decode, even
  364. # using the fallback, and raise an exception.
  365. self.assertRaises(
  366. UnicodeDecodeError,
  367. salt.utils.data.decode,
  368. self.test_data,
  369. keep=False,
  370. normalize=True,
  371. preserve_dict_class=True,
  372. preserve_tuples=True,
  373. to_str=True)
  374. # Now munge the expected data so that we get what we would expect if we
  375. # disable preservation of dict class and tuples
  376. expected[10] = [987, 654.321, _s('яйца'), _s('яйца'), None, [True, _s('яйца'), BYTES]]
  377. expected[11][_s('subdict')][_s('tuple')] = [123, _s('hello'), _s('world'), True, _s('яйца'), BYTES]
  378. expected[12] = {_s('foo'): _s('bar'), 123: 456, _s('яйца'): BYTES}
  379. ret = salt.utils.data.decode(
  380. self.test_data,
  381. keep=True,
  382. normalize=True,
  383. preserve_dict_class=False,
  384. preserve_tuples=False,
  385. to_str=True)
  386. self.assertEqual(ret, expected)
  387. # Now test single non-string, non-data-structure items, these should
  388. # return the same value when passed to this function
  389. for item in (123, 4.56, True, False, None):
  390. log.debug('Testing decode of %s', item)
  391. self.assertEqual(salt.utils.data.decode(item, to_str=True), item)
  392. # Test single strings (not in a data structure)
  393. self.assertEqual(salt.utils.data.decode('foo', to_str=True), _s('foo'))
  394. self.assertEqual(salt.utils.data.decode(_b('bar'), to_str=True), _s('bar'))
  395. # Test binary blob
  396. self.assertEqual(
  397. salt.utils.data.decode(BYTES, keep=True, to_str=True),
  398. BYTES
  399. )
  400. if six.PY3:
  401. self.assertRaises(
  402. UnicodeDecodeError,
  403. salt.utils.data.decode,
  404. BYTES,
  405. keep=False,
  406. to_str=True)
  407. def test_decode_fallback(self):
  408. '''
  409. Test fallback to utf-8
  410. '''
  411. with patch.object(builtins, '__salt_system_encoding__', 'ascii'):
  412. self.assertEqual(salt.utils.data.decode(_b('яйца')), 'яйца')
  413. def test_encode(self):
  414. '''
  415. NOTE: This uses the lambda "_b" defined above in the global scope,
  416. which encodes a string to a bytestring, assuming utf-8.
  417. '''
  418. expected = [
  419. _b('unicode_str'),
  420. _b('питон'),
  421. 123,
  422. 456.789,
  423. True,
  424. False,
  425. None,
  426. _b(EGGS),
  427. BYTES,
  428. [123, 456.789, _b('спам'), True, False, None, _b(EGGS), BYTES],
  429. (987, 654.321, _b('яйца'), _b(EGGS), None, (True, _b(EGGS), BYTES)),
  430. {
  431. _b('str_key'): _b('str_val'),
  432. None: True,
  433. 123: 456.789,
  434. _b(EGGS): BYTES,
  435. _b('subdict'): {
  436. _b('unicode_key'): _b(EGGS),
  437. _b('tuple'): (123, _b('hello'), _b('world'), True, _b(EGGS), BYTES),
  438. _b('list'): [456, _b('спам'), False, _b(EGGS), BYTES]
  439. }
  440. },
  441. OrderedDict([(_b('foo'), _b('bar')), (123, 456), (_b(EGGS), BYTES)])
  442. ]
  443. # Both keep=True and keep=False should work because the BYTES data is
  444. # already bytes.
  445. ret = salt.utils.data.encode(
  446. self.test_data,
  447. keep=True,
  448. preserve_dict_class=True,
  449. preserve_tuples=True)
  450. self.assertEqual(ret, expected)
  451. ret = salt.utils.data.encode(
  452. self.test_data,
  453. keep=False,
  454. preserve_dict_class=True,
  455. preserve_tuples=True)
  456. self.assertEqual(ret, expected)
  457. # Now munge the expected data so that we get what we would expect if we
  458. # disable preservation of dict class and tuples
  459. expected[10] = [987, 654.321, _b('яйца'), _b(EGGS), None, [True, _b(EGGS), BYTES]]
  460. expected[11][_b('subdict')][_b('tuple')] = [
  461. 123, _b('hello'), _b('world'), True, _b(EGGS), BYTES
  462. ]
  463. expected[12] = {_b('foo'): _b('bar'), 123: 456, _b(EGGS): BYTES}
  464. ret = salt.utils.data.encode(
  465. self.test_data,
  466. keep=True,
  467. preserve_dict_class=False,
  468. preserve_tuples=False)
  469. self.assertEqual(ret, expected)
  470. ret = salt.utils.data.encode(
  471. self.test_data,
  472. keep=False,
  473. preserve_dict_class=False,
  474. preserve_tuples=False)
  475. self.assertEqual(ret, expected)
  476. # Now test single non-string, non-data-structure items, these should
  477. # return the same value when passed to this function
  478. for item in (123, 4.56, True, False, None):
  479. log.debug('Testing encode of %s', item)
  480. self.assertEqual(salt.utils.data.encode(item), item)
  481. # Test single strings (not in a data structure)
  482. self.assertEqual(salt.utils.data.encode('foo'), _b('foo'))
  483. self.assertEqual(salt.utils.data.encode(_b('bar')), _b('bar'))
  484. # Test binary blob, nothing should happen even when keep=False since
  485. # the data is already bytes
  486. self.assertEqual(salt.utils.data.encode(BYTES, keep=True), BYTES)
  487. self.assertEqual(salt.utils.data.encode(BYTES, keep=False), BYTES)
  488. def test_encode_keep(self):
  489. '''
  490. Whereas we tested the keep argument in test_decode, it is much easier
  491. to do a more comprehensive test of keep in its own function where we
  492. can force the encoding.
  493. '''
  494. unicode_str = 'питон'
  495. encoding = 'ascii'
  496. # Test single string
  497. self.assertEqual(
  498. salt.utils.data.encode(unicode_str, encoding, keep=True),
  499. unicode_str)
  500. self.assertRaises(
  501. UnicodeEncodeError,
  502. salt.utils.data.encode,
  503. unicode_str,
  504. encoding,
  505. keep=False)
  506. data = [
  507. unicode_str,
  508. [b'foo', [unicode_str], {b'key': unicode_str}, (unicode_str,)],
  509. {b'list': [b'foo', unicode_str],
  510. b'dict': {b'key': unicode_str},
  511. b'tuple': (b'foo', unicode_str)},
  512. ([b'foo', unicode_str], {b'key': unicode_str}, (unicode_str,))
  513. ]
  514. # Since everything was a bytestring aside from the bogus data, the
  515. # return data should be identical. We don't need to test recursive
  516. # decoding, that has already been tested in test_encode.
  517. self.assertEqual(
  518. salt.utils.data.encode(data, encoding,
  519. keep=True, preserve_tuples=True),
  520. data
  521. )
  522. self.assertRaises(
  523. UnicodeEncodeError,
  524. salt.utils.data.encode,
  525. data,
  526. encoding,
  527. keep=False,
  528. preserve_tuples=True)
  529. for index, _ in enumerate(data):
  530. self.assertEqual(
  531. salt.utils.data.encode(data[index], encoding,
  532. keep=True, preserve_tuples=True),
  533. data[index]
  534. )
  535. self.assertRaises(
  536. UnicodeEncodeError,
  537. salt.utils.data.encode,
  538. data[index],
  539. encoding,
  540. keep=False,
  541. preserve_tuples=True)
  542. def test_encode_fallback(self):
  543. '''
  544. Test fallback to utf-8
  545. '''
  546. with patch.object(builtins, '__salt_system_encoding__', 'ascii'):
  547. self.assertEqual(salt.utils.data.encode('яйца'), _b('яйца'))
  548. with patch.object(builtins, '__salt_system_encoding__', 'CP1252'):
  549. self.assertEqual(salt.utils.data.encode('Ψ'), _b('Ψ'))
  550. def test_repack_dict(self):
  551. list_of_one_element_dicts = [{'dict_key_1': 'dict_val_1'},
  552. {'dict_key_2': 'dict_val_2'},
  553. {'dict_key_3': 'dict_val_3'}]
  554. expected_ret = {'dict_key_1': 'dict_val_1',
  555. 'dict_key_2': 'dict_val_2',
  556. 'dict_key_3': 'dict_val_3'}
  557. ret = salt.utils.data.repack_dictlist(list_of_one_element_dicts)
  558. self.assertDictEqual(ret, expected_ret)
  559. # Try with yaml
  560. yaml_key_val_pair = '- key1: val1'
  561. ret = salt.utils.data.repack_dictlist(yaml_key_val_pair)
  562. self.assertDictEqual(ret, {'key1': 'val1'})
  563. # Make sure we handle non-yaml junk data
  564. ret = salt.utils.data.repack_dictlist(LOREM_IPSUM)
  565. self.assertDictEqual(ret, {})
  566. def test_stringify(self):
  567. self.assertRaises(TypeError, salt.utils.data.stringify, 9)
  568. self.assertEqual(
  569. salt.utils.data.stringify(['one', 'two', str('three'), 4, 5]), # future lint: disable=blacklisted-function
  570. ['one', 'two', 'three', '4', '5']
  571. )
  572. def test_json_query(self):
  573. # Raises exception if jmespath module is not found
  574. with patch('salt.utils.data.jmespath', None):
  575. self.assertRaisesRegex(
  576. RuntimeError, 'requires jmespath',
  577. salt.utils.data.json_query, {}, '@'
  578. )
  579. # Test search
  580. user_groups = {
  581. 'user1': {'groups': ['group1', 'group2', 'group3']},
  582. 'user2': {'groups': ['group1', 'group2']},
  583. 'user3': {'groups': ['group3']},
  584. }
  585. expression = '*.groups[0]'
  586. primary_groups = ['group1', 'group1', 'group3']
  587. self.assertEqual(
  588. sorted(salt.utils.data.json_query(user_groups, expression)),
  589. primary_groups
  590. )
  591. class FilterFalseyTestCase(TestCase):
  592. '''
  593. Test suite for salt.utils.data.filter_falsey
  594. '''
  595. def test_nop(self):
  596. '''
  597. Test cases where nothing will be done.
  598. '''
  599. # Test with dictionary without recursion
  600. old_dict = {'foo': 'bar', 'bar': {'baz': {'qux': 'quux'}}, 'baz': ['qux', {'foo': 'bar'}]}
  601. new_dict = salt.utils.data.filter_falsey(old_dict)
  602. self.assertEqual(old_dict, new_dict)
  603. # Check returned type equality
  604. self.assertIs(type(old_dict), type(new_dict))
  605. # Test dictionary with recursion
  606. new_dict = salt.utils.data.filter_falsey(old_dict, recurse_depth=3)
  607. self.assertEqual(old_dict, new_dict)
  608. # Test with list
  609. old_list = ['foo', 'bar']
  610. new_list = salt.utils.data.filter_falsey(old_list)
  611. self.assertEqual(old_list, new_list)
  612. # Check returned type equality
  613. self.assertIs(type(old_list), type(new_list))
  614. # Test with set
  615. old_set = set(['foo', 'bar'])
  616. new_set = salt.utils.data.filter_falsey(old_set)
  617. self.assertEqual(old_set, new_set)
  618. # Check returned type equality
  619. self.assertIs(type(old_set), type(new_set))
  620. # Test with OrderedDict
  621. old_dict = OrderedDict([
  622. ('foo', 'bar'),
  623. ('bar', OrderedDict([('qux', 'quux')])),
  624. ('baz', ['qux', OrderedDict([('foo', 'bar')])])
  625. ])
  626. new_dict = salt.utils.data.filter_falsey(old_dict)
  627. self.assertEqual(old_dict, new_dict)
  628. self.assertIs(type(old_dict), type(new_dict))
  629. # Test excluding int
  630. old_list = [0]
  631. new_list = salt.utils.data.filter_falsey(old_list, ignore_types=[type(0)])
  632. self.assertEqual(old_list, new_list)
  633. # Test excluding str (or unicode) (or both)
  634. old_list = ['']
  635. new_list = salt.utils.data.filter_falsey(old_list, ignore_types=[type('')])
  636. self.assertEqual(old_list, new_list)
  637. # Test excluding list
  638. old_list = [[]]
  639. new_list = salt.utils.data.filter_falsey(old_list, ignore_types=[type([])])
  640. self.assertEqual(old_list, new_list)
  641. # Test excluding dict
  642. old_list = [{}]
  643. new_list = salt.utils.data.filter_falsey(old_list, ignore_types=[type({})])
  644. self.assertEqual(old_list, new_list)
  645. def test_filter_dict_no_recurse(self):
  646. '''
  647. Test filtering a dictionary without recursing.
  648. This will only filter out key-values where the values are falsey.
  649. '''
  650. old_dict = {'foo': None,
  651. 'bar': {'baz': {'qux': None, 'quux': '', 'foo': []}},
  652. 'baz': ['qux'],
  653. 'qux': {},
  654. 'quux': []}
  655. new_dict = salt.utils.data.filter_falsey(old_dict)
  656. expect_dict = {'bar': {'baz': {'qux': None, 'quux': '', 'foo': []}}, 'baz': ['qux']}
  657. self.assertEqual(expect_dict, new_dict)
  658. self.assertIs(type(expect_dict), type(new_dict))
  659. def test_filter_dict_recurse(self):
  660. '''
  661. Test filtering a dictionary with recursing.
  662. This will filter out any key-values where the values are falsey or when
  663. the values *become* falsey after filtering their contents (in case they
  664. are lists or dicts).
  665. '''
  666. old_dict = {'foo': None,
  667. 'bar': {'baz': {'qux': None, 'quux': '', 'foo': []}},
  668. 'baz': ['qux'],
  669. 'qux': {},
  670. 'quux': []}
  671. new_dict = salt.utils.data.filter_falsey(old_dict, recurse_depth=3)
  672. expect_dict = {'baz': ['qux']}
  673. self.assertEqual(expect_dict, new_dict)
  674. self.assertIs(type(expect_dict), type(new_dict))
  675. def test_filter_list_no_recurse(self):
  676. '''
  677. Test filtering a list without recursing.
  678. This will only filter out items which are falsey.
  679. '''
  680. old_list = ['foo', None, [], {}, 0, '']
  681. new_list = salt.utils.data.filter_falsey(old_list)
  682. expect_list = ['foo']
  683. self.assertEqual(expect_list, new_list)
  684. self.assertIs(type(expect_list), type(new_list))
  685. # Ensure nested values are *not* filtered out.
  686. old_list = [
  687. 'foo',
  688. ['foo'],
  689. ['foo', None],
  690. {'foo': 0},
  691. {'foo': 'bar', 'baz': []},
  692. [{'foo': ''}],
  693. ]
  694. new_list = salt.utils.data.filter_falsey(old_list)
  695. self.assertEqual(old_list, new_list)
  696. self.assertIs(type(old_list), type(new_list))
  697. def test_filter_list_recurse(self):
  698. '''
  699. Test filtering a list with recursing.
  700. This will filter out any items which are falsey, or which become falsey
  701. after filtering their contents (in case they are lists or dicts).
  702. '''
  703. old_list = [
  704. 'foo',
  705. ['foo'],
  706. ['foo', None],
  707. {'foo': 0},
  708. {'foo': 'bar', 'baz': []},
  709. [{'foo': ''}]
  710. ]
  711. new_list = salt.utils.data.filter_falsey(old_list, recurse_depth=3)
  712. expect_list = ['foo', ['foo'], ['foo'], {'foo': 'bar'}]
  713. self.assertEqual(expect_list, new_list)
  714. self.assertIs(type(expect_list), type(new_list))
  715. def test_filter_set_no_recurse(self):
  716. '''
  717. Test filtering a set without recursing.
  718. Note that a set cannot contain unhashable types, so recursion is not possible.
  719. '''
  720. old_set = set([
  721. 'foo',
  722. None,
  723. 0,
  724. '',
  725. ])
  726. new_set = salt.utils.data.filter_falsey(old_set)
  727. expect_set = set(['foo'])
  728. self.assertEqual(expect_set, new_set)
  729. self.assertIs(type(expect_set), type(new_set))
  730. def test_filter_ordereddict_no_recurse(self):
  731. '''
  732. Test filtering an OrderedDict without recursing.
  733. '''
  734. old_dict = OrderedDict([
  735. ('foo', None),
  736. ('bar', OrderedDict([('baz', OrderedDict([('qux', None), ('quux', ''), ('foo', [])]))])),
  737. ('baz', ['qux']),
  738. ('qux', {}),
  739. ('quux', [])
  740. ])
  741. new_dict = salt.utils.data.filter_falsey(old_dict)
  742. expect_dict = OrderedDict([
  743. ('bar', OrderedDict([('baz', OrderedDict([('qux', None), ('quux', ''), ('foo', [])]))])),
  744. ('baz', ['qux']),
  745. ])
  746. self.assertEqual(expect_dict, new_dict)
  747. self.assertIs(type(expect_dict), type(new_dict))
  748. def test_filter_ordereddict_recurse(self):
  749. '''
  750. Test filtering an OrderedDict with recursing.
  751. '''
  752. old_dict = OrderedDict([
  753. ('foo', None),
  754. ('bar', OrderedDict([('baz', OrderedDict([('qux', None), ('quux', ''), ('foo', [])]))])),
  755. ('baz', ['qux']),
  756. ('qux', {}),
  757. ('quux', [])
  758. ])
  759. new_dict = salt.utils.data.filter_falsey(old_dict, recurse_depth=3)
  760. expect_dict = OrderedDict([
  761. ('baz', ['qux']),
  762. ])
  763. self.assertEqual(expect_dict, new_dict)
  764. self.assertIs(type(expect_dict), type(new_dict))
  765. def test_filter_list_recurse_limit(self):
  766. '''
  767. Test filtering a list with recursing, but with a limited depth.
  768. Note that the top-level is always processed, so a recursion depth of 2
  769. means that two *additional* levels are processed.
  770. '''
  771. old_list = [None, [None, [None, [None]]]]
  772. new_list = salt.utils.data.filter_falsey(old_list, recurse_depth=2)
  773. self.assertEqual([[[[None]]]], new_list)
  774. def test_filter_dict_recurse_limit(self):
  775. '''
  776. Test filtering a dict with recursing, but with a limited depth.
  777. Note that the top-level is always processed, so a recursion depth of 2
  778. means that two *additional* levels are processed.
  779. '''
  780. old_dict = {'one': None,
  781. 'foo': {'two': None, 'bar': {'three': None, 'baz': {'four': None}}}}
  782. new_dict = salt.utils.data.filter_falsey(old_dict, recurse_depth=2)
  783. self.assertEqual({'foo': {'bar': {'baz': {'four': None}}}}, new_dict)
  784. def test_filter_exclude_types(self):
  785. '''
  786. Test filtering a list recursively, but also ignoring (i.e. not filtering)
  787. out certain types that can be falsey.
  788. '''
  789. # Ignore int, unicode
  790. old_list = ['foo', ['foo'], ['foo', None], {'foo': 0}, {'foo': 'bar', 'baz': []}, [{'foo': ''}]]
  791. new_list = salt.utils.data.filter_falsey(old_list, recurse_depth=3, ignore_types=[type(0), type('')])
  792. self.assertEqual(['foo', ['foo'], ['foo'], {'foo': 0}, {'foo': 'bar'}, [{'foo': ''}]], new_list)
  793. # Ignore list
  794. old_list = ['foo', ['foo'], ['foo', None], {'foo': 0}, {'foo': 'bar', 'baz': []}, [{'foo': ''}]]
  795. new_list = salt.utils.data.filter_falsey(old_list, recurse_depth=3, ignore_types=[type([])])
  796. self.assertEqual(['foo', ['foo'], ['foo'], {'foo': 'bar', 'baz': []}, []], new_list)
  797. # Ignore dict
  798. old_list = ['foo', ['foo'], ['foo', None], {'foo': 0}, {'foo': 'bar', 'baz': []}, [{'foo': ''}]]
  799. new_list = salt.utils.data.filter_falsey(old_list, recurse_depth=3, ignore_types=[type({})])
  800. self.assertEqual(['foo', ['foo'], ['foo'], {}, {'foo': 'bar'}, [{}]], new_list)
  801. # Ignore NoneType
  802. old_list = ['foo', ['foo'], ['foo', None], {'foo': 0}, {'foo': 'bar', 'baz': []}, [{'foo': ''}]]
  803. new_list = salt.utils.data.filter_falsey(old_list, recurse_depth=3, ignore_types=[type(None)])
  804. self.assertEqual(['foo', ['foo'], ['foo', None], {'foo': 'bar'}], new_list)
  805. class FilterRecursiveDiff(TestCase):
  806. '''
  807. Test suite for salt.utils.data.recursive_diff
  808. '''
  809. def test_list_equality(self):
  810. '''
  811. Test cases where equal lists are compared.
  812. '''
  813. test_list = [0, 1, 2]
  814. self.assertEqual({}, salt.utils.data.recursive_diff(test_list, test_list))
  815. test_list = [[0], [1], [0, 1, 2]]
  816. self.assertEqual({}, salt.utils.data.recursive_diff(test_list, test_list))
  817. def test_dict_equality(self):
  818. '''
  819. Test cases where equal dicts are compared.
  820. '''
  821. test_dict = {'foo': 'bar', 'bar': {'baz': {'qux': 'quux'}}, 'frop': 0}
  822. self.assertEqual({}, salt.utils.data.recursive_diff(test_dict, test_dict))
  823. def test_ordereddict_equality(self):
  824. '''
  825. Test cases where equal OrderedDicts are compared.
  826. '''
  827. test_dict = OrderedDict([
  828. ('foo', 'bar'),
  829. ('bar', OrderedDict([('baz', OrderedDict([('qux', 'quux')]))])),
  830. ('frop', 0)])
  831. self.assertEqual({}, salt.utils.data.recursive_diff(test_dict, test_dict))
  832. def test_mixed_equality(self):
  833. '''
  834. Test cases where mixed nested lists and dicts are compared.
  835. '''
  836. test_data = {
  837. 'foo': 'bar',
  838. 'baz': [0, 1, 2],
  839. 'bar': {'baz': [{'qux': 'quux'}, {'froop', 0}]}
  840. }
  841. self.assertEqual({}, salt.utils.data.recursive_diff(test_data, test_data))
  842. def test_set_equality(self):
  843. '''
  844. Test cases where equal sets are compared.
  845. '''
  846. test_set = set([0, 1, 2, 3, 'foo'])
  847. self.assertEqual({}, salt.utils.data.recursive_diff(test_set, test_set))
  848. # This is a bit of an oddity, as python seems to sort the sets in memory
  849. # so both sets end up with the same ordering (0..3).
  850. set_one = set([0, 1, 2, 3])
  851. set_two = set([3, 2, 1, 0])
  852. self.assertEqual({}, salt.utils.data.recursive_diff(set_one, set_two))
  853. def test_tuple_equality(self):
  854. '''
  855. Test cases where equal tuples are compared.
  856. '''
  857. test_tuple = (0, 1, 2, 3, 'foo')
  858. self.assertEqual({}, salt.utils.data.recursive_diff(test_tuple, test_tuple))
  859. def test_list_inequality(self):
  860. '''
  861. Test cases where two inequal lists are compared.
  862. '''
  863. list_one = [0, 1, 2]
  864. list_two = ['foo', 'bar', 'baz']
  865. expected_result = {'old': list_one, 'new': list_two}
  866. self.assertEqual(expected_result, salt.utils.data.recursive_diff(list_one, list_two))
  867. expected_result = {'new': list_one, 'old': list_two}
  868. self.assertEqual(expected_result, salt.utils.data.recursive_diff(list_two, list_one))
  869. list_one = [0, 'foo', 1, 'bar']
  870. list_two = [1, 'foo', 1, 'qux']
  871. expected_result = {'old': [0, 'bar'], 'new': [1, 'qux']}
  872. self.assertEqual(expected_result, salt.utils.data.recursive_diff(list_one, list_two))
  873. expected_result = {'new': [0, 'bar'], 'old': [1, 'qux']}
  874. self.assertEqual(expected_result, salt.utils.data.recursive_diff(list_two, list_one))
  875. list_one = [0, 1, [2, 3]]
  876. list_two = [0, 1, ['foo', 'bar']]
  877. expected_result = {'old': [[2, 3]], 'new': [['foo', 'bar']]}
  878. self.assertEqual(expected_result, salt.utils.data.recursive_diff(list_one, list_two))
  879. expected_result = {'new': [[2, 3]], 'old': [['foo', 'bar']]}
  880. self.assertEqual(expected_result, salt.utils.data.recursive_diff(list_two, list_one))
  881. def test_dict_inequality(self):
  882. '''
  883. Test cases where two inequal dicts are compared.
  884. '''
  885. dict_one = {'foo': 1, 'bar': 2, 'baz': 3}
  886. dict_two = {'foo': 2, 1: 'bar', 'baz': 3}
  887. expected_result = {'old': {'foo': 1, 'bar': 2}, 'new': {'foo': 2, 1: 'bar'}}
  888. self.assertEqual(expected_result, salt.utils.data.recursive_diff(dict_one, dict_two))
  889. expected_result = {'new': {'foo': 1, 'bar': 2}, 'old': {'foo': 2, 1: 'bar'}}
  890. self.assertEqual(expected_result, salt.utils.data.recursive_diff(dict_two, dict_one))
  891. dict_one = {'foo': {'bar': {'baz': 1}}}
  892. dict_two = {'foo': {'qux': {'baz': 1}}}
  893. expected_result = {'old': dict_one, 'new': dict_two}
  894. self.assertEqual(expected_result, salt.utils.data.recursive_diff(dict_one, dict_two))
  895. expected_result = {'new': dict_one, 'old': dict_two}
  896. self.assertEqual(expected_result, salt.utils.data.recursive_diff(dict_two, dict_one))
  897. def test_ordereddict_inequality(self):
  898. '''
  899. Test cases where two inequal OrderedDicts are compared.
  900. '''
  901. odict_one = OrderedDict([('foo', 'bar'), ('bar', 'baz')])
  902. odict_two = OrderedDict([('bar', 'baz'), ('foo', 'bar')])
  903. expected_result = {'old': odict_one, 'new': odict_two}
  904. self.assertEqual(expected_result, salt.utils.data.recursive_diff(odict_one, odict_two))
  905. def test_set_inequality(self):
  906. '''
  907. Test cases where two inequal sets are compared.
  908. Tricky as the sets are compared zipped, so shuffled sets of equal values
  909. are considered different.
  910. '''
  911. set_one = set([0, 1, 2, 4])
  912. set_two = set([0, 1, 3, 4])
  913. expected_result = {'old': set([2]), 'new': set([3])}
  914. self.assertEqual(expected_result, salt.utils.data.recursive_diff(set_one, set_two))
  915. expected_result = {'new': set([2]), 'old': set([3])}
  916. self.assertEqual(expected_result, salt.utils.data.recursive_diff(set_two, set_one))
  917. # It is unknown how different python versions will store sets in memory.
  918. # Python 2.7 seems to sort it (i.e. set_one below becomes {0, 1, 'foo', 'bar'}
  919. # However Python 3.6.8 stores it differently each run.
  920. # So just test for "not equal" here.
  921. set_one = set([0, 'foo', 1, 'bar'])
  922. set_two = set(['foo', 1, 'bar', 2])
  923. expected_result = {}
  924. self.assertNotEqual(expected_result, salt.utils.data.recursive_diff(set_one, set_two))
  925. def test_mixed_inequality(self):
  926. '''
  927. Test cases where two mixed dicts/iterables that are different are compared.
  928. '''
  929. dict_one = {'foo': [1, 2, 3]}
  930. dict_two = {'foo': [3, 2, 1]}
  931. expected_result = {'old': {'foo': [1, 3]}, 'new': {'foo': [3, 1]}}
  932. self.assertEqual(expected_result, salt.utils.data.recursive_diff(dict_one, dict_two))
  933. expected_result = {'new': {'foo': [1, 3]}, 'old': {'foo': [3, 1]}}
  934. self.assertEqual(expected_result, salt.utils.data.recursive_diff(dict_two, dict_one))
  935. list_one = [1, 2, {'foo': ['bar', {'foo': 1, 'bar': 2}]}]
  936. list_two = [3, 4, {'foo': ['qux', {'foo': 1, 'bar': 2}]}]
  937. expected_result = {'old': [1, 2, {'foo': ['bar']}], 'new': [3, 4, {'foo': ['qux']}]}
  938. self.assertEqual(expected_result, salt.utils.data.recursive_diff(list_one, list_two))
  939. expected_result = {'new': [1, 2, {'foo': ['bar']}], 'old': [3, 4, {'foo': ['qux']}]}
  940. self.assertEqual(expected_result, salt.utils.data.recursive_diff(list_two, list_one))
  941. mixed_one = {'foo': set([0, 1, 2]), 'bar': [0, 1, 2]}
  942. mixed_two = {'foo': set([1, 2, 3]), 'bar': [1, 2, 3]}
  943. expected_result = {
  944. 'old': {'foo': set([0]), 'bar': [0, 1, 2]},
  945. 'new': {'foo': set([3]), 'bar': [1, 2, 3]}
  946. }
  947. self.assertEqual(expected_result, salt.utils.data.recursive_diff(mixed_one, mixed_two))
  948. expected_result = {
  949. 'new': {'foo': set([0]), 'bar': [0, 1, 2]},
  950. 'old': {'foo': set([3]), 'bar': [1, 2, 3]}
  951. }
  952. self.assertEqual(expected_result, salt.utils.data.recursive_diff(mixed_two, mixed_one))
  953. def test_tuple_inequality(self):
  954. '''
  955. Test cases where two tuples that are different are compared.
  956. '''
  957. tuple_one = (1, 2, 3)
  958. tuple_two = (3, 2, 1)
  959. expected_result = {'old': (1, 3), 'new': (3, 1)}
  960. self.assertEqual(expected_result, salt.utils.data.recursive_diff(tuple_one, tuple_two))
  961. def test_list_vs_set(self):
  962. '''
  963. Test case comparing a list with a set, will be compared unordered.
  964. '''
  965. mixed_one = [1, 2, 3]
  966. mixed_two = set([3, 2, 1])
  967. expected_result = {}
  968. self.assertEqual(expected_result, salt.utils.data.recursive_diff(mixed_one, mixed_two))
  969. self.assertEqual(expected_result, salt.utils.data.recursive_diff(mixed_two, mixed_one))
  970. def test_dict_vs_ordereddict(self):
  971. '''
  972. Test case comparing a dict with an ordereddict, will be compared unordered.
  973. '''
  974. test_dict = {'foo': 'bar', 'bar': 'baz'}
  975. test_odict = OrderedDict([('foo', 'bar'), ('bar', 'baz')])
  976. self.assertEqual({}, salt.utils.data.recursive_diff(test_dict, test_odict))
  977. self.assertEqual({}, salt.utils.data.recursive_diff(test_odict, test_dict))
  978. test_odict2 = OrderedDict([('bar', 'baz'), ('foo', 'bar')])
  979. self.assertEqual({}, salt.utils.data.recursive_diff(test_dict, test_odict2))
  980. self.assertEqual({}, salt.utils.data.recursive_diff(test_odict2, test_dict))
  981. def test_list_ignore_ignored(self):
  982. '''
  983. Test case comparing two lists with ignore-list supplied (which is not used
  984. when comparing lists).
  985. '''
  986. list_one = [1, 2, 3]
  987. list_two = [3, 2, 1]
  988. expected_result = {'old': [1, 3], 'new': [3, 1]}
  989. self.assertEqual(
  990. expected_result,
  991. salt.utils.data.recursive_diff(list_one, list_two, ignore_keys=[1, 3])
  992. )
  993. def test_dict_ignore(self):
  994. '''
  995. Test case comparing two dicts with ignore-list supplied.
  996. '''
  997. dict_one = {'foo': 1, 'bar': 2, 'baz': 3}
  998. dict_two = {'foo': 3, 'bar': 2, 'baz': 1}
  999. expected_result = {'old': {'baz': 3}, 'new': {'baz': 1}}
  1000. self.assertEqual(
  1001. expected_result,
  1002. salt.utils.data.recursive_diff(dict_one, dict_two, ignore_keys=['foo'])
  1003. )
  1004. def test_ordereddict_ignore(self):
  1005. '''
  1006. Test case comparing two OrderedDicts with ignore-list supplied.
  1007. '''
  1008. odict_one = OrderedDict([('foo', 1), ('bar', 2), ('baz', 3)])
  1009. odict_two = OrderedDict([('baz', 1), ('bar', 2), ('foo', 3)])
  1010. # The key 'foo' will be ignored, which means the key from the other OrderedDict
  1011. # will always be considered "different" since OrderedDicts are compared ordered.
  1012. expected_result = {'old': OrderedDict([('baz', 3)]), 'new': OrderedDict([('baz', 1)])}
  1013. self.assertEqual(
  1014. expected_result,
  1015. salt.utils.data.recursive_diff(odict_one, odict_two, ignore_keys=['foo'])
  1016. )
  1017. def test_dict_vs_ordereddict_ignore(self):
  1018. '''
  1019. Test case comparing a dict with an OrderedDict with ignore-list supplied.
  1020. '''
  1021. dict_one = {'foo': 1, 'bar': 2, 'baz': 3}
  1022. odict_two = OrderedDict([('foo', 3), ('bar', 2), ('baz', 1)])
  1023. expected_result = {'old': {'baz': 3}, 'new': OrderedDict([('baz', 1)])}
  1024. self.assertEqual(
  1025. expected_result,
  1026. salt.utils.data.recursive_diff(dict_one, odict_two, ignore_keys=['foo'])
  1027. )
  1028. def test_mixed_nested_ignore(self):
  1029. '''
  1030. Test case comparing mixed, nested items with ignore-list supplied.
  1031. '''
  1032. dict_one = {'foo': [1], 'bar': {'foo': 1, 'bar': 2}, 'baz': 3}
  1033. dict_two = {'foo': [2], 'bar': {'foo': 3, 'bar': 2}, 'baz': 1}
  1034. expected_result = {'old': {'baz': 3}, 'new': {'baz': 1}}
  1035. self.assertEqual(
  1036. expected_result,
  1037. salt.utils.data.recursive_diff(dict_one, dict_two, ignore_keys=['foo'])
  1038. )
  1039. def test_ordered_dict_unequal_length(self):
  1040. '''
  1041. Test case comparing two OrderedDicts of unequal length.
  1042. '''
  1043. odict_one = OrderedDict([('foo', 1), ('bar', 2), ('baz', 3)])
  1044. odict_two = OrderedDict([('foo', 1), ('bar', 2)])
  1045. expected_result = {'old': OrderedDict([('baz', 3)]), 'new': {}}
  1046. self.assertEqual(
  1047. expected_result,
  1048. salt.utils.data.recursive_diff(odict_one, odict_two)
  1049. )
  1050. def test_list_unequal_length(self):
  1051. '''
  1052. Test case comparing two lists of unequal length.
  1053. '''
  1054. list_one = [1, 2, 3]
  1055. list_two = [1, 2, 3, 4]
  1056. expected_result = {'old': [], 'new': [4]}
  1057. self.assertEqual(
  1058. expected_result,
  1059. salt.utils.data.recursive_diff(list_one, list_two)
  1060. )
  1061. def test_set_unequal_length(self):
  1062. '''
  1063. Test case comparing two sets of unequal length.
  1064. This does not do anything special, as it is unordered.
  1065. '''
  1066. set_one = set([1, 2, 3])
  1067. set_two = set([4, 3, 2, 1])
  1068. expected_result = {'old': set([]), 'new': set([4])}
  1069. self.assertEqual(
  1070. expected_result,
  1071. salt.utils.data.recursive_diff(set_one, set_two)
  1072. )
  1073. def test_tuple_unequal_length(self):
  1074. '''
  1075. Test case comparing two tuples of unequal length.
  1076. This should be the same as comparing two ordered lists.
  1077. '''
  1078. tuple_one = (1, 2, 3)
  1079. tuple_two = (1, 2, 3, 4)
  1080. expected_result = {'old': (), 'new': (4,)}
  1081. self.assertEqual(
  1082. expected_result,
  1083. salt.utils.data.recursive_diff(tuple_one, tuple_two)
  1084. )
  1085. def test_list_unordered(self):
  1086. '''
  1087. Test case comparing two lists unordered.
  1088. '''
  1089. list_one = [1, 2, 3, 4]
  1090. list_two = [4, 3, 2]
  1091. expected_result = {'old': [1], 'new': []}
  1092. self.assertEqual(
  1093. expected_result,
  1094. salt.utils.data.recursive_diff(list_one, list_two, ignore_order=True)
  1095. )
  1096. def test_mixed_nested_unordered(self):
  1097. '''
  1098. Test case comparing nested dicts/lists unordered.
  1099. '''
  1100. dict_one = {'foo': {'bar': [1, 2, 3]}, 'bar': [{'foo': 4}, 0]}
  1101. dict_two = {'foo': {'bar': [3, 2, 1]}, 'bar': [0, {'foo': 4}]}
  1102. expected_result = {}
  1103. self.assertEqual(
  1104. expected_result,
  1105. salt.utils.data.recursive_diff(dict_one, dict_two, ignore_order=True)
  1106. )
  1107. expected_result = {
  1108. 'old': {'foo': {'bar': [1, 3]}, 'bar': [{'foo': 4}, 0]},
  1109. 'new': {'foo': {'bar': [3, 1]}, 'bar': [0, {'foo': 4}]},
  1110. }
  1111. self.assertEqual(
  1112. expected_result,
  1113. salt.utils.data.recursive_diff(dict_one, dict_two)
  1114. )
  1115. def test_ordered_dict_unordered(self):
  1116. '''
  1117. Test case comparing OrderedDicts unordered.
  1118. '''
  1119. odict_one = OrderedDict([('foo', 1), ('bar', 2), ('baz', 3)])
  1120. odict_two = OrderedDict([('baz', 3), ('bar', 2), ('foo', 1)])
  1121. expected_result = {}
  1122. self.assertEqual(
  1123. expected_result,
  1124. salt.utils.data.recursive_diff(odict_one, odict_two, ignore_order=True)
  1125. )
  1126. def test_ignore_missing_keys_dict(self):
  1127. '''
  1128. Test case ignoring missing keys on a comparison of dicts.
  1129. '''
  1130. dict_one = {'foo': 1, 'bar': 2, 'baz': 3}
  1131. dict_two = {'bar': 3}
  1132. expected_result = {'old': {'bar': 2}, 'new': {'bar': 3}}
  1133. self.assertEqual(
  1134. expected_result,
  1135. salt.utils.data.recursive_diff(dict_one, dict_two, ignore_missing_keys=True)
  1136. )
  1137. def test_ignore_missing_keys_ordered_dict(self):
  1138. '''
  1139. Test case not ignoring missing keys on a comparison of OrderedDicts.
  1140. '''
  1141. odict_one = OrderedDict([('foo', 1), ('bar', 2), ('baz', 3)])
  1142. odict_two = OrderedDict([('bar', 3)])
  1143. expected_result = {'old': odict_one, 'new': odict_two}
  1144. self.assertEqual(
  1145. expected_result,
  1146. salt.utils.data.recursive_diff(odict_one, odict_two, ignore_missing_keys=True)
  1147. )
  1148. def test_ignore_missing_keys_recursive(self):
  1149. '''
  1150. Test case ignoring missing keys on a comparison of nested dicts.
  1151. '''
  1152. dict_one = {'foo': {'bar': 2, 'baz': 3}}
  1153. dict_two = {'foo': {'baz': 3}}
  1154. expected_result = {}
  1155. self.assertEqual(
  1156. expected_result,
  1157. salt.utils.data.recursive_diff(dict_one, dict_two, ignore_missing_keys=True)
  1158. )
  1159. # Compare from dict-in-dict
  1160. dict_two = {}
  1161. self.assertEqual(
  1162. expected_result,
  1163. salt.utils.data.recursive_diff(dict_one, dict_two, ignore_missing_keys=True)
  1164. )
  1165. # Compare from dict-in-list
  1166. dict_one = {'foo': ['bar', {'baz': 3}]}
  1167. dict_two = {'foo': ['bar', {}]}
  1168. self.assertEqual(
  1169. expected_result,
  1170. salt.utils.data.recursive_diff(dict_one, dict_two, ignore_missing_keys=True)
  1171. )