# test_config.py # Copyright (C) 2008, 2009 Michael Trier (mtrier@gmail.com) and contributors # # This module is part of GitPython and is released under # the BSD License: http://www.opensource.org/licenses/bsd-license.php import glob import io from git import ( GitConfigParser ) from git.compat import string_types from git.config import _OMD, cp from git.test.lib import ( TestCase, fixture_path, SkipTest, ) from git.test.lib import with_rw_directory import os.path as osp from git.util import rmfile _tc_lock_fpaths = osp.join(osp.dirname(__file__), 'fixtures/*.lock') def _rm_lock_files(): for lfp in glob.glob(_tc_lock_fpaths): rmfile(lfp) class TestBase(TestCase): def setUp(self): _rm_lock_files() def tearDown(self): for lfp in glob.glob(_tc_lock_fpaths): if osp.isfile(lfp): raise AssertionError('Previous TC left hanging git-lock file: {}'.format(lfp)) def _to_memcache(self, file_path): with open(file_path, "rb") as fp: sio = io.BytesIO(fp.read()) sio.name = file_path return sio def test_read_write(self): # writer must create the exact same file as the one read before for filename in ("git_config", "git_config_global"): file_obj = self._to_memcache(fixture_path(filename)) with GitConfigParser(file_obj, read_only=False) as w_config: w_config.read() # enforce reading assert w_config._sections w_config.write() # enforce writing # we stripped lines when reading, so the results differ assert file_obj.getvalue() self.assertEqual(file_obj.getvalue(), self._to_memcache(fixture_path(filename)).getvalue()) # creating an additional config writer must fail due to exclusive access with self.assertRaises(IOError): GitConfigParser(file_obj, read_only=False) # should still have a lock and be able to make changes assert w_config._lock._has_lock() # changes should be written right away sname = "my_section" oname = "mykey" val = "myvalue" w_config.add_section(sname) assert w_config.has_section(sname) w_config.set(sname, oname, val) assert w_config.has_option(sname, oname) assert w_config.get(sname, oname) == val sname_new = "new_section" oname_new = "new_key" ival = 10 w_config.set_value(sname_new, oname_new, ival) assert w_config.get_value(sname_new, oname_new) == ival file_obj.seek(0) r_config = GitConfigParser(file_obj, read_only=True) assert r_config.has_section(sname) assert r_config.has_option(sname, oname) assert r_config.get(sname, oname) == val # END for each filename def test_includes_order(self): with GitConfigParser(list(map(fixture_path, ("git_config", "git_config_global")))) as r_config: r_config.read() # enforce reading # Simple inclusions, again checking them taking precedence assert r_config.get_value('sec', 'var0') == "value0_included" # This one should take the git_config_global value since included # values must be considered as soon as they get them assert r_config.get_value('diff', 'tool') == "meld" try: assert r_config.get_value('sec', 'var1') == "value1_main" except AssertionError: raise SkipTest( 'Known failure -- included values are not in effect right away' ) @with_rw_directory def test_lock_reentry(self, rw_dir): fpl = osp.join(rw_dir, 'l') gcp = GitConfigParser(fpl, read_only=False) with gcp as cw: cw.set_value('include', 'some_value', 'a') # entering again locks the file again... with gcp as cw: cw.set_value('include', 'some_other_value', 'b') # ...so creating an additional config writer must fail due to exclusive access with self.assertRaises(IOError): GitConfigParser(fpl, read_only=False) # but work when the lock is removed with GitConfigParser(fpl, read_only=False): assert osp.exists(fpl) # reentering with an existing lock must fail due to exclusive access with self.assertRaises(IOError): gcp.__enter__() def test_multi_line_config(self): file_obj = self._to_memcache(fixture_path("git_config_with_comments")) with GitConfigParser(file_obj, read_only=False) as config: ev = "ruby -e '\n" ev += " system %(git), %(merge-file), %(--marker-size=%L), %(%A), %(%O), %(%B)\n" ev += " b = File.read(%(%A))\n" ev += " b.sub!(/^<+ .*\\nActiveRecord::Schema\\.define.:version => (\\d+). do\\n=+\\nActiveRecord::Schema\\." # noqa E501 ev += "define.:version => (\\d+). do\\n>+ .*/) do\n" ev += " %(ActiveRecord::Schema.define(:version => #{[$1, $2].max}) do)\n" ev += " end\n" ev += " File.open(%(%A), %(w)) {|f| f.write(b)}\n" ev += " exit 1 if b.include?(%(<)*%L)'" self.assertEqual(config.get('merge "railsschema"', 'driver'), ev) self.assertEqual(config.get('alias', 'lg'), "log --graph --pretty=format:'%Cred%h%Creset -%C(yellow)%d%Creset %s %Cgreen(%cr)%Creset'" " --abbrev-commit --date=relative") self.assertEqual(len(config.sections()), 23) def test_base(self): path_repo = fixture_path("git_config") path_global = fixture_path("git_config_global") r_config = GitConfigParser([path_repo, path_global], read_only=True) assert r_config.read_only num_sections = 0 num_options = 0 # test reader methods assert r_config._is_initialized is False for section in r_config.sections(): num_sections += 1 for option in r_config.options(section): num_options += 1 val = r_config.get(section, option) val_typed = r_config.get_value(section, option) assert isinstance(val_typed, (bool, int, float, ) + string_types) assert val assert "\n" not in option assert "\n" not in val # writing must fail with self.assertRaises(IOError): r_config.set(section, option, None) with self.assertRaises(IOError): r_config.remove_option(section, option) # END for each option with self.assertRaises(IOError): r_config.remove_section(section) # END for each section assert num_sections and num_options assert r_config._is_initialized is True # get value which doesnt exist, with default default = "my default value" assert r_config.get_value("doesnt", "exist", default) == default # it raises if there is no default though with self.assertRaises(cp.NoSectionError): r_config.get_value("doesnt", "exist") @with_rw_directory def test_config_include(self, rw_dir): def write_test_value(cw, value): cw.set_value(value, 'value', value) # end def check_test_value(cr, value): assert cr.get_value(value, 'value') == value # end # PREPARE CONFIG FILE A fpa = osp.join(rw_dir, 'a') with GitConfigParser(fpa, read_only=False) as cw: write_test_value(cw, 'a') fpb = osp.join(rw_dir, 'b') fpc = osp.join(rw_dir, 'c') cw.set_value('include', 'relative_path_b', 'b') cw.set_value('include', 'doesntexist', 'foobar') cw.set_value('include', 'relative_cycle_a_a', 'a') cw.set_value('include', 'absolute_cycle_a_a', fpa) assert osp.exists(fpa) # PREPARE CONFIG FILE B with GitConfigParser(fpb, read_only=False) as cw: write_test_value(cw, 'b') cw.set_value('include', 'relative_cycle_b_a', 'a') cw.set_value('include', 'absolute_cycle_b_a', fpa) cw.set_value('include', 'relative_path_c', 'c') cw.set_value('include', 'absolute_path_c', fpc) # PREPARE CONFIG FILE C with GitConfigParser(fpc, read_only=False) as cw: write_test_value(cw, 'c') with GitConfigParser(fpa, read_only=True) as cr: for tv in ('a', 'b', 'c'): check_test_value(cr, tv) # end for each test to verify assert len(cr.items('include')) == 8, "Expected all include sections to be merged" # test writable config writers - assure write-back doesn't involve includes with GitConfigParser(fpa, read_only=False, merge_includes=True) as cw: tv = 'x' write_test_value(cw, tv) with GitConfigParser(fpa, read_only=True) as cr: with self.assertRaises(cp.NoSectionError): check_test_value(cr, tv) # But can make it skip includes altogether, and thus allow write-backs with GitConfigParser(fpa, read_only=False, merge_includes=False) as cw: write_test_value(cw, tv) with GitConfigParser(fpa, read_only=True) as cr: check_test_value(cr, tv) def test_rename(self): file_obj = self._to_memcache(fixture_path('git_config')) with GitConfigParser(file_obj, read_only=False, merge_includes=False) as cw: with self.assertRaises(ValueError): cw.rename_section("doesntexist", "foo") with self.assertRaises(ValueError): cw.rename_section("core", "include") nn = "bee" assert cw.rename_section('core', nn) is cw assert not cw.has_section('core') assert len(cw.items(nn)) == 4 def test_complex_aliases(self): file_obj = self._to_memcache(fixture_path('.gitconfig')) with GitConfigParser(file_obj, read_only=False) as w_config: self.assertEqual(w_config.get('alias', 'rbi'), '"!g() { git rebase -i origin/${1:-master} ; } ; g"') self.assertEqual(file_obj.getvalue(), self._to_memcache(fixture_path('.gitconfig')).getvalue()) def test_empty_config_value(self): cr = GitConfigParser(fixture_path('git_config_with_empty_value'), read_only=True) assert cr.get_value('core', 'filemode'), "Should read keys with values" with self.assertRaises(cp.NoOptionError): cr.get_value('color', 'ui') def test_multiple_values(self): file_obj = self._to_memcache(fixture_path('git_config_multiple')) with GitConfigParser(file_obj, read_only=False) as cw: self.assertEqual(cw.get('section0', 'option0'), 'value0') self.assertEqual(cw.get_values('section0', 'option0'), ['value0']) self.assertEqual(cw.items('section0'), [('option0', 'value0')]) # Where there are multiple values, "get" returns the last. self.assertEqual(cw.get('section1', 'option1'), 'value1b') self.assertEqual(cw.get_values('section1', 'option1'), ['value1a', 'value1b']) self.assertEqual(cw.items('section1'), [('option1', 'value1b'), ('other_option1', 'other_value1')]) self.assertEqual(cw.items_all('section1'), [('option1', ['value1a', 'value1b']), ('other_option1', ['other_value1'])]) with self.assertRaises(KeyError): cw.get_values('section1', 'missing') self.assertEqual(cw.get_values('section1', 'missing', 1), [1]) self.assertEqual(cw.get_values('section1', 'missing', 's'), ['s']) def test_multiple_values_rename(self): file_obj = self._to_memcache(fixture_path('git_config_multiple')) with GitConfigParser(file_obj, read_only=False) as cw: cw.rename_section('section1', 'section2') cw.write() file_obj.seek(0) cr = GitConfigParser(file_obj, read_only=True) self.assertEqual(cr.get_value('section2', 'option1'), 'value1b') self.assertEqual(cr.get_values('section2', 'option1'), ['value1a', 'value1b']) self.assertEqual(cr.items('section2'), [('option1', 'value1b'), ('other_option1', 'other_value1')]) self.assertEqual(cr.items_all('section2'), [('option1', ['value1a', 'value1b']), ('other_option1', ['other_value1'])]) def test_multiple_to_single(self): file_obj = self._to_memcache(fixture_path('git_config_multiple')) with GitConfigParser(file_obj, read_only=False) as cw: cw.set_value('section1', 'option1', 'value1c') cw.write() file_obj.seek(0) cr = GitConfigParser(file_obj, read_only=True) self.assertEqual(cr.get_value('section1', 'option1'), 'value1c') self.assertEqual(cr.get_values('section1', 'option1'), ['value1c']) self.assertEqual(cr.items('section1'), [('option1', 'value1c'), ('other_option1', 'other_value1')]) self.assertEqual(cr.items_all('section1'), [('option1', ['value1c']), ('other_option1', ['other_value1'])]) def test_single_to_multiple(self): file_obj = self._to_memcache(fixture_path('git_config_multiple')) with GitConfigParser(file_obj, read_only=False) as cw: cw.add_value('section1', 'other_option1', 'other_value1a') cw.write() file_obj.seek(0) cr = GitConfigParser(file_obj, read_only=True) self.assertEqual(cr.get_value('section1', 'option1'), 'value1b') self.assertEqual(cr.get_values('section1', 'option1'), ['value1a', 'value1b']) self.assertEqual(cr.get_value('section1', 'other_option1'), 'other_value1a') self.assertEqual(cr.get_values('section1', 'other_option1'), ['other_value1', 'other_value1a']) self.assertEqual(cr.items('section1'), [('option1', 'value1b'), ('other_option1', 'other_value1a')]) self.assertEqual( cr.items_all('section1'), [('option1', ['value1a', 'value1b']), ('other_option1', ['other_value1', 'other_value1a'])]) def test_add_to_multiple(self): file_obj = self._to_memcache(fixture_path('git_config_multiple')) with GitConfigParser(file_obj, read_only=False) as cw: cw.add_value('section1', 'option1', 'value1c') cw.write() file_obj.seek(0) cr = GitConfigParser(file_obj, read_only=True) self.assertEqual(cr.get_value('section1', 'option1'), 'value1c') self.assertEqual(cr.get_values('section1', 'option1'), ['value1a', 'value1b', 'value1c']) self.assertEqual(cr.items('section1'), [('option1', 'value1c'), ('other_option1', 'other_value1')]) self.assertEqual(cr.items_all('section1'), [('option1', ['value1a', 'value1b', 'value1c']), ('other_option1', ['other_value1'])]) def test_setlast(self): # Test directly, not covered by higher-level tests. omd = _OMD() omd.setlast('key', 'value1') self.assertEqual(omd['key'], 'value1') self.assertEqual(omd.getall('key'), ['value1']) omd.setlast('key', 'value2') self.assertEqual(omd['key'], 'value2') self.assertEqual(omd.getall('key'), ['value2'])