367
432
'/home/bogus/.cache')
370
class TestIniConfig(tests.TestCase):
435
class TestXDGConfigDir(tests.TestCaseInTempDir):
436
# must be in temp dir because config tests for the existence of the bazaar
437
# subdirectory of $XDG_CONFIG_HOME
440
if sys.platform in ('darwin', 'win32'):
441
raise tests.TestNotApplicable(
442
'XDG config dir not used on this platform')
443
super(TestXDGConfigDir, self).setUp()
444
self.overrideEnv('HOME', self.test_home_dir)
445
# BZR_HOME overrides everything we want to test so unset it.
446
self.overrideEnv('BZR_HOME', None)
448
def test_xdg_config_dir_exists(self):
449
"""When ~/.config/bazaar exists, use it as the config dir."""
450
newdir = osutils.pathjoin(self.test_home_dir, '.config', 'bazaar')
452
self.assertEqual(config.config_dir(), newdir)
454
def test_xdg_config_home(self):
455
"""When XDG_CONFIG_HOME is set, use it."""
456
xdgconfigdir = osutils.pathjoin(self.test_home_dir, 'xdgconfig')
457
self.overrideEnv('XDG_CONFIG_HOME', xdgconfigdir)
458
newdir = osutils.pathjoin(xdgconfigdir, 'bazaar')
460
self.assertEqual(config.config_dir(), newdir)
463
class TestIniConfig(tests.TestCaseInTempDir):
372
465
def make_config_parser(self, s):
373
conf = config.IniBasedConfig(None)
374
parser = conf._get_parser(file=StringIO(s.encode('utf-8')))
466
conf = config.IniBasedConfig.from_string(s)
467
return conf, conf._get_parser()
378
470
class TestIniConfigBuilding(TestIniConfig):
380
472
def test_contructs(self):
381
my_config = config.IniBasedConfig("nothing")
473
my_config = config.IniBasedConfig()
383
475
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))
476
my_config = config.IniBasedConfig.from_string(sample_config_text)
477
self.assertIsInstance(my_config._get_parser(), configobj.ConfigObj)
390
479
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)
480
my_config = config.IniBasedConfig.from_string(sample_config_text)
481
parser = my_config._get_parser()
394
482
self.failUnless(my_config._get_parser() is parser)
484
def _dummy_chown(self, path, uid, gid):
485
self.path, self.uid, self.gid = path, uid, gid
487
def test_ini_config_ownership(self):
488
"""Ensure that chown is happening during _write_config_file"""
489
self.requireFeature(features.chown_feature)
490
self.overrideAttr(os, 'chown', self._dummy_chown)
491
self.path = self.uid = self.gid = None
492
conf = config.IniBasedConfig(file_name='./foo.conf')
493
conf._write_config_file()
494
self.assertEquals(self.path, './foo.conf')
495
self.assertTrue(isinstance(self.uid, int))
496
self.assertTrue(isinstance(self.gid, int))
498
def test_get_filename_parameter_is_deprecated_(self):
499
conf = self.callDeprecated([
500
'IniBasedConfig.__init__(get_filename) was deprecated in 2.3.'
501
' Use file_name instead.'],
502
config.IniBasedConfig, lambda: 'ini.conf')
503
self.assertEqual('ini.conf', conf.file_name)
505
def test_get_parser_file_parameter_is_deprecated_(self):
506
config_file = StringIO(sample_config_text.encode('utf-8'))
507
conf = config.IniBasedConfig.from_string(sample_config_text)
508
conf = self.callDeprecated([
509
'IniBasedConfig._get_parser(file=xxx) was deprecated in 2.3.'
510
' Use IniBasedConfig(_content=xxx) instead.'],
511
conf._get_parser, file=config_file)
513
class TestIniConfigSaving(tests.TestCaseInTempDir):
515
def test_cant_save_without_a_file_name(self):
516
conf = config.IniBasedConfig()
517
self.assertRaises(AssertionError, conf._write_config_file)
519
def test_saved_with_content(self):
520
content = 'foo = bar\n'
521
conf = config.IniBasedConfig.from_string(
522
content, file_name='./test.conf', save=True)
523
self.assertFileEqual(content, 'test.conf')
526
class TestIniBaseConfigOnDisk(tests.TestCaseInTempDir):
528
def test_cannot_reload_without_name(self):
529
conf = config.IniBasedConfig.from_string(sample_config_text)
530
self.assertRaises(AssertionError, conf.reload)
532
def test_reload_see_new_value(self):
533
c1 = config.IniBasedConfig.from_string('editor=vim\n',
534
file_name='./test/conf')
535
c1._write_config_file()
536
c2 = config.IniBasedConfig.from_string('editor=emacs\n',
537
file_name='./test/conf')
538
c2._write_config_file()
539
self.assertEqual('vim', c1.get_user_option('editor'))
540
self.assertEqual('emacs', c2.get_user_option('editor'))
541
# Make sure we get the Right value
543
self.assertEqual('emacs', c1.get_user_option('editor'))
546
class TestLockableConfig(tests.TestCaseInTempDir):
548
scenarios = lockable_config_scenarios()
553
config_section = None
556
super(TestLockableConfig, self).setUp()
557
self._content = '[%s]\none=1\ntwo=2\n' % (self.config_section,)
558
self.config = self.create_config(self._content)
560
def get_existing_config(self):
561
return self.config_class(*self.config_args)
563
def create_config(self, content):
564
kwargs = dict(save=True)
565
c = self.config_class.from_string(content, *self.config_args, **kwargs)
568
def test_simple_read_access(self):
569
self.assertEquals('1', self.config.get_user_option('one'))
571
def test_simple_write_access(self):
572
self.config.set_user_option('one', 'one')
573
self.assertEquals('one', self.config.get_user_option('one'))
575
def test_listen_to_the_last_speaker(self):
577
c2 = self.get_existing_config()
578
c1.set_user_option('one', 'ONE')
579
c2.set_user_option('two', 'TWO')
580
self.assertEquals('ONE', c1.get_user_option('one'))
581
self.assertEquals('TWO', c2.get_user_option('two'))
582
# The second update respect the first one
583
self.assertEquals('ONE', c2.get_user_option('one'))
585
def test_last_speaker_wins(self):
586
# If the same config is not shared, the same variable modified twice
587
# can only see a single result.
589
c2 = self.get_existing_config()
590
c1.set_user_option('one', 'c1')
591
c2.set_user_option('one', 'c2')
592
self.assertEquals('c2', c2._get_user_option('one'))
593
# The first modification is still available until another refresh
595
self.assertEquals('c1', c1._get_user_option('one'))
596
c1.set_user_option('two', 'done')
597
self.assertEquals('c2', c1._get_user_option('one'))
599
def test_writes_are_serialized(self):
601
c2 = self.get_existing_config()
603
# We spawn a thread that will pause *during* the write
604
before_writing = threading.Event()
605
after_writing = threading.Event()
606
writing_done = threading.Event()
607
c1_orig = c1._write_config_file
608
def c1_write_config_file():
611
# The lock is held we wait for the main thread to decide when to
614
c1._write_config_file = c1_write_config_file
616
c1.set_user_option('one', 'c1')
618
t1 = threading.Thread(target=c1_set_option)
619
# Collect the thread after the test
620
self.addCleanup(t1.join)
621
# Be ready to unblock the thread if the test goes wrong
622
self.addCleanup(after_writing.set)
624
before_writing.wait()
625
self.assertTrue(c1._lock.is_held)
626
self.assertRaises(errors.LockContention,
627
c2.set_user_option, 'one', 'c2')
628
self.assertEquals('c1', c1.get_user_option('one'))
629
# Let the lock be released
632
c2.set_user_option('one', 'c2')
633
self.assertEquals('c2', c2.get_user_option('one'))
635
def test_read_while_writing(self):
637
# We spawn a thread that will pause *during* the write
638
ready_to_write = threading.Event()
639
do_writing = threading.Event()
640
writing_done = threading.Event()
641
c1_orig = c1._write_config_file
642
def c1_write_config_file():
644
# The lock is held we wait for the main thread to decide when to
649
c1._write_config_file = c1_write_config_file
651
c1.set_user_option('one', 'c1')
652
t1 = threading.Thread(target=c1_set_option)
653
# Collect the thread after the test
654
self.addCleanup(t1.join)
655
# Be ready to unblock the thread if the test goes wrong
656
self.addCleanup(do_writing.set)
658
# Ensure the thread is ready to write
659
ready_to_write.wait()
660
self.assertTrue(c1._lock.is_held)
661
self.assertEquals('c1', c1.get_user_option('one'))
662
# If we read during the write, we get the old value
663
c2 = self.get_existing_config()
664
self.assertEquals('1', c2.get_user_option('one'))
665
# Let the writing occur and ensure it occurred
668
# Now we get the updated value
669
c3 = self.get_existing_config()
670
self.assertEquals('c1', c3.get_user_option('one'))
397
673
class TestGetUserOptionAs(TestIniConfig):
557
831
self.assertEqual(1, len(warnings))
558
832
self.assertEqual(warning, warnings[0])
559
trace.warning = warning
561
branch = self.make_branch('.')
562
conf = branch.get_config()
563
set_option(config.STORE_GLOBAL)
565
set_option(config.STORE_BRANCH)
567
set_option(config.STORE_GLOBAL)
568
assertWarning('Value "4" is masked by "3" from branch.conf')
569
set_option(config.STORE_GLOBAL, warn_masked=False)
571
set_option(config.STORE_LOCATION)
573
set_option(config.STORE_BRANCH)
574
assertWarning('Value "3" is masked by "0" from locations.conf')
575
set_option(config.STORE_BRANCH, warn_masked=False)
578
trace.warning = _warning
581
class TestGlobalConfigItems(tests.TestCase):
833
branch = self.make_branch('.')
834
conf = branch.get_config()
835
set_option(config.STORE_GLOBAL)
837
set_option(config.STORE_BRANCH)
839
set_option(config.STORE_GLOBAL)
840
assertWarning('Value "4" is masked by "3" from branch.conf')
841
set_option(config.STORE_GLOBAL, warn_masked=False)
843
set_option(config.STORE_LOCATION)
845
set_option(config.STORE_BRANCH)
846
assertWarning('Value "3" is masked by "0" from locations.conf')
847
set_option(config.STORE_BRANCH, warn_masked=False)
851
class TestGlobalConfigItems(tests.TestCaseInTempDir):
583
853
def test_user_id(self):
584
config_file = StringIO(sample_config_text.encode('utf-8'))
585
my_config = config.GlobalConfig()
586
my_config._parser = my_config._get_parser(file=config_file)
854
my_config = config.GlobalConfig.from_string(sample_config_text)
587
855
self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
588
856
my_config._get_user_id())
590
858
def test_absent_user_id(self):
591
config_file = StringIO("")
592
859
my_config = config.GlobalConfig()
593
my_config._parser = my_config._get_parser(file=config_file)
594
860
self.assertEqual(None, my_config._get_user_id())
596
862
def test_configured_editor(self):
597
config_file = StringIO(sample_config_text.encode('utf-8'))
598
my_config = config.GlobalConfig()
599
my_config._parser = my_config._get_parser(file=config_file)
863
my_config = config.GlobalConfig.from_string(sample_config_text)
600
864
self.assertEqual("vim", my_config.get_editor())
602
866
def test_signatures_always(self):
603
config_file = StringIO(sample_always_signatures)
604
my_config = config.GlobalConfig()
605
my_config._parser = my_config._get_parser(file=config_file)
867
my_config = config.GlobalConfig.from_string(sample_always_signatures)
606
868
self.assertEqual(config.CHECK_NEVER,
607
869
my_config.signature_checking())
608
870
self.assertEqual(config.SIGN_ALWAYS,
989
1246
self.assertEqual('bzrlib.tests.test_config.post_commit',
990
1247
self.my_config.post_commit())
992
def get_branch_config(self, location, global_config=None):
1249
def get_branch_config(self, location, global_config=None,
1250
location_config=None):
1251
my_branch = FakeBranch(location)
993
1252
if global_config is None:
994
global_file = StringIO(sample_config_text.encode('utf-8'))
996
global_file = StringIO(global_config.encode('utf-8'))
997
branches_file = StringIO(sample_branches_text.encode('utf-8'))
998
self.my_config = config.BranchConfig(FakeBranch(location))
999
# Force location config to use specified file
1000
self.my_location_config = self.my_config._get_location_config()
1001
self.my_location_config._get_parser(branches_file)
1002
# Force global config to use specified file
1003
self.my_config._get_global_config()._get_parser(global_file)
1253
global_config = sample_config_text
1254
if location_config is None:
1255
location_config = sample_branches_text
1257
my_global_config = config.GlobalConfig.from_string(global_config,
1259
my_location_config = config.LocationConfig.from_string(
1260
location_config, my_branch.base, save=True)
1261
my_config = config.BranchConfig(my_branch)
1262
self.my_config = my_config
1263
self.my_location_config = my_config._get_location_config()
1005
1265
def test_set_user_setting_sets_and_saves(self):
1006
1266
self.get_branch_config('/a/c')
1007
1267
record = InstrumentedConfigObj("foo")
1008
1268
self.my_location_config._parser = record
1010
real_mkdir = os.mkdir
1011
self.created = False
1012
def checked_mkdir(path, mode=0777):
1013
self.log('making directory: %s', path)
1014
real_mkdir(path, mode)
1017
os.mkdir = checked_mkdir
1019
self.callDeprecated(['The recurse option is deprecated as of '
1020
'0.14. The section "/a/c" has been '
1021
'converted to use policies.'],
1022
self.my_config.set_user_option,
1023
'foo', 'bar', store=config.STORE_LOCATION)
1025
os.mkdir = real_mkdir
1027
self.failUnless(self.created, 'Failed to create ~/.bazaar')
1028
self.assertEqual([('__contains__', '/a/c'),
1270
self.callDeprecated(['The recurse option is deprecated as of '
1271
'0.14. The section "/a/c" has been '
1272
'converted to use policies.'],
1273
self.my_config.set_user_option,
1274
'foo', 'bar', store=config.STORE_LOCATION)
1275
self.assertEqual([('reload',),
1276
('__contains__', '/a/c'),
1029
1277
('__contains__', '/a/c/'),
1030
1278
('__setitem__', '/a/c', {}),
1031
1279
('__getitem__', '/a/c'),
1312
1559
self.assertIs(None, bzrdir_config.get_default_stack_on())
1562
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
1565
super(TestConfigGetOptions, self).setUp()
1566
create_configs(self)
1568
# One variable in none of the above
1569
def test_no_variable(self):
1570
# Using branch should query branch, locations and bazaar
1571
self.assertOptions([], self.branch_config)
1573
def test_option_in_bazaar(self):
1574
self.bazaar_config.set_user_option('file', 'bazaar')
1575
self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
1578
def test_option_in_locations(self):
1579
self.locations_config.set_user_option('file', 'locations')
1581
[('file', 'locations', self.tree.basedir, 'locations')],
1582
self.locations_config)
1584
def test_option_in_branch(self):
1585
self.branch_config.set_user_option('file', 'branch')
1586
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
1589
def test_option_in_bazaar_and_branch(self):
1590
self.bazaar_config.set_user_option('file', 'bazaar')
1591
self.branch_config.set_user_option('file', 'branch')
1592
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
1593
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
1596
def test_option_in_branch_and_locations(self):
1597
# Hmm, locations override branch :-/
1598
self.locations_config.set_user_option('file', 'locations')
1599
self.branch_config.set_user_option('file', 'branch')
1601
[('file', 'locations', self.tree.basedir, 'locations'),
1602
('file', 'branch', 'DEFAULT', 'branch'),],
1605
def test_option_in_bazaar_locations_and_branch(self):
1606
self.bazaar_config.set_user_option('file', 'bazaar')
1607
self.locations_config.set_user_option('file', 'locations')
1608
self.branch_config.set_user_option('file', 'branch')
1610
[('file', 'locations', self.tree.basedir, 'locations'),
1611
('file', 'branch', 'DEFAULT', 'branch'),
1612
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
1616
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
1619
super(TestConfigRemoveOption, self).setUp()
1620
create_configs_with_file_option(self)
1622
def test_remove_in_locations(self):
1623
self.locations_config.remove_user_option('file', self.tree.basedir)
1625
[('file', 'branch', 'DEFAULT', 'branch'),
1626
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
1629
def test_remove_in_branch(self):
1630
self.branch_config.remove_user_option('file')
1632
[('file', 'locations', self.tree.basedir, 'locations'),
1633
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
1636
def test_remove_in_bazaar(self):
1637
self.bazaar_config.remove_user_option('file')
1639
[('file', 'locations', self.tree.basedir, 'locations'),
1640
('file', 'branch', 'DEFAULT', 'branch'),],
1644
class TestConfigGetSections(tests.TestCaseWithTransport):
1647
super(TestConfigGetSections, self).setUp()
1648
create_configs(self)
1650
def assertSectionNames(self, expected, conf, name=None):
1651
"""Check which sections are returned for a given config.
1653
If fallback configurations exist their sections can be included.
1655
:param expected: A list of section names.
1657
:param conf: The configuration that will be queried.
1659
:param name: An optional section name that will be passed to
1662
sections = list(conf._get_sections(name))
1663
self.assertLength(len(expected), sections)
1664
self.assertEqual(expected, [name for name, _, _ in sections])
1666
def test_bazaar_default_section(self):
1667
self.assertSectionNames(['DEFAULT'], self.bazaar_config)
1669
def test_locations_default_section(self):
1670
# No sections are defined in an empty file
1671
self.assertSectionNames([], self.locations_config)
1673
def test_locations_named_section(self):
1674
self.locations_config.set_user_option('file', 'locations')
1675
self.assertSectionNames([self.tree.basedir], self.locations_config)
1677
def test_locations_matching_sections(self):
1678
loc_config = self.locations_config
1679
loc_config.set_user_option('file', 'locations')
1680
# We need to cheat a bit here to create an option in sections above and
1681
# below the 'location' one.
1682
parser = loc_config._get_parser()
1683
# locations.cong deals with '/' ignoring native os.sep
1684
location_names = self.tree.basedir.split('/')
1685
parent = '/'.join(location_names[:-1])
1686
child = '/'.join(location_names + ['child'])
1688
parser[parent]['file'] = 'parent'
1690
parser[child]['file'] = 'child'
1691
self.assertSectionNames([self.tree.basedir, parent], loc_config)
1693
def test_branch_data_default_section(self):
1694
self.assertSectionNames([None],
1695
self.branch_config._get_branch_data_config())
1697
def test_branch_default_sections(self):
1698
# No sections are defined in an empty locations file
1699
self.assertSectionNames([None, 'DEFAULT'],
1701
# Unless we define an option
1702
self.branch_config._get_location_config().set_user_option(
1703
'file', 'locations')
1704
self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
1707
def test_bazaar_named_section(self):
1708
# We need to cheat as the API doesn't give direct access to sections
1709
# other than DEFAULT.
1710
self.bazaar_config.set_alias('bazaar', 'bzr')
1711
self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
1315
1714
class TestAuthenticationConfigFile(tests.TestCase):
1316
1715
"""Test the authentication.conf file matching"""