1
0

test_yamlloader.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. # -*- coding: utf-8 -*-
  2. '''
  3. Unit tests for salt.utils.yamlloader.SaltYamlSafeLoader
  4. '''
  5. # Import python libs
  6. from __future__ import absolute_import, print_function, unicode_literals
  7. import collections
  8. import textwrap
  9. # Import Salt Libs
  10. from yaml.constructor import ConstructorError
  11. from salt.utils.yamlloader import SaltYamlSafeLoader
  12. import salt.utils.files
  13. from salt.ext import six
  14. # Import Salt Testing Libs
  15. from tests.support.unit import TestCase
  16. from tests.support.mock import patch, mock_open
  17. # Import 3rd-party libs
  18. from salt.ext import six
  19. class YamlLoaderTestCase(TestCase):
  20. '''
  21. TestCase for salt.utils.yamlloader module
  22. '''
  23. @staticmethod
  24. def render_yaml(data):
  25. '''
  26. Takes a YAML string, puts it into a mock file, passes that to the YAML
  27. SaltYamlSafeLoader and then returns the rendered/parsed YAML data
  28. '''
  29. if six.PY2:
  30. # On Python 2, data read from a filehandle will not already be
  31. # unicode, so we need to encode it first to properly simulate
  32. # reading from a file. This is because unicode_literals is imported
  33. # and all of the data to be used in mock_open will be a unicode
  34. # type. Encoding will make it a str.
  35. data = salt.utils.data.encode(data)
  36. with patch('salt.utils.files.fopen', mock_open(read_data=data)) as mocked_file:
  37. with salt.utils.files.fopen(mocked_file) as mocked_stream:
  38. return SaltYamlSafeLoader(mocked_stream).get_data()
  39. @staticmethod
  40. def raise_error(value):
  41. raise TypeError('{0!r} is not a unicode string'.format(value)) # pylint: disable=repr-flag-used-in-string
  42. def assert_unicode(self, value):
  43. '''
  44. Make sure the entire data structure is unicode
  45. '''
  46. if six.PY3:
  47. return
  48. if isinstance(value, six.string_types):
  49. if not isinstance(value, six.text_type):
  50. self.raise_error(value)
  51. elif isinstance(value, collections.Mapping):
  52. for k, v in six.iteritems(value):
  53. self.assert_unicode(k)
  54. self.assert_unicode(v)
  55. elif isinstance(value, collections.Iterable):
  56. for item in value:
  57. self.assert_unicode(item)
  58. def assert_matches(self, ret, expected):
  59. self.assertEqual(ret, expected)
  60. self.assert_unicode(ret)
  61. def test_yaml_basics(self):
  62. '''
  63. Test parsing an ordinary path
  64. '''
  65. self.assert_matches(
  66. self.render_yaml(textwrap.dedent('''\
  67. p1:
  68. - alpha
  69. - beta''')),
  70. {'p1': ['alpha', 'beta']}
  71. )
  72. def test_yaml_merge(self):
  73. '''
  74. Test YAML anchors
  75. '''
  76. # Simple merge test
  77. self.assert_matches(
  78. self.render_yaml(textwrap.dedent('''\
  79. p1: &p1
  80. v1: alpha
  81. p2:
  82. <<: *p1
  83. v2: beta''')),
  84. {'p1': {'v1': 'alpha'}, 'p2': {'v1': 'alpha', 'v2': 'beta'}}
  85. )
  86. # Test that keys/nodes are overwritten
  87. self.assert_matches(
  88. self.render_yaml(textwrap.dedent('''\
  89. p1: &p1
  90. v1: alpha
  91. p2:
  92. <<: *p1
  93. v1: new_alpha''')),
  94. {'p1': {'v1': 'alpha'}, 'p2': {'v1': 'new_alpha'}}
  95. )
  96. # Test merging of lists
  97. self.assert_matches(
  98. self.render_yaml(textwrap.dedent('''\
  99. p1: &p1
  100. v1: &v1
  101. - t1
  102. - t2
  103. p2:
  104. v2: *v1''')),
  105. {"p2": {"v2": ["t1", "t2"]}, "p1": {"v1": ["t1", "t2"]}}
  106. )
  107. def test_yaml_duplicates(self):
  108. '''
  109. Test that duplicates still throw an error
  110. '''
  111. with self.assertRaises(ConstructorError):
  112. self.render_yaml(textwrap.dedent('''\
  113. p1: alpha
  114. p1: beta'''))
  115. with self.assertRaises(ConstructorError):
  116. self.render_yaml(textwrap.dedent('''\
  117. p1: &p1
  118. v1: alpha
  119. p2:
  120. <<: *p1
  121. v2: beta
  122. v2: betabeta'''))
  123. def test_yaml_with_plain_scalars(self):
  124. '''
  125. Test that plain (i.e. unqoted) string and non-string scalars are
  126. properly handled
  127. '''
  128. self.assert_matches(
  129. self.render_yaml(textwrap.dedent('''\
  130. foo:
  131. b: {foo: bar, one: 1, list: [1, two, 3]}''')),
  132. {'foo': {'b': {'foo': 'bar', 'one': 1, 'list': [1, 'two', 3]}}}
  133. )