367
436
'/home/bogus/.cache')
370
class TestIniConfig(tests.TestCase):
439
class TestXDGConfigDir(tests.TestCaseInTempDir):
440
# must be in temp dir because config tests for the existence of the bazaar
441
# subdirectory of $XDG_CONFIG_HOME
444
if sys.platform in ('darwin', 'win32'):
445
raise tests.TestNotApplicable(
446
'XDG config dir not used on this platform')
447
super(TestXDGConfigDir, self).setUp()
448
self.overrideEnv('HOME', self.test_home_dir)
449
# BZR_HOME overrides everything we want to test so unset it.
450
self.overrideEnv('BZR_HOME', None)
452
def test_xdg_config_dir_exists(self):
453
"""When ~/.config/bazaar exists, use it as the config dir."""
454
newdir = osutils.pathjoin(self.test_home_dir, '.config', 'bazaar')
456
self.assertEqual(config.config_dir(), newdir)
458
def test_xdg_config_home(self):
459
"""When XDG_CONFIG_HOME is set, use it."""
460
xdgconfigdir = osutils.pathjoin(self.test_home_dir, 'xdgconfig')
461
self.overrideEnv('XDG_CONFIG_HOME', xdgconfigdir)
462
newdir = osutils.pathjoin(xdgconfigdir, 'bazaar')
464
self.assertEqual(config.config_dir(), newdir)
467
class TestIniConfig(tests.TestCaseInTempDir):
372
469
def make_config_parser(self, s):
373
conf = config.IniBasedConfig(None)
374
parser = conf._get_parser(file=StringIO(s.encode('utf-8')))
470
conf = config.IniBasedConfig.from_string(s)
471
return conf, conf._get_parser()
378
474
class TestIniConfigBuilding(TestIniConfig):
380
476
def test_contructs(self):
381
my_config = config.IniBasedConfig("nothing")
477
my_config = config.IniBasedConfig()
383
479
def test_from_fp(self):
384
config_file = StringIO(sample_config_text.encode('utf-8'))
385
my_config = config.IniBasedConfig(None)
387
isinstance(my_config._get_parser(file=config_file),
388
configobj.ConfigObj))
480
my_config = config.IniBasedConfig.from_string(sample_config_text)
481
self.assertIsInstance(my_config._get_parser(), configobj.ConfigObj)
390
483
def test_cached(self):
391
config_file = StringIO(sample_config_text.encode('utf-8'))
392
my_config = config.IniBasedConfig(None)
393
parser = my_config._get_parser(file=config_file)
484
my_config = config.IniBasedConfig.from_string(sample_config_text)
485
parser = my_config._get_parser()
394
486
self.failUnless(my_config._get_parser() is parser)
488
def _dummy_chown(self, path, uid, gid):
489
self.path, self.uid, self.gid = path, uid, gid
491
def test_ini_config_ownership(self):
492
"""Ensure that chown is happening during _write_config_file"""
493
self.requireFeature(features.chown_feature)
494
self.overrideAttr(os, 'chown', self._dummy_chown)
495
self.path = self.uid = self.gid = None
496
conf = config.IniBasedConfig(file_name='./foo.conf')
497
conf._write_config_file()
498
self.assertEquals(self.path, './foo.conf')
499
self.assertTrue(isinstance(self.uid, int))
500
self.assertTrue(isinstance(self.gid, int))
502
def test_get_filename_parameter_is_deprecated_(self):
503
conf = self.callDeprecated([
504
'IniBasedConfig.__init__(get_filename) was deprecated in 2.3.'
505
' Use file_name instead.'],
506
config.IniBasedConfig, lambda: 'ini.conf')
507
self.assertEqual('ini.conf', conf.file_name)
509
def test_get_parser_file_parameter_is_deprecated_(self):
510
config_file = StringIO(sample_config_text.encode('utf-8'))
511
conf = config.IniBasedConfig.from_string(sample_config_text)
512
conf = self.callDeprecated([
513
'IniBasedConfig._get_parser(file=xxx) was deprecated in 2.3.'
514
' Use IniBasedConfig(_content=xxx) instead.'],
515
conf._get_parser, file=config_file)
517
class TestIniConfigSaving(tests.TestCaseInTempDir):
519
def test_cant_save_without_a_file_name(self):
520
conf = config.IniBasedConfig()
521
self.assertRaises(AssertionError, conf._write_config_file)
523
def test_saved_with_content(self):
524
content = 'foo = bar\n'
525
conf = config.IniBasedConfig.from_string(
526
content, file_name='./test.conf', save=True)
527
self.assertFileEqual(content, 'test.conf')
530
class TestIniBaseConfigOnDisk(tests.TestCaseInTempDir):
532
def test_cannot_reload_without_name(self):
533
conf = config.IniBasedConfig.from_string(sample_config_text)
534
self.assertRaises(AssertionError, conf.reload)
536
def test_reload_see_new_value(self):
537
c1 = config.IniBasedConfig.from_string('editor=vim\n',
538
file_name='./test/conf')
539
c1._write_config_file()
540
c2 = config.IniBasedConfig.from_string('editor=emacs\n',
541
file_name='./test/conf')
542
c2._write_config_file()
543
self.assertEqual('vim', c1.get_user_option('editor'))
544
self.assertEqual('emacs', c2.get_user_option('editor'))
545
# Make sure we get the Right value
547
self.assertEqual('emacs', c1.get_user_option('editor'))
550
class TestLockableConfig(tests.TestCaseInTempDir):
552
scenarios = lockable_config_scenarios()
557
config_section = None
560
super(TestLockableConfig, self).setUp()
561
self._content = '[%s]\none=1\ntwo=2\n' % (self.config_section,)
562
self.config = self.create_config(self._content)
564
def get_existing_config(self):
565
return self.config_class(*self.config_args)
567
def create_config(self, content):
568
kwargs = dict(save=True)
569
c = self.config_class.from_string(content, *self.config_args, **kwargs)
572
def test_simple_read_access(self):
573
self.assertEquals('1', self.config.get_user_option('one'))
575
def test_simple_write_access(self):
576
self.config.set_user_option('one', 'one')
577
self.assertEquals('one', self.config.get_user_option('one'))
579
def test_listen_to_the_last_speaker(self):
581
c2 = self.get_existing_config()
582
c1.set_user_option('one', 'ONE')
583
c2.set_user_option('two', 'TWO')
584
self.assertEquals('ONE', c1.get_user_option('one'))
585
self.assertEquals('TWO', c2.get_user_option('two'))
586
# The second update respect the first one
587
self.assertEquals('ONE', c2.get_user_option('one'))
589
def test_last_speaker_wins(self):
590
# If the same config is not shared, the same variable modified twice
591
# can only see a single result.
593
c2 = self.get_existing_config()
594
c1.set_user_option('one', 'c1')
595
c2.set_user_option('one', 'c2')
596
self.assertEquals('c2', c2._get_user_option('one'))
597
# The first modification is still available until another refresh
599
self.assertEquals('c1', c1._get_user_option('one'))
600
c1.set_user_option('two', 'done')
601
self.assertEquals('c2', c1._get_user_option('one'))
603
def test_writes_are_serialized(self):
605
c2 = self.get_existing_config()
607
# We spawn a thread that will pause *during* the write
608
before_writing = threading.Event()
609
after_writing = threading.Event()
610
writing_done = threading.Event()
611
c1_orig = c1._write_config_file
612
def c1_write_config_file():
615
# The lock is held we wait for the main thread to decide when to
618
c1._write_config_file = c1_write_config_file
620
c1.set_user_option('one', 'c1')
622
t1 = threading.Thread(target=c1_set_option)
623
# Collect the thread after the test
624
self.addCleanup(t1.join)
625
# Be ready to unblock the thread if the test goes wrong
626
self.addCleanup(after_writing.set)
628
before_writing.wait()
629
self.assertTrue(c1._lock.is_held)
630
self.assertRaises(errors.LockContention,
631
c2.set_user_option, 'one', 'c2')
632
self.assertEquals('c1', c1.get_user_option('one'))
633
# Let the lock be released
636
c2.set_user_option('one', 'c2')
637
self.assertEquals('c2', c2.get_user_option('one'))
639
def test_read_while_writing(self):
641
# We spawn a thread that will pause *during* the write
642
ready_to_write = threading.Event()
643
do_writing = threading.Event()
644
writing_done = threading.Event()
645
c1_orig = c1._write_config_file
646
def c1_write_config_file():
648
# The lock is held we wait for the main thread to decide when to
653
c1._write_config_file = c1_write_config_file
655
c1.set_user_option('one', 'c1')
656
t1 = threading.Thread(target=c1_set_option)
657
# Collect the thread after the test
658
self.addCleanup(t1.join)
659
# Be ready to unblock the thread if the test goes wrong
660
self.addCleanup(do_writing.set)
662
# Ensure the thread is ready to write
663
ready_to_write.wait()
664
self.assertTrue(c1._lock.is_held)
665
self.assertEquals('c1', c1.get_user_option('one'))
666
# If we read during the write, we get the old value
667
c2 = self.get_existing_config()
668
self.assertEquals('1', c2.get_user_option('one'))
669
# Let the writing occur and ensure it occurred
672
# Now we get the updated value
673
c3 = self.get_existing_config()
674
self.assertEquals('c1', c3.get_user_option('one'))
397
677
class TestGetUserOptionAs(TestIniConfig):
550
835
self.assertEqual(1, len(warnings))
551
836
self.assertEqual(warning, warnings[0])
552
trace.warning = warning
554
branch = self.make_branch('.')
555
conf = branch.get_config()
556
set_option(config.STORE_GLOBAL)
558
set_option(config.STORE_BRANCH)
560
set_option(config.STORE_GLOBAL)
561
assertWarning('Value "4" is masked by "3" from branch.conf')
562
set_option(config.STORE_GLOBAL, warn_masked=False)
564
set_option(config.STORE_LOCATION)
566
set_option(config.STORE_BRANCH)
567
assertWarning('Value "3" is masked by "0" from locations.conf')
568
set_option(config.STORE_BRANCH, warn_masked=False)
571
trace.warning = _warning
574
class TestGlobalConfigItems(tests.TestCase):
837
branch = self.make_branch('.')
838
conf = branch.get_config()
839
set_option(config.STORE_GLOBAL)
841
set_option(config.STORE_BRANCH)
843
set_option(config.STORE_GLOBAL)
844
assertWarning('Value "4" is masked by "3" from branch.conf')
845
set_option(config.STORE_GLOBAL, warn_masked=False)
847
set_option(config.STORE_LOCATION)
849
set_option(config.STORE_BRANCH)
850
assertWarning('Value "3" is masked by "0" from locations.conf')
851
set_option(config.STORE_BRANCH, warn_masked=False)
855
class TestGlobalConfigItems(tests.TestCaseInTempDir):
576
857
def test_user_id(self):
577
config_file = StringIO(sample_config_text.encode('utf-8'))
578
my_config = config.GlobalConfig()
579
my_config._parser = my_config._get_parser(file=config_file)
858
my_config = config.GlobalConfig.from_string(sample_config_text)
580
859
self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
581
860
my_config._get_user_id())
583
862
def test_absent_user_id(self):
584
config_file = StringIO("")
585
863
my_config = config.GlobalConfig()
586
my_config._parser = my_config._get_parser(file=config_file)
587
864
self.assertEqual(None, my_config._get_user_id())
589
866
def test_configured_editor(self):
590
config_file = StringIO(sample_config_text.encode('utf-8'))
591
my_config = config.GlobalConfig()
592
my_config._parser = my_config._get_parser(file=config_file)
867
my_config = config.GlobalConfig.from_string(sample_config_text)
593
868
self.assertEqual("vim", my_config.get_editor())
595
870
def test_signatures_always(self):
596
config_file = StringIO(sample_always_signatures)
597
my_config = config.GlobalConfig()
598
my_config._parser = my_config._get_parser(file=config_file)
871
my_config = config.GlobalConfig.from_string(sample_always_signatures)
599
872
self.assertEqual(config.CHECK_NEVER,
600
873
my_config.signature_checking())
601
874
self.assertEqual(config.SIGN_ALWAYS,
982
1285
self.assertEqual('bzrlib.tests.test_config.post_commit',
983
1286
self.my_config.post_commit())
985
def get_branch_config(self, location, global_config=None):
1288
def get_branch_config(self, location, global_config=None,
1289
location_config=None):
1290
my_branch = FakeBranch(location)
986
1291
if global_config is None:
987
global_file = StringIO(sample_config_text.encode('utf-8'))
989
global_file = StringIO(global_config.encode('utf-8'))
990
branches_file = StringIO(sample_branches_text.encode('utf-8'))
991
self.my_config = config.BranchConfig(FakeBranch(location))
992
# Force location config to use specified file
993
self.my_location_config = self.my_config._get_location_config()
994
self.my_location_config._get_parser(branches_file)
995
# Force global config to use specified file
996
self.my_config._get_global_config()._get_parser(global_file)
1292
global_config = sample_config_text
1293
if location_config is None:
1294
location_config = sample_branches_text
1296
my_global_config = config.GlobalConfig.from_string(global_config,
1298
my_location_config = config.LocationConfig.from_string(
1299
location_config, my_branch.base, save=True)
1300
my_config = config.BranchConfig(my_branch)
1301
self.my_config = my_config
1302
self.my_location_config = my_config._get_location_config()
998
1304
def test_set_user_setting_sets_and_saves(self):
999
1305
self.get_branch_config('/a/c')
1000
1306
record = InstrumentedConfigObj("foo")
1001
1307
self.my_location_config._parser = record
1003
real_mkdir = os.mkdir
1004
self.created = False
1005
def checked_mkdir(path, mode=0777):
1006
self.log('making directory: %s', path)
1007
real_mkdir(path, mode)
1010
os.mkdir = checked_mkdir
1012
self.callDeprecated(['The recurse option is deprecated as of '
1013
'0.14. The section "/a/c" has been '
1014
'converted to use policies.'],
1015
self.my_config.set_user_option,
1016
'foo', 'bar', store=config.STORE_LOCATION)
1018
os.mkdir = real_mkdir
1020
self.failUnless(self.created, 'Failed to create ~/.bazaar')
1021
self.assertEqual([('__contains__', '/a/c'),
1309
self.callDeprecated(['The recurse option is deprecated as of '
1310
'0.14. The section "/a/c" has been '
1311
'converted to use policies.'],
1312
self.my_config.set_user_option,
1313
'foo', 'bar', store=config.STORE_LOCATION)
1314
self.assertEqual([('reload',),
1315
('__contains__', '/a/c'),
1022
1316
('__contains__', '/a/c/'),
1023
1317
('__setitem__', '/a/c', {}),
1024
1318
('__getitem__', '/a/c'),
1305
1598
self.assertIs(None, bzrdir_config.get_default_stack_on())
1601
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
1604
super(TestConfigGetOptions, self).setUp()
1605
create_configs(self)
1607
# One variable in none of the above
1608
def test_no_variable(self):
1609
# Using branch should query branch, locations and bazaar
1610
self.assertOptions([], self.branch_config)
1612
def test_option_in_bazaar(self):
1613
self.bazaar_config.set_user_option('file', 'bazaar')
1614
self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
1617
def test_option_in_locations(self):
1618
self.locations_config.set_user_option('file', 'locations')
1620
[('file', 'locations', self.tree.basedir, 'locations')],
1621
self.locations_config)
1623
def test_option_in_branch(self):
1624
self.branch_config.set_user_option('file', 'branch')
1625
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
1628
def test_option_in_bazaar_and_branch(self):
1629
self.bazaar_config.set_user_option('file', 'bazaar')
1630
self.branch_config.set_user_option('file', 'branch')
1631
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
1632
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
1635
def test_option_in_branch_and_locations(self):
1636
# Hmm, locations override branch :-/
1637
self.locations_config.set_user_option('file', 'locations')
1638
self.branch_config.set_user_option('file', 'branch')
1640
[('file', 'locations', self.tree.basedir, 'locations'),
1641
('file', 'branch', 'DEFAULT', 'branch'),],
1644
def test_option_in_bazaar_locations_and_branch(self):
1645
self.bazaar_config.set_user_option('file', 'bazaar')
1646
self.locations_config.set_user_option('file', 'locations')
1647
self.branch_config.set_user_option('file', 'branch')
1649
[('file', 'locations', self.tree.basedir, 'locations'),
1650
('file', 'branch', 'DEFAULT', 'branch'),
1651
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
1655
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
1658
super(TestConfigRemoveOption, self).setUp()
1659
create_configs_with_file_option(self)
1661
def test_remove_in_locations(self):
1662
self.locations_config.remove_user_option('file', self.tree.basedir)
1664
[('file', 'branch', 'DEFAULT', 'branch'),
1665
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
1668
def test_remove_in_branch(self):
1669
self.branch_config.remove_user_option('file')
1671
[('file', 'locations', self.tree.basedir, 'locations'),
1672
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
1675
def test_remove_in_bazaar(self):
1676
self.bazaar_config.remove_user_option('file')
1678
[('file', 'locations', self.tree.basedir, 'locations'),
1679
('file', 'branch', 'DEFAULT', 'branch'),],
1683
class TestConfigGetSections(tests.TestCaseWithTransport):
1686
super(TestConfigGetSections, self).setUp()
1687
create_configs(self)
1689
def assertSectionNames(self, expected, conf, name=None):
1690
"""Check which sections are returned for a given config.
1692
If fallback configurations exist their sections can be included.
1694
:param expected: A list of section names.
1696
:param conf: The configuration that will be queried.
1698
:param name: An optional section name that will be passed to
1701
sections = list(conf._get_sections(name))
1702
self.assertLength(len(expected), sections)
1703
self.assertEqual(expected, [name for name, _, _ in sections])
1705
def test_bazaar_default_section(self):
1706
self.assertSectionNames(['DEFAULT'], self.bazaar_config)
1708
def test_locations_default_section(self):
1709
# No sections are defined in an empty file
1710
self.assertSectionNames([], self.locations_config)
1712
def test_locations_named_section(self):
1713
self.locations_config.set_user_option('file', 'locations')
1714
self.assertSectionNames([self.tree.basedir], self.locations_config)
1716
def test_locations_matching_sections(self):
1717
loc_config = self.locations_config
1718
loc_config.set_user_option('file', 'locations')
1719
# We need to cheat a bit here to create an option in sections above and
1720
# below the 'location' one.
1721
parser = loc_config._get_parser()
1722
# locations.cong deals with '/' ignoring native os.sep
1723
location_names = self.tree.basedir.split('/')
1724
parent = '/'.join(location_names[:-1])
1725
child = '/'.join(location_names + ['child'])
1727
parser[parent]['file'] = 'parent'
1729
parser[child]['file'] = 'child'
1730
self.assertSectionNames([self.tree.basedir, parent], loc_config)
1732
def test_branch_data_default_section(self):
1733
self.assertSectionNames([None],
1734
self.branch_config._get_branch_data_config())
1736
def test_branch_default_sections(self):
1737
# No sections are defined in an empty locations file
1738
self.assertSectionNames([None, 'DEFAULT'],
1740
# Unless we define an option
1741
self.branch_config._get_location_config().set_user_option(
1742
'file', 'locations')
1743
self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
1746
def test_bazaar_named_section(self):
1747
# We need to cheat as the API doesn't give direct access to sections
1748
# other than DEFAULT.
1749
self.bazaar_config.set_alias('bazaar', 'bzr')
1750
self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
1308
1753
class TestAuthenticationConfigFile(tests.TestCase):
1309
1754
"""Test the authentication.conf file matching"""