367
386
'/home/bogus/.cache')
370
class TestIniConfig(tests.TestCase):
389
class TestIniConfig(tests.TestCaseInTempDir):
372
391
def make_config_parser(self, s):
373
conf = config.IniBasedConfig(None)
374
parser = conf._get_parser(file=StringIO(s.encode('utf-8')))
392
conf = config.IniBasedConfig.from_string(s)
393
return conf, conf._get_parser()
378
396
class TestIniConfigBuilding(TestIniConfig):
380
398
def test_contructs(self):
381
my_config = config.IniBasedConfig("nothing")
399
my_config = config.IniBasedConfig()
383
401
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))
402
my_config = config.IniBasedConfig.from_string(sample_config_text)
403
self.assertIsInstance(my_config._get_parser(), configobj.ConfigObj)
390
405
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)
406
my_config = config.IniBasedConfig.from_string(sample_config_text)
407
parser = my_config._get_parser()
394
408
self.failUnless(my_config._get_parser() is parser)
410
def _dummy_chown(self, path, uid, gid):
411
self.path, self.uid, self.gid = path, uid, gid
413
def test_ini_config_ownership(self):
414
"""Ensure that chown is happening during _write_config_file"""
415
self.requireFeature(features.chown_feature)
416
self.overrideAttr(os, 'chown', self._dummy_chown)
417
self.path = self.uid = self.gid = None
418
conf = config.IniBasedConfig(file_name='./foo.conf')
419
conf._write_config_file()
420
self.assertEquals(self.path, './foo.conf')
421
self.assertTrue(isinstance(self.uid, int))
422
self.assertTrue(isinstance(self.gid, int))
424
def test_get_filename_parameter_is_deprecated_(self):
425
conf = self.callDeprecated([
426
'IniBasedConfig.__init__(get_filename) was deprecated in 2.3.'
427
' Use file_name instead.'],
428
config.IniBasedConfig, lambda: 'ini.conf')
429
self.assertEqual('ini.conf', conf.file_name)
431
def test_get_parser_file_parameter_is_deprecated_(self):
432
config_file = StringIO(sample_config_text.encode('utf-8'))
433
conf = config.IniBasedConfig.from_string(sample_config_text)
434
conf = self.callDeprecated([
435
'IniBasedConfig._get_parser(file=xxx) was deprecated in 2.3.'
436
' Use IniBasedConfig(_content=xxx) instead.'],
437
conf._get_parser, file=config_file)
439
class TestIniConfigSaving(tests.TestCaseInTempDir):
441
def test_cant_save_without_a_file_name(self):
442
conf = config.IniBasedConfig()
443
self.assertRaises(AssertionError, conf._write_config_file)
445
def test_saved_with_content(self):
446
content = 'foo = bar\n'
447
conf = config.IniBasedConfig.from_string(
448
content, file_name='./test.conf', save=True)
449
self.assertFileEqual(content, 'test.conf')
452
class TestIniBaseConfigOnDisk(tests.TestCaseInTempDir):
454
def test_cannot_reload_without_name(self):
455
conf = config.IniBasedConfig.from_string(sample_config_text)
456
self.assertRaises(AssertionError, conf.reload)
458
def test_reload_see_new_value(self):
459
c1 = config.IniBasedConfig.from_string('editor=vim\n',
460
file_name='./test/conf')
461
c1._write_config_file()
462
c2 = config.IniBasedConfig.from_string('editor=emacs\n',
463
file_name='./test/conf')
464
c2._write_config_file()
465
self.assertEqual('vim', c1.get_user_option('editor'))
466
self.assertEqual('emacs', c2.get_user_option('editor'))
467
# Make sure we get the Right value
469
self.assertEqual('emacs', c1.get_user_option('editor'))
472
class TestLockableConfig(tests.TestCaseInTempDir):
474
scenarios = lockable_config_scenarios()
479
config_section = None
482
super(TestLockableConfig, self).setUp()
483
self._content = '[%s]\none=1\ntwo=2\n' % (self.config_section,)
484
self.config = self.create_config(self._content)
486
def get_existing_config(self):
487
return self.config_class(*self.config_args)
489
def create_config(self, content):
490
kwargs = dict(save=True)
491
c = self.config_class.from_string(content, *self.config_args, **kwargs)
494
def test_simple_read_access(self):
495
self.assertEquals('1', self.config.get_user_option('one'))
497
def test_simple_write_access(self):
498
self.config.set_user_option('one', 'one')
499
self.assertEquals('one', self.config.get_user_option('one'))
501
def test_listen_to_the_last_speaker(self):
503
c2 = self.get_existing_config()
504
c1.set_user_option('one', 'ONE')
505
c2.set_user_option('two', 'TWO')
506
self.assertEquals('ONE', c1.get_user_option('one'))
507
self.assertEquals('TWO', c2.get_user_option('two'))
508
# The second update respect the first one
509
self.assertEquals('ONE', c2.get_user_option('one'))
511
def test_last_speaker_wins(self):
512
# If the same config is not shared, the same variable modified twice
513
# can only see a single result.
515
c2 = self.get_existing_config()
516
c1.set_user_option('one', 'c1')
517
c2.set_user_option('one', 'c2')
518
self.assertEquals('c2', c2._get_user_option('one'))
519
# The first modification is still available until another refresh
521
self.assertEquals('c1', c1._get_user_option('one'))
522
c1.set_user_option('two', 'done')
523
self.assertEquals('c2', c1._get_user_option('one'))
525
def test_writes_are_serialized(self):
527
c2 = self.get_existing_config()
529
# We spawn a thread that will pause *during* the write
530
before_writing = threading.Event()
531
after_writing = threading.Event()
532
writing_done = threading.Event()
533
c1_orig = c1._write_config_file
534
def c1_write_config_file():
537
# The lock is held we wait for the main thread to decide when to
540
c1._write_config_file = c1_write_config_file
542
c1.set_user_option('one', 'c1')
544
t1 = threading.Thread(target=c1_set_option)
545
# Collect the thread after the test
546
self.addCleanup(t1.join)
547
# Be ready to unblock the thread if the test goes wrong
548
self.addCleanup(after_writing.set)
550
before_writing.wait()
551
self.assertTrue(c1._lock.is_held)
552
self.assertRaises(errors.LockContention,
553
c2.set_user_option, 'one', 'c2')
554
self.assertEquals('c1', c1.get_user_option('one'))
555
# Let the lock be released
558
c2.set_user_option('one', 'c2')
559
self.assertEquals('c2', c2.get_user_option('one'))
561
def test_read_while_writing(self):
563
# We spawn a thread that will pause *during* the write
564
ready_to_write = threading.Event()
565
do_writing = threading.Event()
566
writing_done = threading.Event()
567
c1_orig = c1._write_config_file
568
def c1_write_config_file():
570
# The lock is held we wait for the main thread to decide when to
575
c1._write_config_file = c1_write_config_file
577
c1.set_user_option('one', 'c1')
578
t1 = threading.Thread(target=c1_set_option)
579
# Collect the thread after the test
580
self.addCleanup(t1.join)
581
# Be ready to unblock the thread if the test goes wrong
582
self.addCleanup(do_writing.set)
584
# Ensure the thread is ready to write
585
ready_to_write.wait()
586
self.assertTrue(c1._lock.is_held)
587
self.assertEquals('c1', c1.get_user_option('one'))
588
# If we read during the write, we get the old value
589
c2 = self.get_existing_config()
590
self.assertEquals('1', c2.get_user_option('one'))
591
# Let the writing occur and ensure it occurred
594
# Now we get the updated value
595
c3 = self.get_existing_config()
596
self.assertEquals('c1', c3.get_user_option('one'))
397
599
class TestGetUserOptionAs(TestIniConfig):
557
757
self.assertEqual(1, len(warnings))
558
758
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):
759
branch = self.make_branch('.')
760
conf = branch.get_config()
761
set_option(config.STORE_GLOBAL)
763
set_option(config.STORE_BRANCH)
765
set_option(config.STORE_GLOBAL)
766
assertWarning('Value "4" is masked by "3" from branch.conf')
767
set_option(config.STORE_GLOBAL, warn_masked=False)
769
set_option(config.STORE_LOCATION)
771
set_option(config.STORE_BRANCH)
772
assertWarning('Value "3" is masked by "0" from locations.conf')
773
set_option(config.STORE_BRANCH, warn_masked=False)
777
class TestGlobalConfigItems(tests.TestCaseInTempDir):
583
779
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)
780
my_config = config.GlobalConfig.from_string(sample_config_text)
587
781
self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
588
782
my_config._get_user_id())
590
784
def test_absent_user_id(self):
591
config_file = StringIO("")
592
785
my_config = config.GlobalConfig()
593
my_config._parser = my_config._get_parser(file=config_file)
594
786
self.assertEqual(None, my_config._get_user_id())
596
788
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)
789
my_config = config.GlobalConfig.from_string(sample_config_text)
600
790
self.assertEqual("vim", my_config.get_editor())
602
792
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)
793
my_config = config.GlobalConfig.from_string(sample_always_signatures)
606
794
self.assertEqual(config.CHECK_NEVER,
607
795
my_config.signature_checking())
608
796
self.assertEqual(config.SIGN_ALWAYS,
990
1158
self.my_config.post_commit())
992
1160
def get_branch_config(self, location, global_config=None):
1161
my_branch = FakeBranch(location)
993
1162
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)
1163
global_config = sample_config_text
1165
my_global_config = config.GlobalConfig.from_string(global_config,
1167
my_location_config = config.LocationConfig.from_string(
1168
sample_branches_text, my_branch.base, save=True)
1169
my_config = config.BranchConfig(my_branch)
1170
self.my_config = my_config
1171
self.my_location_config = my_config._get_location_config()
1005
1173
def test_set_user_setting_sets_and_saves(self):
1006
1174
self.get_branch_config('/a/c')
1007
1175
record = InstrumentedConfigObj("foo")
1008
1176
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'),
1178
self.callDeprecated(['The recurse option is deprecated as of '
1179
'0.14. The section "/a/c" has been '
1180
'converted to use policies.'],
1181
self.my_config.set_user_option,
1182
'foo', 'bar', store=config.STORE_LOCATION)
1183
self.assertEqual([('reload',),
1184
('__contains__', '/a/c'),
1029
1185
('__contains__', '/a/c/'),
1030
1186
('__setitem__', '/a/c', {}),
1031
1187
('__getitem__', '/a/c'),
1137
1292
def test_gpg_signing_command(self):
1138
1293
my_config = self.get_branch_config(
1294
global_config=sample_config_text,
1139
1295
# branch data cannot set gpg_signing_command
1140
1296
branch_data_config="gpg_signing_command=pgp")
1141
config_file = StringIO(sample_config_text.encode('utf-8'))
1142
my_config._get_global_config()._get_parser(config_file)
1143
1297
self.assertEqual('gnome-gpg', my_config.gpg_signing_command())
1145
1299
def test_get_user_option_global(self):
1146
branch = FakeBranch()
1147
my_config = config.BranchConfig(branch)
1148
config_file = StringIO(sample_config_text.encode('utf-8'))
1149
(my_config._get_global_config()._get_parser(config_file))
1300
my_config = self.get_branch_config(global_config=sample_config_text)
1150
1301
self.assertEqual('something',
1151
1302
my_config.get_user_option('user_global_option'))
1153
1304
def test_post_commit_default(self):
1154
branch = FakeBranch()
1155
my_config = self.get_branch_config(sample_config_text, '/a/c',
1156
sample_branches_text)
1305
my_config = self.get_branch_config(global_config=sample_config_text,
1307
location_config=sample_branches_text)
1157
1308
self.assertEqual(my_config.branch.base, '/a/c')
1158
1309
self.assertEqual('bzrlib.tests.test_config.post_commit',
1159
1310
my_config.post_commit())
1160
1311
my_config.set_user_option('post_commit', 'rmtree_root')
1161
# post-commit is ignored when bresent in branch data
1312
# post-commit is ignored when present in branch data
1162
1313
self.assertEqual('bzrlib.tests.test_config.post_commit',
1163
1314
my_config.post_commit())
1164
1315
my_config.set_user_option('post_commit', 'rmtree_root',
1166
1317
self.assertEqual('rmtree_root', my_config.post_commit())
1168
1319
def test_config_precedence(self):
1320
# FIXME: eager test, luckily no persitent config file makes it fail
1169
1322
my_config = self.get_branch_config(global_config=precedence_global)
1170
1323
self.assertEqual(my_config.get_user_option('option'), 'global')
1171
1324
my_config = self.get_branch_config(global_config=precedence_global,
1172
branch_data_config=precedence_branch)
1325
branch_data_config=precedence_branch)
1173
1326
self.assertEqual(my_config.get_user_option('option'), 'branch')
1174
my_config = self.get_branch_config(global_config=precedence_global,
1175
branch_data_config=precedence_branch,
1176
location_config=precedence_location)
1327
my_config = self.get_branch_config(
1328
global_config=precedence_global,
1329
branch_data_config=precedence_branch,
1330
location_config=precedence_location)
1177
1331
self.assertEqual(my_config.get_user_option('option'), 'recurse')
1178
my_config = self.get_branch_config(global_config=precedence_global,
1179
branch_data_config=precedence_branch,
1180
location_config=precedence_location,
1181
location='http://example.com/specific')
1332
my_config = self.get_branch_config(
1333
global_config=precedence_global,
1334
branch_data_config=precedence_branch,
1335
location_config=precedence_location,
1336
location='http://example.com/specific')
1182
1337
self.assertEqual(my_config.get_user_option('option'), 'exact')
1184
1339
def test_get_mail_client(self):
1312
1467
self.assertIs(None, bzrdir_config.get_default_stack_on())
1470
def create_configs(test):
1471
"""Create configuration files for a given test.
1473
This requires creating a tree (and populate the ``test.tree`` attribute)
1474
and its associated branch and will populate the following attributes:
1476
- branch_config: A BranchConfig for the associated branch.
1478
- locations_config : A LocationConfig for the associated branch
1480
- bazaar_config: A GlobalConfig.
1482
The tree and branch are created in a 'tree' subdirectory so the tests can
1483
still use the test directory to stay outside of the branch.
1485
tree = test.make_branch_and_tree('tree')
1487
test.branch_config = config.BranchConfig(tree.branch)
1488
test.locations_config = config.LocationConfig(tree.basedir)
1489
test.bazaar_config = config.GlobalConfig()
1492
def create_configs_with_file_option(test):
1493
"""Create configuration files with a ``file`` option set in each.
1495
This builds on ``create_configs`` and add one ``file`` option in each
1496
configuration with a value which allows identifying the configuration file.
1498
create_configs(test)
1499
test.bazaar_config.set_user_option('file', 'bazaar')
1500
test.locations_config.set_user_option('file', 'locations')
1501
test.branch_config.set_user_option('file', 'branch')
1504
class TestConfigGetOptions(tests.TestCaseWithTransport):
1507
super(TestConfigGetOptions, self).setUp()
1508
create_configs(self)
1510
def assertOptions(self, expected, conf):
1511
actual = list(conf._get_options())
1512
self.assertEqual(expected, actual)
1514
# One variable in none of the above
1515
def test_no_variable(self):
1516
# Using branch should query branch, locations and bazaar
1517
self.assertOptions([], self.branch_config)
1519
def test_option_in_bazaar(self):
1520
self.bazaar_config.set_user_option('file', 'bazaar')
1521
self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
1524
def test_option_in_locations(self):
1525
self.locations_config.set_user_option('file', 'locations')
1527
[('file', 'locations', self.tree.basedir, 'locations')],
1528
self.locations_config)
1530
def test_option_in_branch(self):
1531
self.branch_config.set_user_option('file', 'branch')
1532
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
1535
def test_option_in_bazaar_and_branch(self):
1536
self.bazaar_config.set_user_option('file', 'bazaar')
1537
self.branch_config.set_user_option('file', 'branch')
1538
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
1539
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
1542
def test_option_in_branch_and_locations(self):
1543
# Hmm, locations override branch :-/
1544
self.locations_config.set_user_option('file', 'locations')
1545
self.branch_config.set_user_option('file', 'branch')
1547
[('file', 'locations', self.tree.basedir, 'locations'),
1548
('file', 'branch', 'DEFAULT', 'branch'),],
1551
def test_option_in_bazaar_locations_and_branch(self):
1552
self.bazaar_config.set_user_option('file', 'bazaar')
1553
self.locations_config.set_user_option('file', 'locations')
1554
self.branch_config.set_user_option('file', 'branch')
1556
[('file', 'locations', self.tree.basedir, 'locations'),
1557
('file', 'branch', 'DEFAULT', 'branch'),
1558
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
1562
class TestConfigRemoveOption(tests.TestCaseWithTransport):
1565
super(TestConfigRemoveOption, self).setUp()
1566
create_configs_with_file_option(self)
1568
def assertOptions(self, expected, conf):
1569
actual = list(conf._get_options())
1570
self.assertEqual(expected, actual)
1572
def test_remove_in_locations(self):
1573
self.locations_config.remove_user_option('file', self.tree.basedir)
1575
[('file', 'branch', 'DEFAULT', 'branch'),
1576
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
1579
def test_remove_in_branch(self):
1580
self.branch_config.remove_user_option('file')
1582
[('file', 'locations', self.tree.basedir, 'locations'),
1583
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
1586
def test_remove_in_bazaar(self):
1587
self.bazaar_config.remove_user_option('file')
1589
[('file', 'locations', self.tree.basedir, 'locations'),
1590
('file', 'branch', 'DEFAULT', 'branch'),],
1594
class TestConfigGetSections(tests.TestCaseWithTransport):
1597
super(TestConfigGetSections, self).setUp()
1598
create_configs(self)
1600
def assertSectionNames(self, expected, conf, name=None):
1601
"""Check which sections are returned for a given config.
1603
If fallback configurations exist their sections can be included.
1605
:param expected: A list of section names.
1607
:param conf: The configuration that will be queried.
1609
:param name: An optional section name that will be passed to
1612
sections = list(conf._get_sections(name))
1613
self.assertLength(len(expected), sections)
1614
self.assertEqual(expected, [name for name, _, _ in sections])
1616
def test_bazaar_default_section(self):
1617
self.assertSectionNames(['DEFAULT'], self.bazaar_config)
1619
def test_locations_default_section(self):
1620
# No sections are defined in an empty file
1621
self.assertSectionNames([], self.locations_config)
1623
def test_locations_named_section(self):
1624
self.locations_config.set_user_option('file', 'locations')
1625
self.assertSectionNames([self.tree.basedir], self.locations_config)
1627
def test_locations_matching_sections(self):
1628
loc_config = self.locations_config
1629
loc_config.set_user_option('file', 'locations')
1630
# We need to cheat a bit here to create an option in sections above and
1631
# below the 'location' one.
1632
parser = loc_config._get_parser()
1633
# locations.cong deals with '/' ignoring native os.sep
1634
location_names = self.tree.basedir.split('/')
1635
parent = '/'.join(location_names[:-1])
1636
child = '/'.join(location_names + ['child'])
1638
parser[parent]['file'] = 'parent'
1640
parser[child]['file'] = 'child'
1641
self.assertSectionNames([self.tree.basedir, parent], loc_config)
1643
def test_branch_data_default_section(self):
1644
self.assertSectionNames([None],
1645
self.branch_config._get_branch_data_config())
1647
def test_branch_default_sections(self):
1648
# No sections are defined in an empty locations file
1649
self.assertSectionNames([None, 'DEFAULT'],
1651
# Unless we define an option
1652
self.branch_config._get_location_config().set_user_option(
1653
'file', 'locations')
1654
self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
1657
def test_bazaar_named_section(self):
1658
# We need to cheat as the API doesn't give direct access to sections
1659
# other than DEFAULT.
1660
self.bazaar_config.set_alias('bazaar', 'bzr')
1661
self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
1315
1664
class TestAuthenticationConfigFile(tests.TestCase):
1316
1665
"""Test the authentication.conf file matching"""