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.
268 lines
11 KiB
268 lines
11 KiB
6 years ago
|
# 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 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: %s', 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')
|