test_configparser.py 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. # -*- coding: utf-8 -*-
  2. '''
  3. tests.unit.utils.test_configparser
  4. ==================================
  5. Test the funcs in the custom parsers in salt.utils.configparser
  6. '''
  7. # Import Python Libs
  8. from __future__ import absolute_import, print_function, unicode_literals
  9. import copy
  10. import errno
  11. import logging
  12. import os
  13. log = logging.getLogger(__name__)
  14. # Import Salt Testing Libs
  15. from tests.support.runtime import RUNTIME_VARS
  16. from tests.support.unit import TestCase
  17. # Import salt libs
  18. import salt.utils.files
  19. import salt.utils.stringutils
  20. import salt.utils.configparser
  21. import salt.utils.platform
  22. from salt.ext import six
  23. # The user.name param here is intentionally indented with spaces instead of a
  24. # tab to test that we properly load a file with mixed indentation.
  25. ORIG_CONFIG = '''[user]
  26. name = Артём Анисимов
  27. \temail = foo@bar.com
  28. [remote "origin"]
  29. \turl = https://github.com/terminalmage/salt.git
  30. \tfetch = +refs/heads/*:refs/remotes/origin/*
  31. \tpushurl = git@github.com:terminalmage/salt.git
  32. [color "diff"]
  33. \told = 196
  34. \tnew = 39
  35. [core]
  36. \tpager = less -R
  37. \trepositoryformatversion = 0
  38. \tfilemode = true
  39. \tbare = false
  40. \tlogallrefupdates = true
  41. [alias]
  42. \tmodified = ! git status --porcelain | awk 'match($1, "M"){print $2}'
  43. \tgraph = log --all --decorate --oneline --graph
  44. \thist = log --pretty=format:\\"%h %ad | %s%d [%an]\\" --graph --date=short
  45. [http]
  46. \tsslverify = false'''.split('\n')
  47. class TestGitConfigParser(TestCase):
  48. '''
  49. Tests for salt.utils.configparser.GitConfigParser
  50. '''
  51. maxDiff = None
  52. orig_config = os.path.join(RUNTIME_VARS.TMP, 'test_gitconfig.orig')
  53. new_config = os.path.join(RUNTIME_VARS.TMP, 'test_gitconfig.new')
  54. remote = 'remote "origin"'
  55. def tearDown(self):
  56. del self.conf
  57. try:
  58. os.remove(self.new_config)
  59. except OSError as exc:
  60. if exc.errno != errno.ENOENT:
  61. raise
  62. def setUp(self):
  63. if not os.path.exists(self.orig_config):
  64. with salt.utils.files.fopen(self.orig_config, 'wb') as fp_:
  65. fp_.write(
  66. salt.utils.stringutils.to_bytes(
  67. os.linesep.join(ORIG_CONFIG)
  68. )
  69. )
  70. self.conf = salt.utils.configparser.GitConfigParser()
  71. with salt.utils.files.fopen(self.orig_config, 'rb') as fp:
  72. self.conf._read(fp, self.orig_config)
  73. @classmethod
  74. def tearDownClass(cls):
  75. try:
  76. os.remove(cls.orig_config)
  77. except OSError as exc:
  78. if exc.errno != errno.ENOENT:
  79. raise
  80. @staticmethod
  81. def fix_indent(lines):
  82. '''
  83. Fixes the space-indented 'user' line, because when we write the config
  84. object to a file space indentation will be replaced by tab indentation.
  85. '''
  86. ret = copy.copy(lines)
  87. for i, _ in enumerate(ret):
  88. if ret[i].startswith(salt.utils.configparser.GitConfigParser.SPACEINDENT):
  89. ret[i] = ret[i].replace(salt.utils.configparser.GitConfigParser.SPACEINDENT, '\t')
  90. return ret
  91. @staticmethod
  92. def get_lines(path):
  93. with salt.utils.files.fopen(path, 'rb') as fp_:
  94. return salt.utils.stringutils.to_unicode(fp_.read()).splitlines()
  95. def _test_write(self, mode):
  96. kwargs = {'mode': mode}
  97. if six.PY3 and salt.utils.platform.is_windows() and 'b' not in mode:
  98. kwargs['encoding'] = 'utf-8'
  99. with salt.utils.files.fopen(self.new_config, **kwargs) as fp_:
  100. self.conf.write(fp_)
  101. self.assertEqual(
  102. self.get_lines(self.new_config),
  103. self.fix_indent(ORIG_CONFIG)
  104. )
  105. def test_get(self):
  106. '''
  107. Test getting an option's value
  108. '''
  109. # Numeric values should be loaded as strings
  110. self.assertEqual(self.conf.get('color "diff"', 'old'), '196')
  111. # Complex strings should be loaded with their literal quotes and
  112. # slashes intact
  113. self.assertEqual(
  114. self.conf.get('alias', 'modified'),
  115. """! git status --porcelain | awk 'match($1, "M"){print $2}'"""
  116. )
  117. # future lint: disable=non-unicode-string
  118. self.assertEqual(
  119. self.conf.get('alias', 'hist'),
  120. salt.utils.stringutils.to_unicode(
  121. r"""log --pretty=format:\"%h %ad | %s%d [%an]\" --graph --date=short"""
  122. )
  123. )
  124. # future lint: enable=non-unicode-string
  125. def test_read_space_indent(self):
  126. '''
  127. Test that user.name was successfully loaded despite being indented
  128. using spaces instead of a tab. Additionally, this tests that the value
  129. was loaded as a unicode type on PY2.
  130. '''
  131. self.assertEqual(self.conf.get('user', 'name'), u'Артём Анисимов')
  132. def test_set_new_option(self):
  133. '''
  134. Test setting a new option in an existing section
  135. '''
  136. self.conf.set('http', 'useragent', 'myawesomeagent')
  137. self.assertEqual(self.conf.get('http', 'useragent'), 'myawesomeagent')
  138. def test_add_section(self):
  139. '''
  140. Test adding a section and adding an item to that section
  141. '''
  142. self.conf.add_section('foo')
  143. self.conf.set('foo', 'bar', 'baz')
  144. self.assertEqual(self.conf.get('foo', 'bar'), 'baz')
  145. def test_replace_option(self):
  146. '''
  147. Test replacing an existing option
  148. '''
  149. # We're also testing the normalization of key names, here. Setting
  150. # "sslVerify" should actually set an "sslverify" option.
  151. self.conf.set('http', 'sslVerify', 'true')
  152. self.assertEqual(self.conf.get('http', 'sslverify'), 'true')
  153. def test_set_multivar(self):
  154. '''
  155. Test setting a multivar and then writing the resulting file
  156. '''
  157. orig_refspec = '+refs/heads/*:refs/remotes/origin/*'
  158. new_refspec = '+refs/tags/*:refs/tags/*'
  159. # Make sure that the original value is a string
  160. self.assertEqual(
  161. self.conf.get(self.remote, 'fetch'),
  162. orig_refspec
  163. )
  164. # Add another refspec
  165. self.conf.set_multivar(self.remote, 'fetch', new_refspec)
  166. # The value should now be a list
  167. self.assertEqual(
  168. self.conf.get(self.remote, 'fetch'),
  169. [orig_refspec, new_refspec]
  170. )
  171. # Write the config object to a file
  172. with salt.utils.files.fopen(self.new_config, 'wb') as fp_:
  173. self.conf.write(fp_)
  174. # Confirm that the new file was written correctly
  175. expected = self.fix_indent(ORIG_CONFIG)
  176. expected.insert(6, '\tfetch = %s' % new_refspec) # pylint: disable=string-substitution-usage-error
  177. self.assertEqual(self.get_lines(self.new_config), expected)
  178. def test_remove_option(self):
  179. '''
  180. test removing an option, including all items from a multivar
  181. '''
  182. for item in ('fetch', 'pushurl'):
  183. self.conf.remove_option(self.remote, item)
  184. # To confirm that the option is now gone, a get should raise an
  185. # NoOptionError exception.
  186. self.assertRaises(
  187. salt.utils.configparser.NoOptionError,
  188. self.conf.get,
  189. self.remote,
  190. item)
  191. def test_remove_option_regexp(self):
  192. '''
  193. test removing an option, including all items from a multivar
  194. '''
  195. orig_refspec = '+refs/heads/*:refs/remotes/origin/*'
  196. new_refspec_1 = '+refs/tags/*:refs/tags/*'
  197. new_refspec_2 = '+refs/foo/*:refs/foo/*'
  198. # First, add both refspecs
  199. self.conf.set_multivar(self.remote, 'fetch', new_refspec_1)
  200. self.conf.set_multivar(self.remote, 'fetch', new_refspec_2)
  201. # Make sure that all three values are there
  202. self.assertEqual(
  203. self.conf.get(self.remote, 'fetch'),
  204. [orig_refspec, new_refspec_1, new_refspec_2]
  205. )
  206. # If the regex doesn't match, no items should be removed
  207. self.assertFalse(
  208. self.conf.remove_option_regexp(
  209. self.remote,
  210. 'fetch',
  211. salt.utils.stringutils.to_unicode(r'\d{7,10}') # future lint: disable=non-unicode-string
  212. )
  213. )
  214. # Make sure that all three values are still there (since none should
  215. # have been removed)
  216. self.assertEqual(
  217. self.conf.get(self.remote, 'fetch'),
  218. [orig_refspec, new_refspec_1, new_refspec_2]
  219. )
  220. # Remove one of the values
  221. self.assertTrue(
  222. self.conf.remove_option_regexp(self.remote, 'fetch', 'tags'))
  223. # Confirm that the value is gone
  224. self.assertEqual(
  225. self.conf.get(self.remote, 'fetch'),
  226. [orig_refspec, new_refspec_2]
  227. )
  228. # Remove the other one we added earlier
  229. self.assertTrue(
  230. self.conf.remove_option_regexp(self.remote, 'fetch', 'foo'))
  231. # Since the option now only has one value, it should be a string
  232. self.assertEqual(self.conf.get(self.remote, 'fetch'), orig_refspec)
  233. # Remove the last remaining option
  234. self.assertTrue(
  235. self.conf.remove_option_regexp(self.remote, 'fetch', 'heads'))
  236. # Trying to do a get now should raise an exception
  237. self.assertRaises(
  238. salt.utils.configparser.NoOptionError,
  239. self.conf.get,
  240. self.remote,
  241. 'fetch')
  242. def test_write(self):
  243. '''
  244. Test writing using non-binary filehandle
  245. '''
  246. self._test_write(mode='w')
  247. def test_write_binary(self):
  248. '''
  249. Test writing using binary filehandle
  250. '''
  251. self._test_write(mode='wb')