You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

375 lines
16KB

  1. # test_config.py
  2. # Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors
  3. #
  4. # This module is part of GitPython and is released under
  5. # the BSD License: http://www.opensource.org/licenses/bsd-license.php
  6. import glob
  7. import io
  8. from git import (
  9. GitConfigParser
  10. )
  11. from git.compat import string_types
  12. from git.config import _OMD, cp
  13. from git.test.lib import (
  14. TestCase,
  15. fixture_path,
  16. SkipTest,
  17. )
  18. from git.test.lib import with_rw_directory
  19. import os.path as osp
  20. from git.util import rmfile
  21. _tc_lock_fpaths = osp.join(osp.dirname(__file__), 'fixtures/*.lock')
  22. def _rm_lock_files():
  23. for lfp in glob.glob(_tc_lock_fpaths):
  24. rmfile(lfp)
  25. class TestBase(TestCase):
  26. def setUp(self):
  27. _rm_lock_files()
  28. def tearDown(self):
  29. for lfp in glob.glob(_tc_lock_fpaths):
  30. if osp.isfile(lfp):
  31. raise AssertionError('Previous TC left hanging git-lock file: %s', lfp)
  32. def _to_memcache(self, file_path):
  33. with open(file_path, "rb") as fp:
  34. sio = io.BytesIO(fp.read())
  35. sio.name = file_path
  36. return sio
  37. def test_read_write(self):
  38. # writer must create the exact same file as the one read before
  39. for filename in ("git_config", "git_config_global"):
  40. file_obj = self._to_memcache(fixture_path(filename))
  41. with GitConfigParser(file_obj, read_only=False) as w_config:
  42. w_config.read() # enforce reading
  43. assert w_config._sections
  44. w_config.write() # enforce writing
  45. # we stripped lines when reading, so the results differ
  46. assert file_obj.getvalue()
  47. self.assertEqual(file_obj.getvalue(), self._to_memcache(fixture_path(filename)).getvalue())
  48. # creating an additional config writer must fail due to exclusive access
  49. with self.assertRaises(IOError):
  50. GitConfigParser(file_obj, read_only=False)
  51. # should still have a lock and be able to make changes
  52. assert w_config._lock._has_lock()
  53. # changes should be written right away
  54. sname = "my_section"
  55. oname = "mykey"
  56. val = "myvalue"
  57. w_config.add_section(sname)
  58. assert w_config.has_section(sname)
  59. w_config.set(sname, oname, val)
  60. assert w_config.has_option(sname, oname)
  61. assert w_config.get(sname, oname) == val
  62. sname_new = "new_section"
  63. oname_new = "new_key"
  64. ival = 10
  65. w_config.set_value(sname_new, oname_new, ival)
  66. assert w_config.get_value(sname_new, oname_new) == ival
  67. file_obj.seek(0)
  68. r_config = GitConfigParser(file_obj, read_only=True)
  69. assert r_config.has_section(sname)
  70. assert r_config.has_option(sname, oname)
  71. assert r_config.get(sname, oname) == val
  72. # END for each filename
  73. def test_includes_order(self):
  74. with GitConfigParser(list(map(fixture_path, ("git_config", "git_config_global")))) as r_config:
  75. r_config.read() # enforce reading
  76. # Simple inclusions, again checking them taking precedence
  77. assert r_config.get_value('sec', 'var0') == "value0_included"
  78. # This one should take the git_config_global value since included
  79. # values must be considered as soon as they get them
  80. assert r_config.get_value('diff', 'tool') == "meld"
  81. try:
  82. assert r_config.get_value('sec', 'var1') == "value1_main"
  83. except AssertionError:
  84. raise SkipTest(
  85. 'Known failure -- included values are not in effect right away'
  86. )
  87. @with_rw_directory
  88. def test_lock_reentry(self, rw_dir):
  89. fpl = osp.join(rw_dir, 'l')
  90. gcp = GitConfigParser(fpl, read_only=False)
  91. with gcp as cw:
  92. cw.set_value('include', 'some_value', 'a')
  93. # entering again locks the file again...
  94. with gcp as cw:
  95. cw.set_value('include', 'some_other_value', 'b')
  96. # ...so creating an additional config writer must fail due to exclusive access
  97. with self.assertRaises(IOError):
  98. GitConfigParser(fpl, read_only=False)
  99. # but work when the lock is removed
  100. with GitConfigParser(fpl, read_only=False):
  101. assert osp.exists(fpl)
  102. # reentering with an existing lock must fail due to exclusive access
  103. with self.assertRaises(IOError):
  104. gcp.__enter__()
  105. def test_multi_line_config(self):
  106. file_obj = self._to_memcache(fixture_path("git_config_with_comments"))
  107. with GitConfigParser(file_obj, read_only=False) as config:
  108. ev = "ruby -e '\n"
  109. ev += " system %(git), %(merge-file), %(--marker-size=%L), %(%A), %(%O), %(%B)\n"
  110. ev += " b = File.read(%(%A))\n"
  111. ev += " b.sub!(/^<+ .*\\nActiveRecord::Schema\\.define.:version => (\\d+). do\\n=+\\nActiveRecord::Schema\\." # noqa E501
  112. ev += "define.:version => (\\d+). do\\n>+ .*/) do\n"
  113. ev += " %(ActiveRecord::Schema.define(:version => #{[$1, $2].max}) do)\n"
  114. ev += " end\n"
  115. ev += " File.open(%(%A), %(w)) {|f| f.write(b)}\n"
  116. ev += " exit 1 if b.include?(%(<)*%L)'"
  117. self.assertEqual(config.get('merge "railsschema"', 'driver'), ev)
  118. self.assertEqual(config.get('alias', 'lg'),
  119. "log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr)%Creset'"
  120. " --abbrev-commit --date=relative")
  121. self.assertEqual(len(config.sections()), 23)
  122. def test_base(self):
  123. path_repo = fixture_path("git_config")
  124. path_global = fixture_path("git_config_global")
  125. r_config = GitConfigParser([path_repo, path_global], read_only=True)
  126. assert r_config.read_only
  127. num_sections = 0
  128. num_options = 0
  129. # test reader methods
  130. assert r_config._is_initialized is False
  131. for section in r_config.sections():
  132. num_sections += 1
  133. for option in r_config.options(section):
  134. num_options += 1
  135. val = r_config.get(section, option)
  136. val_typed = r_config.get_value(section, option)
  137. assert isinstance(val_typed, (bool, int, float, ) + string_types)
  138. assert val
  139. assert "\n" not in option
  140. assert "\n" not in val
  141. # writing must fail
  142. with self.assertRaises(IOError):
  143. r_config.set(section, option, None)
  144. with self.assertRaises(IOError):
  145. r_config.remove_option(section, option)
  146. # END for each option
  147. with self.assertRaises(IOError):
  148. r_config.remove_section(section)
  149. # END for each section
  150. assert num_sections and num_options
  151. assert r_config._is_initialized is True
  152. # get value which doesnt exist, with default
  153. default = "my default value"
  154. assert r_config.get_value("doesnt", "exist", default) == default
  155. # it raises if there is no default though
  156. with self.assertRaises(cp.NoSectionError):
  157. r_config.get_value("doesnt", "exist")
  158. @with_rw_directory
  159. def test_config_include(self, rw_dir):
  160. def write_test_value(cw, value):
  161. cw.set_value(value, 'value', value)
  162. # end
  163. def check_test_value(cr, value):
  164. assert cr.get_value(value, 'value') == value
  165. # end
  166. # PREPARE CONFIG FILE A
  167. fpa = osp.join(rw_dir, 'a')
  168. with GitConfigParser(fpa, read_only=False) as cw:
  169. write_test_value(cw, 'a')
  170. fpb = osp.join(rw_dir, 'b')
  171. fpc = osp.join(rw_dir, 'c')
  172. cw.set_value('include', 'relative_path_b', 'b')
  173. cw.set_value('include', 'doesntexist', 'foobar')
  174. cw.set_value('include', 'relative_cycle_a_a', 'a')
  175. cw.set_value('include', 'absolute_cycle_a_a', fpa)
  176. assert osp.exists(fpa)
  177. # PREPARE CONFIG FILE B
  178. with GitConfigParser(fpb, read_only=False) as cw:
  179. write_test_value(cw, 'b')
  180. cw.set_value('include', 'relative_cycle_b_a', 'a')
  181. cw.set_value('include', 'absolute_cycle_b_a', fpa)
  182. cw.set_value('include', 'relative_path_c', 'c')
  183. cw.set_value('include', 'absolute_path_c', fpc)
  184. # PREPARE CONFIG FILE C
  185. with GitConfigParser(fpc, read_only=False) as cw:
  186. write_test_value(cw, 'c')
  187. with GitConfigParser(fpa, read_only=True) as cr:
  188. for tv in ('a', 'b', 'c'):
  189. check_test_value(cr, tv)
  190. # end for each test to verify
  191. assert len(cr.items('include')) == 8, "Expected all include sections to be merged"
  192. # test writable config writers - assure write-back doesn't involve includes
  193. with GitConfigParser(fpa, read_only=False, merge_includes=True) as cw:
  194. tv = 'x'
  195. write_test_value(cw, tv)
  196. with GitConfigParser(fpa, read_only=True) as cr:
  197. with self.assertRaises(cp.NoSectionError):
  198. check_test_value(cr, tv)
  199. # But can make it skip includes altogether, and thus allow write-backs
  200. with GitConfigParser(fpa, read_only=False, merge_includes=False) as cw:
  201. write_test_value(cw, tv)
  202. with GitConfigParser(fpa, read_only=True) as cr:
  203. check_test_value(cr, tv)
  204. def test_rename(self):
  205. file_obj = self._to_memcache(fixture_path('git_config'))
  206. with GitConfigParser(file_obj, read_only=False, merge_includes=False) as cw:
  207. with self.assertRaises(ValueError):
  208. cw.rename_section("doesntexist", "foo")
  209. with self.assertRaises(ValueError):
  210. cw.rename_section("core", "include")
  211. nn = "bee"
  212. assert cw.rename_section('core', nn) is cw
  213. assert not cw.has_section('core')
  214. assert len(cw.items(nn)) == 4
  215. def test_complex_aliases(self):
  216. file_obj = self._to_memcache(fixture_path('.gitconfig'))
  217. with GitConfigParser(file_obj, read_only=False) as w_config:
  218. self.assertEqual(w_config.get('alias', 'rbi'), '"!g() { git rebase -i origin/${1:-master} ; } ; g"')
  219. self.assertEqual(file_obj.getvalue(), self._to_memcache(fixture_path('.gitconfig')).getvalue())
  220. def test_empty_config_value(self):
  221. cr = GitConfigParser(fixture_path('git_config_with_empty_value'), read_only=True)
  222. assert cr.get_value('core', 'filemode'), "Should read keys with values"
  223. with self.assertRaises(cp.NoOptionError):
  224. cr.get_value('color', 'ui')
  225. def test_multiple_values(self):
  226. file_obj = self._to_memcache(fixture_path('git_config_multiple'))
  227. with GitConfigParser(file_obj, read_only=False) as cw:
  228. self.assertEqual(cw.get('section0', 'option0'), 'value0')
  229. self.assertEqual(cw.get_values('section0', 'option0'), ['value0'])
  230. self.assertEqual(cw.items('section0'), [('option0', 'value0')])
  231. # Where there are multiple values, "get" returns the last.
  232. self.assertEqual(cw.get('section1', 'option1'), 'value1b')
  233. self.assertEqual(cw.get_values('section1', 'option1'),
  234. ['value1a', 'value1b'])
  235. self.assertEqual(cw.items('section1'),
  236. [('option1', 'value1b'),
  237. ('other_option1', 'other_value1')])
  238. self.assertEqual(cw.items_all('section1'),
  239. [('option1', ['value1a', 'value1b']),
  240. ('other_option1', ['other_value1'])])
  241. with self.assertRaises(KeyError):
  242. cw.get_values('section1', 'missing')
  243. self.assertEqual(cw.get_values('section1', 'missing', 1), [1])
  244. self.assertEqual(cw.get_values('section1', 'missing', 's'), ['s'])
  245. def test_multiple_values_rename(self):
  246. file_obj = self._to_memcache(fixture_path('git_config_multiple'))
  247. with GitConfigParser(file_obj, read_only=False) as cw:
  248. cw.rename_section('section1', 'section2')
  249. cw.write()
  250. file_obj.seek(0)
  251. cr = GitConfigParser(file_obj, read_only=True)
  252. self.assertEqual(cr.get_value('section2', 'option1'), 'value1b')
  253. self.assertEqual(cr.get_values('section2', 'option1'),
  254. ['value1a', 'value1b'])
  255. self.assertEqual(cr.items('section2'),
  256. [('option1', 'value1b'),
  257. ('other_option1', 'other_value1')])
  258. self.assertEqual(cr.items_all('section2'),
  259. [('option1', ['value1a', 'value1b']),
  260. ('other_option1', ['other_value1'])])
  261. def test_multiple_to_single(self):
  262. file_obj = self._to_memcache(fixture_path('git_config_multiple'))
  263. with GitConfigParser(file_obj, read_only=False) as cw:
  264. cw.set_value('section1', 'option1', 'value1c')
  265. cw.write()
  266. file_obj.seek(0)
  267. cr = GitConfigParser(file_obj, read_only=True)
  268. self.assertEqual(cr.get_value('section1', 'option1'), 'value1c')
  269. self.assertEqual(cr.get_values('section1', 'option1'), ['value1c'])
  270. self.assertEqual(cr.items('section1'),
  271. [('option1', 'value1c'),
  272. ('other_option1', 'other_value1')])
  273. self.assertEqual(cr.items_all('section1'),
  274. [('option1', ['value1c']),
  275. ('other_option1', ['other_value1'])])
  276. def test_single_to_multiple(self):
  277. file_obj = self._to_memcache(fixture_path('git_config_multiple'))
  278. with GitConfigParser(file_obj, read_only=False) as cw:
  279. cw.add_value('section1', 'other_option1', 'other_value1a')
  280. cw.write()
  281. file_obj.seek(0)
  282. cr = GitConfigParser(file_obj, read_only=True)
  283. self.assertEqual(cr.get_value('section1', 'option1'), 'value1b')
  284. self.assertEqual(cr.get_values('section1', 'option1'),
  285. ['value1a', 'value1b'])
  286. self.assertEqual(cr.get_value('section1', 'other_option1'),
  287. 'other_value1a')
  288. self.assertEqual(cr.get_values('section1', 'other_option1'),
  289. ['other_value1', 'other_value1a'])
  290. self.assertEqual(cr.items('section1'),
  291. [('option1', 'value1b'),
  292. ('other_option1', 'other_value1a')])
  293. self.assertEqual(
  294. cr.items_all('section1'),
  295. [('option1', ['value1a', 'value1b']),
  296. ('other_option1', ['other_value1', 'other_value1a'])])
  297. def test_add_to_multiple(self):
  298. file_obj = self._to_memcache(fixture_path('git_config_multiple'))
  299. with GitConfigParser(file_obj, read_only=False) as cw:
  300. cw.add_value('section1', 'option1', 'value1c')
  301. cw.write()
  302. file_obj.seek(0)
  303. cr = GitConfigParser(file_obj, read_only=True)
  304. self.assertEqual(cr.get_value('section1', 'option1'), 'value1c')
  305. self.assertEqual(cr.get_values('section1', 'option1'),
  306. ['value1a', 'value1b', 'value1c'])
  307. self.assertEqual(cr.items('section1'),
  308. [('option1', 'value1c'),
  309. ('other_option1', 'other_value1')])
  310. self.assertEqual(cr.items_all('section1'),
  311. [('option1', ['value1a', 'value1b', 'value1c']),
  312. ('other_option1', ['other_value1'])])
  313. def test_setlast(self):
  314. # Test directly, not covered by higher-level tests.
  315. omd = _OMD()
  316. omd.setlast('key', 'value1')
  317. self.assertEqual(omd['key'], 'value1')
  318. self.assertEqual(omd.getall('key'), ['value1'])
  319. omd.setlast('key', 'value2')
  320. self.assertEqual(omd['key'], 'value2')
  321. self.assertEqual(omd.getall('key'), ['value2'])