357
1438
# This is testing the correct file names are provided.
358
1439
# TODO: consolidate with the test for GlobalConfigs filename checks.
360
# replace the class that is constructured, to check its parameters
1441
# replace the class that is constructed, to check its parameters
361
1442
oldparserclass = config.ConfigObj
362
1443
config.ConfigObj = InstrumentedConfigObj
363
my_config = config.LocationConfig('http://www.example.com')
1445
my_config = config.LocationConfig('http://www.example.com')
365
1446
parser = my_config._get_parser()
367
1448
config.ConfigObj = oldparserclass
368
self.failUnless(isinstance(parser, InstrumentedConfigObj))
1449
self.assertIsInstance(parser, InstrumentedConfigObj)
369
1450
self.assertEqual(parser._calls,
370
[('__init__', config.branches_config_filename())])
1451
[('__init__', config.locations_config_filename(),
372
1454
def test_get_global_config(self):
373
my_config = config.LocationConfig('http://example.com')
1455
my_config = config.BranchConfig(FakeBranch('http://example.com'))
374
1456
global_config = my_config._get_global_config()
375
self.failUnless(isinstance(global_config, config.GlobalConfig))
376
self.failUnless(global_config is my_config._get_global_config())
378
def test__get_section_no_match(self):
379
self.get_location_config('/')
380
self.assertEqual(None, self.my_config._get_section())
382
def test__get_section_exact(self):
383
self.get_location_config('http://www.example.com')
384
self.assertEqual('http://www.example.com',
385
self.my_config._get_section())
387
def test__get_section_suffix_does_not(self):
388
self.get_location_config('http://www.example.com-com')
389
self.assertEqual(None, self.my_config._get_section())
391
def test__get_section_subdir_recursive(self):
392
self.get_location_config('http://www.example.com/com')
393
self.assertEqual('http://www.example.com',
394
self.my_config._get_section())
396
def test__get_section_subdir_matches(self):
397
self.get_location_config('http://www.example.com/useglobal')
398
self.assertEqual('http://www.example.com/useglobal',
399
self.my_config._get_section())
401
def test__get_section_subdir_nonrecursive(self):
402
self.get_location_config(
403
'http://www.example.com/useglobal/childbranch')
404
self.assertEqual('http://www.example.com',
405
self.my_config._get_section())
407
def test__get_section_subdir_trailing_slash(self):
408
self.get_location_config('/b')
409
self.assertEqual('/b/', self.my_config._get_section())
411
def test__get_section_subdir_child(self):
412
self.get_location_config('/a/foo')
413
self.assertEqual('/a/*', self.my_config._get_section())
415
def test__get_section_subdir_child_child(self):
416
self.get_location_config('/a/foo/bar')
417
self.assertEqual('/a/', self.my_config._get_section())
419
def test__get_section_trailing_slash_with_children(self):
420
self.get_location_config('/a/')
421
self.assertEqual('/a/', self.my_config._get_section())
423
def test__get_section_explicit_over_glob(self):
424
self.get_location_config('/a/c')
425
self.assertEqual('/a/c', self.my_config._get_section())
427
def get_location_config(self, location, global_config=None):
428
if global_config is None:
429
global_file = StringIO(sample_config_text)
431
global_file = StringIO(global_config)
432
branches_file = StringIO(sample_branches_text)
433
self.my_config = config.LocationConfig(location)
434
self.my_config._get_parser(branches_file)
435
self.my_config._get_global_config()._get_parser(global_file)
1457
self.assertIsInstance(global_config, config.GlobalConfig)
1458
self.assertIs(global_config, my_config._get_global_config())
1460
def assertLocationMatching(self, expected):
1461
self.assertEqual(expected,
1462
list(self.my_location_config._get_matching_sections()))
1464
def test__get_matching_sections_no_match(self):
1465
self.get_branch_config('/')
1466
self.assertLocationMatching([])
1468
def test__get_matching_sections_exact(self):
1469
self.get_branch_config('http://www.example.com')
1470
self.assertLocationMatching([('http://www.example.com', '')])
1472
def test__get_matching_sections_suffix_does_not(self):
1473
self.get_branch_config('http://www.example.com-com')
1474
self.assertLocationMatching([])
1476
def test__get_matching_sections_subdir_recursive(self):
1477
self.get_branch_config('http://www.example.com/com')
1478
self.assertLocationMatching([('http://www.example.com', 'com')])
1480
def test__get_matching_sections_ignoreparent(self):
1481
self.get_branch_config('http://www.example.com/ignoreparent')
1482
self.assertLocationMatching([('http://www.example.com/ignoreparent',
1485
def test__get_matching_sections_ignoreparent_subdir(self):
1486
self.get_branch_config(
1487
'http://www.example.com/ignoreparent/childbranch')
1488
self.assertLocationMatching([('http://www.example.com/ignoreparent',
1491
def test__get_matching_sections_subdir_trailing_slash(self):
1492
self.get_branch_config('/b')
1493
self.assertLocationMatching([('/b/', '')])
1495
def test__get_matching_sections_subdir_child(self):
1496
self.get_branch_config('/a/foo')
1497
self.assertLocationMatching([('/a/*', ''), ('/a/', 'foo')])
1499
def test__get_matching_sections_subdir_child_child(self):
1500
self.get_branch_config('/a/foo/bar')
1501
self.assertLocationMatching([('/a/*', 'bar'), ('/a/', 'foo/bar')])
1503
def test__get_matching_sections_trailing_slash_with_children(self):
1504
self.get_branch_config('/a/')
1505
self.assertLocationMatching([('/a/', '')])
1507
def test__get_matching_sections_explicit_over_glob(self):
1508
# XXX: 2006-09-08 jamesh
1509
# This test only passes because ord('c') > ord('*'). If there
1510
# was a config section for '/a/?', it would get precedence
1512
self.get_branch_config('/a/c')
1513
self.assertLocationMatching([('/a/c', ''), ('/a/*', ''), ('/a/', 'c')])
1515
def test__get_option_policy_normal(self):
1516
self.get_branch_config('http://www.example.com')
1518
self.my_location_config._get_config_policy(
1519
'http://www.example.com', 'normal_option'),
1522
def test__get_option_policy_norecurse(self):
1523
self.get_branch_config('http://www.example.com')
1525
self.my_location_config._get_option_policy(
1526
'http://www.example.com', 'norecurse_option'),
1527
config.POLICY_NORECURSE)
1528
# Test old recurse=False setting:
1530
self.my_location_config._get_option_policy(
1531
'http://www.example.com/norecurse', 'normal_option'),
1532
config.POLICY_NORECURSE)
1534
def test__get_option_policy_normal(self):
1535
self.get_branch_config('http://www.example.com')
1537
self.my_location_config._get_option_policy(
1538
'http://www.example.com', 'appendpath_option'),
1539
config.POLICY_APPENDPATH)
1541
def test__get_options_with_policy(self):
1542
self.get_branch_config('/dir/subdir',
1543
location_config="""\
1545
other_url = /other-dir
1546
other_url:policy = appendpath
1548
other_url = /other-subdir
1551
[(u'other_url', u'/other-subdir', u'/dir/subdir', 'locations'),
1552
(u'other_url', u'/other-dir', u'/dir', 'locations'),
1553
(u'other_url:policy', u'appendpath', u'/dir', 'locations')],
1554
self.my_location_config)
437
1556
def test_location_without_username(self):
438
self.get_location_config('http://www.example.com/useglobal')
439
self.assertEqual('Robert Collins <robertc@example.com>',
1557
self.get_branch_config('http://www.example.com/ignoreparent')
1558
self.assertEqual(u'Erik B\u00e5gfors <erik@bagfors.nu>',
440
1559
self.my_config.username())
442
1561
def test_location_not_listed(self):
443
self.get_location_config('/home/robertc/sources')
444
self.assertEqual('Robert Collins <robertc@example.com>',
1562
"""Test that the global username is used when no location matches"""
1563
self.get_branch_config('/home/robertc/sources')
1564
self.assertEqual(u'Erik B\u00e5gfors <erik@bagfors.nu>',
445
1565
self.my_config.username())
447
1567
def test_overriding_location(self):
448
self.get_location_config('http://www.example.com/foo')
1568
self.get_branch_config('http://www.example.com/foo')
449
1569
self.assertEqual('Robert Collins <robertc@example.org>',
450
1570
self.my_config.username())
452
1572
def test_signatures_not_set(self):
453
self.get_location_config('http://www.example.com',
1573
self.get_branch_config('http://www.example.com',
454
1574
global_config=sample_ignore_signatures)
455
self.assertEqual(config.CHECK_NEVER,
456
self.my_config.signature_checking())
1575
self.assertEqual(config.CHECK_ALWAYS,
1576
self.applyDeprecated(deprecated_in((2, 5, 0)),
1577
self.my_config.signature_checking))
1578
self.assertEqual(config.SIGN_NEVER,
1579
self.applyDeprecated(deprecated_in((2, 5, 0)),
1580
self.my_config.signing_policy))
458
1582
def test_signatures_never(self):
459
self.get_location_config('/a/c')
1583
self.get_branch_config('/a/c')
460
1584
self.assertEqual(config.CHECK_NEVER,
461
self.my_config.signature_checking())
1585
self.applyDeprecated(deprecated_in((2, 5, 0)),
1586
self.my_config.signature_checking))
463
1588
def test_signatures_when_available(self):
464
self.get_location_config('/a/', global_config=sample_ignore_signatures)
1589
self.get_branch_config('/a/', global_config=sample_ignore_signatures)
465
1590
self.assertEqual(config.CHECK_IF_POSSIBLE,
466
self.my_config.signature_checking())
1591
self.applyDeprecated(deprecated_in((2, 5, 0)),
1592
self.my_config.signature_checking))
468
1594
def test_signatures_always(self):
469
self.get_location_config('/b')
1595
self.get_branch_config('/b')
470
1596
self.assertEqual(config.CHECK_ALWAYS,
471
self.my_config.signature_checking())
1597
self.applyDeprecated(deprecated_in((2, 5, 0)),
1598
self.my_config.signature_checking))
473
1600
def test_gpg_signing_command(self):
474
self.get_location_config('/b')
475
self.assertEqual("gnome-gpg", self.my_config.gpg_signing_command())
1601
self.get_branch_config('/b')
1602
self.assertEqual("gnome-gpg",
1603
self.applyDeprecated(deprecated_in((2, 5, 0)),
1604
self.my_config.gpg_signing_command))
477
1606
def test_gpg_signing_command_missing(self):
478
self.get_location_config('/a')
479
self.assertEqual("false", self.my_config.gpg_signing_command())
1607
self.get_branch_config('/a')
1608
self.assertEqual("false",
1609
self.applyDeprecated(deprecated_in((2, 5, 0)),
1610
self.my_config.gpg_signing_command))
1612
def test_gpg_signing_key(self):
1613
self.get_branch_config('/b')
1614
self.assertEqual("DD4D5088", self.applyDeprecated(deprecated_in((2, 5, 0)),
1615
self.my_config.gpg_signing_key))
1617
def test_gpg_signing_key_default(self):
1618
self.get_branch_config('/a')
1619
self.assertEqual("erik@bagfors.nu",
1620
self.applyDeprecated(deprecated_in((2, 5, 0)),
1621
self.my_config.gpg_signing_key))
481
1623
def test_get_user_option_global(self):
482
self.get_location_config('/a')
1624
self.get_branch_config('/a')
483
1625
self.assertEqual('something',
484
1626
self.my_config.get_user_option('user_global_option'))
486
1628
def test_get_user_option_local(self):
487
self.get_location_config('/a')
1629
self.get_branch_config('/a')
488
1630
self.assertEqual('local',
489
1631
self.my_config.get_user_option('user_local_option'))
1633
def test_get_user_option_appendpath(self):
1634
# returned as is for the base path:
1635
self.get_branch_config('http://www.example.com')
1636
self.assertEqual('append',
1637
self.my_config.get_user_option('appendpath_option'))
1638
# Extra path components get appended:
1639
self.get_branch_config('http://www.example.com/a/b/c')
1640
self.assertEqual('append/a/b/c',
1641
self.my_config.get_user_option('appendpath_option'))
1642
# Overriden for http://www.example.com/dir, where it is a
1644
self.get_branch_config('http://www.example.com/dir/a/b/c')
1645
self.assertEqual('normal',
1646
self.my_config.get_user_option('appendpath_option'))
1648
def test_get_user_option_norecurse(self):
1649
self.get_branch_config('http://www.example.com')
1650
self.assertEqual('norecurse',
1651
self.my_config.get_user_option('norecurse_option'))
1652
self.get_branch_config('http://www.example.com/dir')
1653
self.assertEqual(None,
1654
self.my_config.get_user_option('norecurse_option'))
1655
# http://www.example.com/norecurse is a recurse=False section
1656
# that redefines normal_option. Subdirectories do not pick up
1657
# this redefinition.
1658
self.get_branch_config('http://www.example.com/norecurse')
1659
self.assertEqual('norecurse',
1660
self.my_config.get_user_option('normal_option'))
1661
self.get_branch_config('http://www.example.com/norecurse/subdir')
1662
self.assertEqual('normal',
1663
self.my_config.get_user_option('normal_option'))
1665
def test_set_user_option_norecurse(self):
1666
self.get_branch_config('http://www.example.com')
1667
self.my_config.set_user_option('foo', 'bar',
1668
store=config.STORE_LOCATION_NORECURSE)
1670
self.my_location_config._get_option_policy(
1671
'http://www.example.com', 'foo'),
1672
config.POLICY_NORECURSE)
1674
def test_set_user_option_appendpath(self):
1675
self.get_branch_config('http://www.example.com')
1676
self.my_config.set_user_option('foo', 'bar',
1677
store=config.STORE_LOCATION_APPENDPATH)
1679
self.my_location_config._get_option_policy(
1680
'http://www.example.com', 'foo'),
1681
config.POLICY_APPENDPATH)
1683
def test_set_user_option_change_policy(self):
1684
self.get_branch_config('http://www.example.com')
1685
self.my_config.set_user_option('norecurse_option', 'normal',
1686
store=config.STORE_LOCATION)
1688
self.my_location_config._get_option_policy(
1689
'http://www.example.com', 'norecurse_option'),
1692
def test_set_user_option_recurse_false_section(self):
1693
# The following section has recurse=False set. The test is to
1694
# make sure that a normal option can be added to the section,
1695
# converting recurse=False to the norecurse policy.
1696
self.get_branch_config('http://www.example.com/norecurse')
1697
self.callDeprecated(['The recurse option is deprecated as of 0.14. '
1698
'The section "http://www.example.com/norecurse" '
1699
'has been converted to use policies.'],
1700
self.my_config.set_user_option,
1701
'foo', 'bar', store=config.STORE_LOCATION)
1703
self.my_location_config._get_option_policy(
1704
'http://www.example.com/norecurse', 'foo'),
1706
# The previously existing option is still norecurse:
1708
self.my_location_config._get_option_policy(
1709
'http://www.example.com/norecurse', 'normal_option'),
1710
config.POLICY_NORECURSE)
491
1712
def test_post_commit_default(self):
492
self.get_location_config('/a/c')
493
self.assertEqual('bzrlib.selftest.testconfig.post_commit',
494
self.my_config.post_commit())
497
class TestLocationConfig(TestCaseInTempDir):
499
def get_location_config(self, location, global_config=None):
1713
self.get_branch_config('/a/c')
1714
self.assertEqual('bzrlib.tests.test_config.post_commit',
1715
self.applyDeprecated(deprecated_in((2, 5, 0)),
1716
self.my_config.post_commit))
1718
def get_branch_config(self, location, global_config=None,
1719
location_config=None):
1720
my_branch = FakeBranch(location)
500
1721
if global_config is None:
501
global_file = StringIO(sample_config_text)
503
global_file = StringIO(global_config)
504
branches_file = StringIO(sample_branches_text)
505
self.my_config = config.LocationConfig(location)
506
self.my_config._get_parser(branches_file)
507
self.my_config._get_global_config()._get_parser(global_file)
1722
global_config = sample_config_text
1723
if location_config is None:
1724
location_config = sample_branches_text
1726
my_global_config = config.GlobalConfig.from_string(global_config,
1728
my_location_config = config.LocationConfig.from_string(
1729
location_config, my_branch.base, save=True)
1730
my_config = config.BranchConfig(my_branch)
1731
self.my_config = my_config
1732
self.my_location_config = my_config._get_location_config()
509
1734
def test_set_user_setting_sets_and_saves(self):
510
# TODO RBC 20051029 test hat mkdir ~/.bazaar is called ..
511
self.get_location_config('/a/c')
1735
self.get_branch_config('/a/c')
512
1736
record = InstrumentedConfigObj("foo")
513
self.my_config._parser = record
515
# XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX
516
# broken: creates .bazaar in the top-level directory, not
517
# inside the test directory
518
self.my_config.set_user_option('foo', 'bar')
519
self.assertEqual([('__contains__', '/a/c'),
1737
self.my_location_config._parser = record
1739
self.callDeprecated(['The recurse option is deprecated as of '
1740
'0.14. The section "/a/c" has been '
1741
'converted to use policies.'],
1742
self.my_config.set_user_option,
1743
'foo', 'bar', store=config.STORE_LOCATION)
1744
self.assertEqual([('reload',),
1745
('__contains__', '/a/c'),
520
1746
('__contains__', '/a/c/'),
521
1747
('__setitem__', '/a/c', {}),
522
1748
('__getitem__', '/a/c'),
523
1749
('__setitem__', 'foo', 'bar'),
1750
('__getitem__', '/a/c'),
1751
('as_bool', 'recurse'),
1752
('__getitem__', '/a/c'),
1753
('__delitem__', 'recurse'),
1754
('__getitem__', '/a/c'),
1756
('__getitem__', '/a/c'),
1757
('__contains__', 'foo:policy'),
525
1759
record._calls[1:])
528
class TestBranchConfigItems(TestCase):
1761
def test_set_user_setting_sets_and_saves2(self):
1762
self.get_branch_config('/a/c')
1763
self.assertIs(self.my_config.get_user_option('foo'), None)
1764
self.my_config.set_user_option('foo', 'bar')
1766
self.my_config.branch.control_files.files['branch.conf'].strip(),
1768
self.assertEqual(self.my_config.get_user_option('foo'), 'bar')
1769
self.my_config.set_user_option('foo', 'baz',
1770
store=config.STORE_LOCATION)
1771
self.assertEqual(self.my_config.get_user_option('foo'), 'baz')
1772
self.my_config.set_user_option('foo', 'qux')
1773
self.assertEqual(self.my_config.get_user_option('foo'), 'baz')
1775
def test_get_bzr_remote_path(self):
1776
my_config = config.LocationConfig('/a/c')
1777
self.assertEqual('bzr', my_config.get_bzr_remote_path())
1778
my_config.set_user_option('bzr_remote_path', '/path-bzr')
1779
self.assertEqual('/path-bzr', my_config.get_bzr_remote_path())
1780
self.overrideEnv('BZR_REMOTE_PATH', '/environ-bzr')
1781
self.assertEqual('/environ-bzr', my_config.get_bzr_remote_path())
1784
precedence_global = 'option = global'
1785
precedence_branch = 'option = branch'
1786
precedence_location = """
1790
[http://example.com/specific]
1794
class TestBranchConfigItems(tests.TestCaseInTempDir):
1796
def get_branch_config(self, global_config=None, location=None,
1797
location_config=None, branch_data_config=None):
1798
my_branch = FakeBranch(location)
1799
if global_config is not None:
1800
my_global_config = config.GlobalConfig.from_string(global_config,
1802
if location_config is not None:
1803
my_location_config = config.LocationConfig.from_string(
1804
location_config, my_branch.base, save=True)
1805
my_config = config.BranchConfig(my_branch)
1806
if branch_data_config is not None:
1807
my_config.branch.control_files.files['branch.conf'] = \
530
1811
def test_user_id(self):
531
1812
branch = FakeBranch()
532
1813
my_config = config.BranchConfig(branch)
533
self.assertEqual("Robert Collins <robertc@example.net>",
534
my_config._get_user_id())
535
branch.email = "John"
536
self.assertEqual("John", my_config._get_user_id())
538
def test_not_set_in_branch(self):
539
branch = FakeBranch()
540
my_config = config.BranchConfig(branch)
542
config_file = StringIO(sample_config_text)
543
(my_config._get_location_config().
544
_get_global_config()._get_parser(config_file))
545
self.assertEqual("Robert Collins <robertc@example.com>",
546
my_config._get_user_id())
547
branch.email = "John"
548
self.assertEqual("John", my_config._get_user_id())
550
def test_BZREMAIL_OVERRIDES(self):
551
os.environ['BZREMAIL'] = "Robert Collins <robertc@example.org>"
1814
self.assertIsNot(None, my_config.username())
1815
my_config.branch.control_files.files['email'] = "John"
1816
my_config.set_user_option('email',
1817
"Robert Collins <robertc@example.org>")
1818
self.assertEqual("Robert Collins <robertc@example.org>",
1819
my_config.username())
1821
def test_BZR_EMAIL_OVERRIDES(self):
1822
self.overrideEnv('BZR_EMAIL', "Robert Collins <robertc@example.org>")
552
1823
branch = FakeBranch()
553
1824
my_config = config.BranchConfig(branch)
554
1825
self.assertEqual("Robert Collins <robertc@example.org>",
555
1826
my_config.username())
557
1828
def test_signatures_forced(self):
558
branch = FakeBranch()
559
my_config = config.BranchConfig(branch)
560
config_file = StringIO(sample_always_signatures)
561
(my_config._get_location_config().
562
_get_global_config()._get_parser(config_file))
563
self.assertEqual(config.CHECK_ALWAYS, my_config.signature_checking())
1829
my_config = self.get_branch_config(
1830
global_config=sample_always_signatures)
1831
self.assertEqual(config.CHECK_NEVER,
1832
self.applyDeprecated(deprecated_in((2, 5, 0)),
1833
my_config.signature_checking))
1834
self.assertEqual(config.SIGN_ALWAYS,
1835
self.applyDeprecated(deprecated_in((2, 5, 0)),
1836
my_config.signing_policy))
1837
self.assertTrue(self.applyDeprecated(deprecated_in((2, 5, 0)),
1838
my_config.signature_needed))
1840
def test_signatures_forced_branch(self):
1841
my_config = self.get_branch_config(
1842
global_config=sample_ignore_signatures,
1843
branch_data_config=sample_always_signatures)
1844
self.assertEqual(config.CHECK_NEVER,
1845
self.applyDeprecated(deprecated_in((2, 5, 0)),
1846
my_config.signature_checking))
1847
self.assertEqual(config.SIGN_ALWAYS,
1848
self.applyDeprecated(deprecated_in((2, 5, 0)),
1849
my_config.signing_policy))
1850
self.assertTrue(self.applyDeprecated(deprecated_in((2, 5, 0)),
1851
my_config.signature_needed))
565
1853
def test_gpg_signing_command(self):
566
branch = FakeBranch()
567
my_config = config.BranchConfig(branch)
568
config_file = StringIO(sample_config_text)
569
(my_config._get_location_config().
570
_get_global_config()._get_parser(config_file))
571
self.assertEqual('gnome-gpg', my_config.gpg_signing_command())
1854
my_config = self.get_branch_config(
1855
global_config=sample_config_text,
1856
# branch data cannot set gpg_signing_command
1857
branch_data_config="gpg_signing_command=pgp")
1858
self.assertEqual('gnome-gpg',
1859
self.applyDeprecated(deprecated_in((2, 5, 0)),
1860
my_config.gpg_signing_command))
573
1862
def test_get_user_option_global(self):
574
branch = FakeBranch()
575
my_config = config.BranchConfig(branch)
576
config_file = StringIO(sample_config_text)
577
(my_config._get_location_config().
578
_get_global_config()._get_parser(config_file))
1863
my_config = self.get_branch_config(global_config=sample_config_text)
579
1864
self.assertEqual('something',
580
1865
my_config.get_user_option('user_global_option'))
582
1867
def test_post_commit_default(self):
583
branch = FakeBranch()
585
my_config = config.BranchConfig(branch)
586
config_file = StringIO(sample_config_text)
587
(my_config._get_location_config().
588
_get_global_config()._get_parser(config_file))
589
branch_file = StringIO(sample_branches_text)
590
my_config._get_location_config()._get_parser(branch_file)
591
self.assertEqual('bzrlib.selftest.testconfig.post_commit',
592
my_config.post_commit())
595
class TestMailAddressExtraction(TestCase):
1868
my_config = self.get_branch_config(global_config=sample_config_text,
1870
location_config=sample_branches_text)
1871
self.assertEqual(my_config.branch.base, '/a/c')
1872
self.assertEqual('bzrlib.tests.test_config.post_commit',
1873
self.applyDeprecated(deprecated_in((2, 5, 0)),
1874
my_config.post_commit))
1875
my_config.set_user_option('post_commit', 'rmtree_root')
1876
# post-commit is ignored when present in branch data
1877
self.assertEqual('bzrlib.tests.test_config.post_commit',
1878
self.applyDeprecated(deprecated_in((2, 5, 0)),
1879
my_config.post_commit))
1880
my_config.set_user_option('post_commit', 'rmtree_root',
1881
store=config.STORE_LOCATION)
1882
self.assertEqual('rmtree_root',
1883
self.applyDeprecated(deprecated_in((2, 5, 0)),
1884
my_config.post_commit))
1886
def test_config_precedence(self):
1887
# FIXME: eager test, luckily no persitent config file makes it fail
1889
my_config = self.get_branch_config(global_config=precedence_global)
1890
self.assertEqual(my_config.get_user_option('option'), 'global')
1891
my_config = self.get_branch_config(global_config=precedence_global,
1892
branch_data_config=precedence_branch)
1893
self.assertEqual(my_config.get_user_option('option'), 'branch')
1894
my_config = self.get_branch_config(
1895
global_config=precedence_global,
1896
branch_data_config=precedence_branch,
1897
location_config=precedence_location)
1898
self.assertEqual(my_config.get_user_option('option'), 'recurse')
1899
my_config = self.get_branch_config(
1900
global_config=precedence_global,
1901
branch_data_config=precedence_branch,
1902
location_config=precedence_location,
1903
location='http://example.com/specific')
1904
self.assertEqual(my_config.get_user_option('option'), 'exact')
1906
def test_get_mail_client(self):
1907
config = self.get_branch_config()
1908
client = config.get_mail_client()
1909
self.assertIsInstance(client, mail_client.DefaultMail)
1912
config.set_user_option('mail_client', 'evolution')
1913
client = config.get_mail_client()
1914
self.assertIsInstance(client, mail_client.Evolution)
1916
config.set_user_option('mail_client', 'kmail')
1917
client = config.get_mail_client()
1918
self.assertIsInstance(client, mail_client.KMail)
1920
config.set_user_option('mail_client', 'mutt')
1921
client = config.get_mail_client()
1922
self.assertIsInstance(client, mail_client.Mutt)
1924
config.set_user_option('mail_client', 'thunderbird')
1925
client = config.get_mail_client()
1926
self.assertIsInstance(client, mail_client.Thunderbird)
1929
config.set_user_option('mail_client', 'default')
1930
client = config.get_mail_client()
1931
self.assertIsInstance(client, mail_client.DefaultMail)
1933
config.set_user_option('mail_client', 'editor')
1934
client = config.get_mail_client()
1935
self.assertIsInstance(client, mail_client.Editor)
1937
config.set_user_option('mail_client', 'mapi')
1938
client = config.get_mail_client()
1939
self.assertIsInstance(client, mail_client.MAPIClient)
1941
config.set_user_option('mail_client', 'xdg-email')
1942
client = config.get_mail_client()
1943
self.assertIsInstance(client, mail_client.XDGEmail)
1945
config.set_user_option('mail_client', 'firebird')
1946
self.assertRaises(errors.UnknownMailClient, config.get_mail_client)
1949
class TestMailAddressExtraction(tests.TestCase):
597
1951
def test_extract_email_address(self):
598
1952
self.assertEqual('jane@test.com',
599
1953
config.extract_email_address('Jane <jane@test.com>'))
600
self.assertRaises(errors.BzrError,
1954
self.assertRaises(errors.NoEmailInUsername,
601
1955
config.extract_email_address, 'Jane Tester')
1957
def test_parse_username(self):
1958
self.assertEqual(('', 'jdoe@example.com'),
1959
config.parse_username('jdoe@example.com'))
1960
self.assertEqual(('', 'jdoe@example.com'),
1961
config.parse_username('<jdoe@example.com>'))
1962
self.assertEqual(('John Doe', 'jdoe@example.com'),
1963
config.parse_username('John Doe <jdoe@example.com>'))
1964
self.assertEqual(('John Doe', ''),
1965
config.parse_username('John Doe'))
1966
self.assertEqual(('John Doe', 'jdoe@example.com'),
1967
config.parse_username('John Doe jdoe@example.com'))
1969
class TestTreeConfig(tests.TestCaseWithTransport):
1971
def test_get_value(self):
1972
"""Test that retreiving a value from a section is possible"""
1973
branch = self.make_branch('.')
1974
tree_config = config.TreeConfig(branch)
1975
tree_config.set_option('value', 'key', 'SECTION')
1976
tree_config.set_option('value2', 'key2')
1977
tree_config.set_option('value3-top', 'key3')
1978
tree_config.set_option('value3-section', 'key3', 'SECTION')
1979
value = tree_config.get_option('key', 'SECTION')
1980
self.assertEqual(value, 'value')
1981
value = tree_config.get_option('key2')
1982
self.assertEqual(value, 'value2')
1983
self.assertEqual(tree_config.get_option('non-existant'), None)
1984
value = tree_config.get_option('non-existant', 'SECTION')
1985
self.assertEqual(value, None)
1986
value = tree_config.get_option('non-existant', default='default')
1987
self.assertEqual(value, 'default')
1988
self.assertEqual(tree_config.get_option('key2', 'NOSECTION'), None)
1989
value = tree_config.get_option('key2', 'NOSECTION', default='default')
1990
self.assertEqual(value, 'default')
1991
value = tree_config.get_option('key3')
1992
self.assertEqual(value, 'value3-top')
1993
value = tree_config.get_option('key3', 'SECTION')
1994
self.assertEqual(value, 'value3-section')
1997
class TestTransportConfig(tests.TestCaseWithTransport):
1999
def test_load_utf8(self):
2000
"""Ensure we can load an utf8-encoded file."""
2001
t = self.get_transport()
2002
unicode_user = u'b\N{Euro Sign}ar'
2003
unicode_content = u'user=%s' % (unicode_user,)
2004
utf8_content = unicode_content.encode('utf8')
2005
# Store the raw content in the config file
2006
t.put_bytes('foo.conf', utf8_content)
2007
conf = config.TransportConfig(t, 'foo.conf')
2008
self.assertEquals(unicode_user, conf.get_option('user'))
2010
def test_load_non_ascii(self):
2011
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2012
t = self.get_transport()
2013
t.put_bytes('foo.conf', 'user=foo\n#\xff\n')
2014
conf = config.TransportConfig(t, 'foo.conf')
2015
self.assertRaises(errors.ConfigContentError, conf._get_configobj)
2017
def test_load_erroneous_content(self):
2018
"""Ensure we display a proper error on content that can't be parsed."""
2019
t = self.get_transport()
2020
t.put_bytes('foo.conf', '[open_section\n')
2021
conf = config.TransportConfig(t, 'foo.conf')
2022
self.assertRaises(errors.ParseConfigError, conf._get_configobj)
2024
def test_load_permission_denied(self):
2025
"""Ensure we get an empty config file if the file is inaccessible."""
2028
warnings.append(args[0] % args[1:])
2029
self.overrideAttr(trace, 'warning', warning)
2031
class DenyingTransport(object):
2033
def __init__(self, base):
2036
def get_bytes(self, relpath):
2037
raise errors.PermissionDenied(relpath, "")
2039
cfg = config.TransportConfig(
2040
DenyingTransport("nonexisting://"), 'control.conf')
2041
self.assertIs(None, cfg.get_option('non-existant', 'SECTION'))
2044
[u'Permission denied while trying to open configuration file '
2045
u'nonexisting:///control.conf.'])
2047
def test_get_value(self):
2048
"""Test that retreiving a value from a section is possible"""
2049
bzrdir_config = config.TransportConfig(self.get_transport('.'),
2051
bzrdir_config.set_option('value', 'key', 'SECTION')
2052
bzrdir_config.set_option('value2', 'key2')
2053
bzrdir_config.set_option('value3-top', 'key3')
2054
bzrdir_config.set_option('value3-section', 'key3', 'SECTION')
2055
value = bzrdir_config.get_option('key', 'SECTION')
2056
self.assertEqual(value, 'value')
2057
value = bzrdir_config.get_option('key2')
2058
self.assertEqual(value, 'value2')
2059
self.assertEqual(bzrdir_config.get_option('non-existant'), None)
2060
value = bzrdir_config.get_option('non-existant', 'SECTION')
2061
self.assertEqual(value, None)
2062
value = bzrdir_config.get_option('non-existant', default='default')
2063
self.assertEqual(value, 'default')
2064
self.assertEqual(bzrdir_config.get_option('key2', 'NOSECTION'), None)
2065
value = bzrdir_config.get_option('key2', 'NOSECTION',
2067
self.assertEqual(value, 'default')
2068
value = bzrdir_config.get_option('key3')
2069
self.assertEqual(value, 'value3-top')
2070
value = bzrdir_config.get_option('key3', 'SECTION')
2071
self.assertEqual(value, 'value3-section')
2073
def test_set_unset_default_stack_on(self):
2074
my_dir = self.make_bzrdir('.')
2075
bzrdir_config = config.BzrDirConfig(my_dir)
2076
self.assertIs(None, bzrdir_config.get_default_stack_on())
2077
bzrdir_config.set_default_stack_on('Foo')
2078
self.assertEqual('Foo', bzrdir_config._config.get_option(
2079
'default_stack_on'))
2080
self.assertEqual('Foo', bzrdir_config.get_default_stack_on())
2081
bzrdir_config.set_default_stack_on(None)
2082
self.assertIs(None, bzrdir_config.get_default_stack_on())
2085
class TestOldConfigHooks(tests.TestCaseWithTransport):
2088
super(TestOldConfigHooks, self).setUp()
2089
create_configs_with_file_option(self)
2091
def assertGetHook(self, conf, name, value):
2095
config.OldConfigHooks.install_named_hook('get', hook, None)
2097
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2098
self.assertLength(0, calls)
2099
actual_value = conf.get_user_option(name)
2100
self.assertEquals(value, actual_value)
2101
self.assertLength(1, calls)
2102
self.assertEquals((conf, name, value), calls[0])
2104
def test_get_hook_bazaar(self):
2105
self.assertGetHook(self.bazaar_config, 'file', 'bazaar')
2107
def test_get_hook_locations(self):
2108
self.assertGetHook(self.locations_config, 'file', 'locations')
2110
def test_get_hook_branch(self):
2111
# Since locations masks branch, we define a different option
2112
self.branch_config.set_user_option('file2', 'branch')
2113
self.assertGetHook(self.branch_config, 'file2', 'branch')
2115
def assertSetHook(self, conf, name, value):
2119
config.OldConfigHooks.install_named_hook('set', hook, None)
2121
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2122
self.assertLength(0, calls)
2123
conf.set_user_option(name, value)
2124
self.assertLength(1, calls)
2125
# We can't assert the conf object below as different configs use
2126
# different means to implement set_user_option and we care only about
2128
self.assertEquals((name, value), calls[0][1:])
2130
def test_set_hook_bazaar(self):
2131
self.assertSetHook(self.bazaar_config, 'foo', 'bazaar')
2133
def test_set_hook_locations(self):
2134
self.assertSetHook(self.locations_config, 'foo', 'locations')
2136
def test_set_hook_branch(self):
2137
self.assertSetHook(self.branch_config, 'foo', 'branch')
2139
def assertRemoveHook(self, conf, name, section_name=None):
2143
config.OldConfigHooks.install_named_hook('remove', hook, None)
2145
config.OldConfigHooks.uninstall_named_hook, 'remove', None)
2146
self.assertLength(0, calls)
2147
conf.remove_user_option(name, section_name)
2148
self.assertLength(1, calls)
2149
# We can't assert the conf object below as different configs use
2150
# different means to implement remove_user_option and we care only about
2152
self.assertEquals((name,), calls[0][1:])
2154
def test_remove_hook_bazaar(self):
2155
self.assertRemoveHook(self.bazaar_config, 'file')
2157
def test_remove_hook_locations(self):
2158
self.assertRemoveHook(self.locations_config, 'file',
2159
self.locations_config.location)
2161
def test_remove_hook_branch(self):
2162
self.assertRemoveHook(self.branch_config, 'file')
2164
def assertLoadHook(self, name, conf_class, *conf_args):
2168
config.OldConfigHooks.install_named_hook('load', hook, None)
2170
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2171
self.assertLength(0, calls)
2173
conf = conf_class(*conf_args)
2174
# Access an option to trigger a load
2175
conf.get_user_option(name)
2176
self.assertLength(1, calls)
2177
# Since we can't assert about conf, we just use the number of calls ;-/
2179
def test_load_hook_bazaar(self):
2180
self.assertLoadHook('file', config.GlobalConfig)
2182
def test_load_hook_locations(self):
2183
self.assertLoadHook('file', config.LocationConfig, self.tree.basedir)
2185
def test_load_hook_branch(self):
2186
self.assertLoadHook('file', config.BranchConfig, self.tree.branch)
2188
def assertSaveHook(self, conf):
2192
config.OldConfigHooks.install_named_hook('save', hook, None)
2194
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2195
self.assertLength(0, calls)
2196
# Setting an option triggers a save
2197
conf.set_user_option('foo', 'bar')
2198
self.assertLength(1, calls)
2199
# Since we can't assert about conf, we just use the number of calls ;-/
2201
def test_save_hook_bazaar(self):
2202
self.assertSaveHook(self.bazaar_config)
2204
def test_save_hook_locations(self):
2205
self.assertSaveHook(self.locations_config)
2207
def test_save_hook_branch(self):
2208
self.assertSaveHook(self.branch_config)
2211
class TestOldConfigHooksForRemote(tests.TestCaseWithTransport):
2212
"""Tests config hooks for remote configs.
2214
No tests for the remove hook as this is not implemented there.
2218
super(TestOldConfigHooksForRemote, self).setUp()
2219
self.transport_server = test_server.SmartTCPServer_for_testing
2220
create_configs_with_file_option(self)
2222
def assertGetHook(self, conf, name, value):
2226
config.OldConfigHooks.install_named_hook('get', hook, None)
2228
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2229
self.assertLength(0, calls)
2230
actual_value = conf.get_option(name)
2231
self.assertEquals(value, actual_value)
2232
self.assertLength(1, calls)
2233
self.assertEquals((conf, name, value), calls[0])
2235
def test_get_hook_remote_branch(self):
2236
remote_branch = branch.Branch.open(self.get_url('tree'))
2237
self.assertGetHook(remote_branch._get_config(), 'file', 'branch')
2239
def test_get_hook_remote_bzrdir(self):
2240
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2241
conf = remote_bzrdir._get_config()
2242
conf.set_option('remotedir', 'file')
2243
self.assertGetHook(conf, 'file', 'remotedir')
2245
def assertSetHook(self, conf, name, value):
2249
config.OldConfigHooks.install_named_hook('set', hook, None)
2251
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2252
self.assertLength(0, calls)
2253
conf.set_option(value, name)
2254
self.assertLength(1, calls)
2255
# We can't assert the conf object below as different configs use
2256
# different means to implement set_user_option and we care only about
2258
self.assertEquals((name, value), calls[0][1:])
2260
def test_set_hook_remote_branch(self):
2261
remote_branch = branch.Branch.open(self.get_url('tree'))
2262
self.addCleanup(remote_branch.lock_write().unlock)
2263
self.assertSetHook(remote_branch._get_config(), 'file', 'remote')
2265
def test_set_hook_remote_bzrdir(self):
2266
remote_branch = branch.Branch.open(self.get_url('tree'))
2267
self.addCleanup(remote_branch.lock_write().unlock)
2268
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2269
self.assertSetHook(remote_bzrdir._get_config(), 'file', 'remotedir')
2271
def assertLoadHook(self, expected_nb_calls, name, conf_class, *conf_args):
2275
config.OldConfigHooks.install_named_hook('load', hook, None)
2277
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2278
self.assertLength(0, calls)
2280
conf = conf_class(*conf_args)
2281
# Access an option to trigger a load
2282
conf.get_option(name)
2283
self.assertLength(expected_nb_calls, calls)
2284
# Since we can't assert about conf, we just use the number of calls ;-/
2286
def test_load_hook_remote_branch(self):
2287
remote_branch = branch.Branch.open(self.get_url('tree'))
2288
self.assertLoadHook(1, 'file', remote.RemoteBranchConfig, remote_branch)
2290
def test_load_hook_remote_bzrdir(self):
2291
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2292
# The config file doesn't exist, set an option to force its creation
2293
conf = remote_bzrdir._get_config()
2294
conf.set_option('remotedir', 'file')
2295
# We get one call for the server and one call for the client, this is
2296
# caused by the differences in implementations betwen
2297
# SmartServerBzrDirRequestConfigFile (in smart/bzrdir.py) and
2298
# SmartServerBranchGetConfigFile (in smart/branch.py)
2299
self.assertLoadHook(2 ,'file', remote.RemoteBzrDirConfig, remote_bzrdir)
2301
def assertSaveHook(self, conf):
2305
config.OldConfigHooks.install_named_hook('save', hook, None)
2307
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2308
self.assertLength(0, calls)
2309
# Setting an option triggers a save
2310
conf.set_option('foo', 'bar')
2311
self.assertLength(1, calls)
2312
# Since we can't assert about conf, we just use the number of calls ;-/
2314
def test_save_hook_remote_branch(self):
2315
remote_branch = branch.Branch.open(self.get_url('tree'))
2316
self.addCleanup(remote_branch.lock_write().unlock)
2317
self.assertSaveHook(remote_branch._get_config())
2319
def test_save_hook_remote_bzrdir(self):
2320
remote_branch = branch.Branch.open(self.get_url('tree'))
2321
self.addCleanup(remote_branch.lock_write().unlock)
2322
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2323
self.assertSaveHook(remote_bzrdir._get_config())
2326
class TestOption(tests.TestCase):
2328
def test_default_value(self):
2329
opt = config.Option('foo', default='bar')
2330
self.assertEquals('bar', opt.get_default())
2332
def test_callable_default_value(self):
2333
def bar_as_unicode():
2335
opt = config.Option('foo', default=bar_as_unicode)
2336
self.assertEquals('bar', opt.get_default())
2338
def test_default_value_from_env(self):
2339
opt = config.Option('foo', default='bar', default_from_env=['FOO'])
2340
self.overrideEnv('FOO', 'quux')
2341
# Env variable provides a default taking over the option one
2342
self.assertEquals('quux', opt.get_default())
2344
def test_first_default_value_from_env_wins(self):
2345
opt = config.Option('foo', default='bar',
2346
default_from_env=['NO_VALUE', 'FOO', 'BAZ'])
2347
self.overrideEnv('FOO', 'foo')
2348
self.overrideEnv('BAZ', 'baz')
2349
# The first env var set wins
2350
self.assertEquals('foo', opt.get_default())
2352
def test_not_supported_list_default_value(self):
2353
self.assertRaises(AssertionError, config.Option, 'foo', default=[1])
2355
def test_not_supported_object_default_value(self):
2356
self.assertRaises(AssertionError, config.Option, 'foo',
2359
def test_not_supported_callable_default_value_not_unicode(self):
2360
def bar_not_unicode():
2362
opt = config.Option('foo', default=bar_not_unicode)
2363
self.assertRaises(AssertionError, opt.get_default)
2366
class TestOptionConverterMixin(object):
2368
def assertConverted(self, expected, opt, value):
2369
self.assertEquals(expected, opt.convert_from_unicode(value),
2370
'Expecting %s, got %s' % (expected, value,))
2372
def assertWarns(self, opt, value):
2375
warnings.append(args[0] % args[1:])
2376
self.overrideAttr(trace, 'warning', warning)
2377
self.assertEquals(None, opt.convert_from_unicode(value))
2378
self.assertLength(1, warnings)
2380
'Value "%s" is not valid for "%s"' % (value, opt.name),
2383
def assertErrors(self, opt, value):
2384
self.assertRaises(errors.ConfigOptionValueError,
2385
opt.convert_from_unicode, value)
2387
def assertConvertInvalid(self, opt, invalid_value):
2389
self.assertEquals(None, opt.convert_from_unicode(invalid_value),
2390
'%s is not None' % (invalid_value,))
2391
opt.invalid = 'warning'
2392
self.assertWarns(opt, invalid_value)
2393
opt.invalid = 'error'
2394
self.assertErrors(opt, invalid_value)
2397
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2399
def get_option(self):
2400
return config.Option('foo', help='A boolean.',
2401
from_unicode=config.bool_from_store)
2403
def test_convert_invalid(self):
2404
opt = self.get_option()
2405
# A string that is not recognized as a boolean
2406
self.assertConvertInvalid(opt, u'invalid-boolean')
2407
# A list of strings is never recognized as a boolean
2408
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2410
def test_convert_valid(self):
2411
opt = self.get_option()
2412
self.assertConverted(True, opt, u'True')
2413
self.assertConverted(True, opt, u'1')
2414
self.assertConverted(False, opt, u'False')
2417
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2419
def get_option(self):
2420
return config.Option('foo', help='An integer.',
2421
from_unicode=config.int_from_store)
2423
def test_convert_invalid(self):
2424
opt = self.get_option()
2425
# A string that is not recognized as an integer
2426
self.assertConvertInvalid(opt, u'forty-two')
2427
# A list of strings is never recognized as an integer
2428
self.assertConvertInvalid(opt, [u'a', u'list'])
2430
def test_convert_valid(self):
2431
opt = self.get_option()
2432
self.assertConverted(16, opt, u'16')
2435
class TestOptionWithSIUnitConverter(tests.TestCase, TestOptionConverterMixin):
2437
def get_option(self):
2438
return config.Option('foo', help='An integer in SI units.',
2439
from_unicode=config.int_SI_from_store)
2441
def test_convert_invalid(self):
2442
opt = self.get_option()
2443
self.assertConvertInvalid(opt, u'not-a-unit')
2444
self.assertConvertInvalid(opt, u'Gb') # Forgot the int
2445
self.assertConvertInvalid(opt, u'1b') # Forgot the unit
2446
self.assertConvertInvalid(opt, u'1GG')
2447
self.assertConvertInvalid(opt, u'1Mbb')
2448
self.assertConvertInvalid(opt, u'1MM')
2450
def test_convert_valid(self):
2451
opt = self.get_option()
2452
self.assertConverted(int(5e3), opt, u'5kb')
2453
self.assertConverted(int(5e6), opt, u'5M')
2454
self.assertConverted(int(5e6), opt, u'5MB')
2455
self.assertConverted(int(5e9), opt, u'5g')
2456
self.assertConverted(int(5e9), opt, u'5gB')
2457
self.assertConverted(100, opt, u'100')
2460
class TestOptionWithListConverter(tests.TestCase, TestOptionConverterMixin):
2462
def get_option(self):
2463
return config.Option('foo', help='A list.',
2464
from_unicode=config.list_from_store)
2466
def test_convert_invalid(self):
2467
# No string is invalid as all forms can be converted to a list
2470
def test_convert_valid(self):
2471
opt = self.get_option()
2472
# An empty string is an empty list
2473
self.assertConverted([], opt, '') # Using a bare str() just in case
2474
self.assertConverted([], opt, u'')
2476
self.assertConverted([u'True'], opt, u'True')
2478
self.assertConverted([u'42'], opt, u'42')
2480
self.assertConverted([u'bar'], opt, u'bar')
2481
# A list remains a list (configObj will turn a string containing commas
2482
# into a list, but that's not what we're testing here)
2483
self.assertConverted([u'foo', u'1', u'True'],
2484
opt, [u'foo', u'1', u'True'])
2487
class TestOptionConverterMixin(object):
2489
def assertConverted(self, expected, opt, value):
2490
self.assertEquals(expected, opt.convert_from_unicode(value))
2492
def assertWarns(self, opt, value):
2495
warnings.append(args[0] % args[1:])
2496
self.overrideAttr(trace, 'warning', warning)
2497
self.assertEquals(None, opt.convert_from_unicode(value))
2498
self.assertLength(1, warnings)
2500
'Value "%s" is not valid for "%s"' % (value, opt.name),
2503
def assertErrors(self, opt, value):
2504
self.assertRaises(errors.ConfigOptionValueError,
2505
opt.convert_from_unicode, value)
2507
def assertConvertInvalid(self, opt, invalid_value):
2509
self.assertEquals(None, opt.convert_from_unicode(invalid_value))
2510
opt.invalid = 'warning'
2511
self.assertWarns(opt, invalid_value)
2512
opt.invalid = 'error'
2513
self.assertErrors(opt, invalid_value)
2516
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2518
def get_option(self):
2519
return config.Option('foo', help='A boolean.',
2520
from_unicode=config.bool_from_store)
2522
def test_convert_invalid(self):
2523
opt = self.get_option()
2524
# A string that is not recognized as a boolean
2525
self.assertConvertInvalid(opt, u'invalid-boolean')
2526
# A list of strings is never recognized as a boolean
2527
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2529
def test_convert_valid(self):
2530
opt = self.get_option()
2531
self.assertConverted(True, opt, u'True')
2532
self.assertConverted(True, opt, u'1')
2533
self.assertConverted(False, opt, u'False')
2536
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2538
def get_option(self):
2539
return config.Option('foo', help='An integer.',
2540
from_unicode=config.int_from_store)
2542
def test_convert_invalid(self):
2543
opt = self.get_option()
2544
# A string that is not recognized as an integer
2545
self.assertConvertInvalid(opt, u'forty-two')
2546
# A list of strings is never recognized as an integer
2547
self.assertConvertInvalid(opt, [u'a', u'list'])
2549
def test_convert_valid(self):
2550
opt = self.get_option()
2551
self.assertConverted(16, opt, u'16')
2554
class TestOptionWithListConverter(tests.TestCase, TestOptionConverterMixin):
2556
def get_option(self):
2557
return config.Option('foo', help='A list.',
2558
from_unicode=config.list_from_store)
2560
def test_convert_invalid(self):
2561
opt = self.get_option()
2562
# We don't even try to convert a list into a list, we only expect
2564
self.assertConvertInvalid(opt, [1])
2565
# No string is invalid as all forms can be converted to a list
2567
def test_convert_valid(self):
2568
opt = self.get_option()
2569
# An empty string is an empty list
2570
self.assertConverted([], opt, '') # Using a bare str() just in case
2571
self.assertConverted([], opt, u'')
2573
self.assertConverted([u'True'], opt, u'True')
2575
self.assertConverted([u'42'], opt, u'42')
2577
self.assertConverted([u'bar'], opt, u'bar')
2580
class TestOptionRegistry(tests.TestCase):
2583
super(TestOptionRegistry, self).setUp()
2584
# Always start with an empty registry
2585
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2586
self.registry = config.option_registry
2588
def test_register(self):
2589
opt = config.Option('foo')
2590
self.registry.register(opt)
2591
self.assertIs(opt, self.registry.get('foo'))
2593
def test_registered_help(self):
2594
opt = config.Option('foo', help='A simple option')
2595
self.registry.register(opt)
2596
self.assertEquals('A simple option', self.registry.get_help('foo'))
2598
lazy_option = config.Option('lazy_foo', help='Lazy help')
2600
def test_register_lazy(self):
2601
self.registry.register_lazy('lazy_foo', self.__module__,
2602
'TestOptionRegistry.lazy_option')
2603
self.assertIs(self.lazy_option, self.registry.get('lazy_foo'))
2605
def test_registered_lazy_help(self):
2606
self.registry.register_lazy('lazy_foo', self.__module__,
2607
'TestOptionRegistry.lazy_option')
2608
self.assertEquals('Lazy help', self.registry.get_help('lazy_foo'))
2611
class TestRegisteredOptions(tests.TestCase):
2612
"""All registered options should verify some constraints."""
2614
scenarios = [(key, {'option_name': key, 'option': option}) for key, option
2615
in config.option_registry.iteritems()]
2618
super(TestRegisteredOptions, self).setUp()
2619
self.registry = config.option_registry
2621
def test_proper_name(self):
2622
# An option should be registered under its own name, this can't be
2623
# checked at registration time for the lazy ones.
2624
self.assertEquals(self.option_name, self.option.name)
2626
def test_help_is_set(self):
2627
option_help = self.registry.get_help(self.option_name)
2628
self.assertNotEquals(None, option_help)
2629
# Come on, think about the user, he really wants to know what the
2631
self.assertIsNot(None, option_help)
2632
self.assertNotEquals('', option_help)
2635
class TestSection(tests.TestCase):
2637
# FIXME: Parametrize so that all sections produced by Stores run these
2638
# tests -- vila 2011-04-01
2640
def test_get_a_value(self):
2641
a_dict = dict(foo='bar')
2642
section = config.Section('myID', a_dict)
2643
self.assertEquals('bar', section.get('foo'))
2645
def test_get_unknown_option(self):
2647
section = config.Section(None, a_dict)
2648
self.assertEquals('out of thin air',
2649
section.get('foo', 'out of thin air'))
2651
def test_options_is_shared(self):
2653
section = config.Section(None, a_dict)
2654
self.assertIs(a_dict, section.options)
2657
class TestMutableSection(tests.TestCase):
2659
scenarios = [('mutable',
2661
lambda opts: config.MutableSection('myID', opts)},),
2665
a_dict = dict(foo='bar')
2666
section = self.get_section(a_dict)
2667
section.set('foo', 'new_value')
2668
self.assertEquals('new_value', section.get('foo'))
2669
# The change appears in the shared section
2670
self.assertEquals('new_value', a_dict.get('foo'))
2671
# We keep track of the change
2672
self.assertTrue('foo' in section.orig)
2673
self.assertEquals('bar', section.orig.get('foo'))
2675
def test_set_preserve_original_once(self):
2676
a_dict = dict(foo='bar')
2677
section = self.get_section(a_dict)
2678
section.set('foo', 'first_value')
2679
section.set('foo', 'second_value')
2680
# We keep track of the original value
2681
self.assertTrue('foo' in section.orig)
2682
self.assertEquals('bar', section.orig.get('foo'))
2684
def test_remove(self):
2685
a_dict = dict(foo='bar')
2686
section = self.get_section(a_dict)
2687
section.remove('foo')
2688
# We get None for unknown options via the default value
2689
self.assertEquals(None, section.get('foo'))
2690
# Or we just get the default value
2691
self.assertEquals('unknown', section.get('foo', 'unknown'))
2692
self.assertFalse('foo' in section.options)
2693
# We keep track of the deletion
2694
self.assertTrue('foo' in section.orig)
2695
self.assertEquals('bar', section.orig.get('foo'))
2697
def test_remove_new_option(self):
2699
section = self.get_section(a_dict)
2700
section.set('foo', 'bar')
2701
section.remove('foo')
2702
self.assertFalse('foo' in section.options)
2703
# The option didn't exist initially so it we need to keep track of it
2704
# with a special value
2705
self.assertTrue('foo' in section.orig)
2706
self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
2709
class TestCommandLineStore(tests.TestCase):
2712
super(TestCommandLineStore, self).setUp()
2713
self.store = config.CommandLineStore()
2715
def get_section(self):
2716
"""Get the unique section for the command line overrides."""
2717
sections = list(self.store.get_sections())
2718
self.assertLength(1, sections)
2719
store, section = sections[0]
2720
self.assertEquals(self.store, store)
2723
def test_no_override(self):
2724
self.store._from_cmdline([])
2725
section = self.get_section()
2726
self.assertLength(0, list(section.iter_option_names()))
2728
def test_simple_override(self):
2729
self.store._from_cmdline(['a=b'])
2730
section = self.get_section()
2731
self.assertEqual('b', section.get('a'))
2733
def test_list_override(self):
2734
self.store._from_cmdline(['l=1,2,3'])
2735
val = self.get_section().get('l')
2736
self.assertEqual('1,2,3', val)
2737
# Reminder: lists should be registered as such explicitely, otherwise
2738
# the conversion needs to be done afterwards.
2739
self.assertEqual(['1', '2', '3'], config.list_from_store(val))
2741
def test_multiple_overrides(self):
2742
self.store._from_cmdline(['a=b', 'x=y'])
2743
section = self.get_section()
2744
self.assertEquals('b', section.get('a'))
2745
self.assertEquals('y', section.get('x'))
2747
def test_wrong_syntax(self):
2748
self.assertRaises(errors.BzrCommandError,
2749
self.store._from_cmdline, ['a=b', 'c'])
2752
class TestStore(tests.TestCaseWithTransport):
2754
def assertSectionContent(self, expected, (store, section)):
2755
"""Assert that some options have the proper values in a section."""
2756
expected_name, expected_options = expected
2757
self.assertEquals(expected_name, section.id)
2760
dict([(k, section.get(k)) for k in expected_options.keys()]))
2763
class TestReadonlyStore(TestStore):
2765
scenarios = [(key, {'get_store': builder}) for key, builder
2766
in config.test_store_builder_registry.iteritems()]
2768
def test_building_delays_load(self):
2769
store = self.get_store(self)
2770
self.assertEquals(False, store.is_loaded())
2771
store._load_from_string('')
2772
self.assertEquals(True, store.is_loaded())
2774
def test_get_no_sections_for_empty(self):
2775
store = self.get_store(self)
2776
store._load_from_string('')
2777
self.assertEquals([], list(store.get_sections()))
2779
def test_get_default_section(self):
2780
store = self.get_store(self)
2781
store._load_from_string('foo=bar')
2782
sections = list(store.get_sections())
2783
self.assertLength(1, sections)
2784
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2786
def test_get_named_section(self):
2787
store = self.get_store(self)
2788
store._load_from_string('[baz]\nfoo=bar')
2789
sections = list(store.get_sections())
2790
self.assertLength(1, sections)
2791
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2793
def test_load_from_string_fails_for_non_empty_store(self):
2794
store = self.get_store(self)
2795
store._load_from_string('foo=bar')
2796
self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2799
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2800
"""Simulate loading a config store with content of various encodings.
2802
All files produced by bzr are in utf8 content.
2804
Users may modify them manually and end up with a file that can't be
2805
loaded. We need to issue proper error messages in this case.
2808
invalid_utf8_char = '\xff'
2810
def test_load_utf8(self):
2811
"""Ensure we can load an utf8-encoded file."""
2812
t = self.get_transport()
2813
# From http://pad.lv/799212
2814
unicode_user = u'b\N{Euro Sign}ar'
2815
unicode_content = u'user=%s' % (unicode_user,)
2816
utf8_content = unicode_content.encode('utf8')
2817
# Store the raw content in the config file
2818
t.put_bytes('foo.conf', utf8_content)
2819
store = config.TransportIniFileStore(t, 'foo.conf')
2821
stack = config.Stack([store.get_sections], store)
2822
self.assertEquals(unicode_user, stack.get('user'))
2824
def test_load_non_ascii(self):
2825
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2826
t = self.get_transport()
2827
t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2828
store = config.TransportIniFileStore(t, 'foo.conf')
2829
self.assertRaises(errors.ConfigContentError, store.load)
2831
def test_load_erroneous_content(self):
2832
"""Ensure we display a proper error on content that can't be parsed."""
2833
t = self.get_transport()
2834
t.put_bytes('foo.conf', '[open_section\n')
2835
store = config.TransportIniFileStore(t, 'foo.conf')
2836
self.assertRaises(errors.ParseConfigError, store.load)
2838
def test_load_permission_denied(self):
2839
"""Ensure we get warned when trying to load an inaccessible file."""
2842
warnings.append(args[0] % args[1:])
2843
self.overrideAttr(trace, 'warning', warning)
2845
t = self.get_transport()
2847
def get_bytes(relpath):
2848
raise errors.PermissionDenied(relpath, "")
2849
t.get_bytes = get_bytes
2850
store = config.TransportIniFileStore(t, 'foo.conf')
2851
self.assertRaises(errors.PermissionDenied, store.load)
2854
[u'Permission denied while trying to load configuration store %s.'
2855
% store.external_url()])
2858
class TestIniConfigContent(tests.TestCaseWithTransport):
2859
"""Simulate loading a IniBasedConfig with content of various encodings.
2861
All files produced by bzr are in utf8 content.
2863
Users may modify them manually and end up with a file that can't be
2864
loaded. We need to issue proper error messages in this case.
2867
invalid_utf8_char = '\xff'
2869
def test_load_utf8(self):
2870
"""Ensure we can load an utf8-encoded file."""
2871
# From http://pad.lv/799212
2872
unicode_user = u'b\N{Euro Sign}ar'
2873
unicode_content = u'user=%s' % (unicode_user,)
2874
utf8_content = unicode_content.encode('utf8')
2875
# Store the raw content in the config file
2876
with open('foo.conf', 'wb') as f:
2877
f.write(utf8_content)
2878
conf = config.IniBasedConfig(file_name='foo.conf')
2879
self.assertEquals(unicode_user, conf.get_user_option('user'))
2881
def test_load_badly_encoded_content(self):
2882
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2883
with open('foo.conf', 'wb') as f:
2884
f.write('user=foo\n#%s\n' % (self.invalid_utf8_char,))
2885
conf = config.IniBasedConfig(file_name='foo.conf')
2886
self.assertRaises(errors.ConfigContentError, conf._get_parser)
2888
def test_load_erroneous_content(self):
2889
"""Ensure we display a proper error on content that can't be parsed."""
2890
with open('foo.conf', 'wb') as f:
2891
f.write('[open_section\n')
2892
conf = config.IniBasedConfig(file_name='foo.conf')
2893
self.assertRaises(errors.ParseConfigError, conf._get_parser)
2896
class TestMutableStore(TestStore):
2898
scenarios = [(key, {'store_id': key, 'get_store': builder}) for key, builder
2899
in config.test_store_builder_registry.iteritems()]
2902
super(TestMutableStore, self).setUp()
2903
self.transport = self.get_transport()
2905
def has_store(self, store):
2906
store_basename = urlutils.relative_url(self.transport.external_url(),
2907
store.external_url())
2908
return self.transport.has(store_basename)
2910
def test_save_empty_creates_no_file(self):
2911
# FIXME: There should be a better way than relying on the test
2912
# parametrization to identify branch.conf -- vila 2011-0526
2913
if self.store_id in ('branch', 'remote_branch'):
2914
raise tests.TestNotApplicable(
2915
'branch.conf is *always* created when a branch is initialized')
2916
store = self.get_store(self)
2918
self.assertEquals(False, self.has_store(store))
2920
def test_save_emptied_succeeds(self):
2921
store = self.get_store(self)
2922
store._load_from_string('foo=bar\n')
2923
section = store.get_mutable_section(None)
2924
section.remove('foo')
2926
self.assertEquals(True, self.has_store(store))
2927
modified_store = self.get_store(self)
2928
sections = list(modified_store.get_sections())
2929
self.assertLength(0, sections)
2931
def test_save_with_content_succeeds(self):
2932
# FIXME: There should be a better way than relying on the test
2933
# parametrization to identify branch.conf -- vila 2011-0526
2934
if self.store_id in ('branch', 'remote_branch'):
2935
raise tests.TestNotApplicable(
2936
'branch.conf is *always* created when a branch is initialized')
2937
store = self.get_store(self)
2938
store._load_from_string('foo=bar\n')
2939
self.assertEquals(False, self.has_store(store))
2941
self.assertEquals(True, self.has_store(store))
2942
modified_store = self.get_store(self)
2943
sections = list(modified_store.get_sections())
2944
self.assertLength(1, sections)
2945
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2947
def test_set_option_in_empty_store(self):
2948
store = self.get_store(self)
2949
section = store.get_mutable_section(None)
2950
section.set('foo', 'bar')
2952
modified_store = self.get_store(self)
2953
sections = list(modified_store.get_sections())
2954
self.assertLength(1, sections)
2955
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2957
def test_set_option_in_default_section(self):
2958
store = self.get_store(self)
2959
store._load_from_string('')
2960
section = store.get_mutable_section(None)
2961
section.set('foo', 'bar')
2963
modified_store = self.get_store(self)
2964
sections = list(modified_store.get_sections())
2965
self.assertLength(1, sections)
2966
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2968
def test_set_option_in_named_section(self):
2969
store = self.get_store(self)
2970
store._load_from_string('')
2971
section = store.get_mutable_section('baz')
2972
section.set('foo', 'bar')
2974
modified_store = self.get_store(self)
2975
sections = list(modified_store.get_sections())
2976
self.assertLength(1, sections)
2977
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2979
def test_load_hook(self):
2980
# We first needs to ensure that the store exists
2981
store = self.get_store(self)
2982
section = store.get_mutable_section('baz')
2983
section.set('foo', 'bar')
2985
# Now we can try to load it
2986
store = self.get_store(self)
2990
config.ConfigHooks.install_named_hook('load', hook, None)
2991
self.assertLength(0, calls)
2993
self.assertLength(1, calls)
2994
self.assertEquals((store,), calls[0])
2996
def test_save_hook(self):
3000
config.ConfigHooks.install_named_hook('save', hook, None)
3001
self.assertLength(0, calls)
3002
store = self.get_store(self)
3003
section = store.get_mutable_section('baz')
3004
section.set('foo', 'bar')
3006
self.assertLength(1, calls)
3007
self.assertEquals((store,), calls[0])
3010
class TestTransportIniFileStore(TestStore):
3012
def test_loading_unknown_file_fails(self):
3013
store = config.TransportIniFileStore(self.get_transport(),
3015
self.assertRaises(errors.NoSuchFile, store.load)
3017
def test_invalid_content(self):
3018
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3019
self.assertEquals(False, store.is_loaded())
3020
exc = self.assertRaises(
3021
errors.ParseConfigError, store._load_from_string,
3022
'this is invalid !')
3023
self.assertEndsWith(exc.filename, 'foo.conf')
3024
# And the load failed
3025
self.assertEquals(False, store.is_loaded())
3027
def test_get_embedded_sections(self):
3028
# A more complicated example (which also shows that section names and
3029
# option names share the same name space...)
3030
# FIXME: This should be fixed by forbidding dicts as values ?
3031
# -- vila 2011-04-05
3032
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3033
store._load_from_string('''
3037
foo_in_DEFAULT=foo_DEFAULT
3045
sections = list(store.get_sections())
3046
self.assertLength(4, sections)
3047
# The default section has no name.
3048
# List values are provided as strings and need to be explicitly
3049
# converted by specifying from_unicode=list_from_store at option
3051
self.assertSectionContent((None, {'foo': 'bar', 'l': u'1,2'}),
3053
self.assertSectionContent(
3054
('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
3055
self.assertSectionContent(
3056
('bar', {'foo_in_bar': 'barbar'}), sections[2])
3057
# sub sections are provided as embedded dicts.
3058
self.assertSectionContent(
3059
('baz', {'foo_in_baz': 'barbaz', 'qux': {'foo_in_qux': 'quux'}}),
3063
class TestLockableIniFileStore(TestStore):
3065
def test_create_store_in_created_dir(self):
3066
self.assertPathDoesNotExist('dir')
3067
t = self.get_transport('dir/subdir')
3068
store = config.LockableIniFileStore(t, 'foo.conf')
3069
store.get_mutable_section(None).set('foo', 'bar')
3071
self.assertPathExists('dir/subdir')
3074
class TestConcurrentStoreUpdates(TestStore):
3075
"""Test that Stores properly handle conccurent updates.
3077
New Store implementation may fail some of these tests but until such
3078
implementations exist it's hard to properly filter them from the scenarios
3079
applied here. If you encounter such a case, contact the bzr devs.
3082
scenarios = [(key, {'get_stack': builder}) for key, builder
3083
in config.test_stack_builder_registry.iteritems()]
3086
super(TestConcurrentStoreUpdates, self).setUp()
3087
self.stack = self.get_stack(self)
3088
if not isinstance(self.stack, config._CompatibleStack):
3089
raise tests.TestNotApplicable(
3090
'%s is not meant to be compatible with the old config design'
3092
self.stack.set('one', '1')
3093
self.stack.set('two', '2')
3095
self.stack.store.save()
3097
def test_simple_read_access(self):
3098
self.assertEquals('1', self.stack.get('one'))
3100
def test_simple_write_access(self):
3101
self.stack.set('one', 'one')
3102
self.assertEquals('one', self.stack.get('one'))
3104
def test_listen_to_the_last_speaker(self):
3106
c2 = self.get_stack(self)
3107
c1.set('one', 'ONE')
3108
c2.set('two', 'TWO')
3109
self.assertEquals('ONE', c1.get('one'))
3110
self.assertEquals('TWO', c2.get('two'))
3111
# The second update respect the first one
3112
self.assertEquals('ONE', c2.get('one'))
3114
def test_last_speaker_wins(self):
3115
# If the same config is not shared, the same variable modified twice
3116
# can only see a single result.
3118
c2 = self.get_stack(self)
3121
self.assertEquals('c2', c2.get('one'))
3122
# The first modification is still available until another refresh
3124
self.assertEquals('c1', c1.get('one'))
3125
c1.set('two', 'done')
3126
self.assertEquals('c2', c1.get('one'))
3128
def test_writes_are_serialized(self):
3130
c2 = self.get_stack(self)
3132
# We spawn a thread that will pause *during* the config saving.
3133
before_writing = threading.Event()
3134
after_writing = threading.Event()
3135
writing_done = threading.Event()
3136
c1_save_without_locking_orig = c1.store.save_without_locking
3137
def c1_save_without_locking():
3138
before_writing.set()
3139
c1_save_without_locking_orig()
3140
# The lock is held. We wait for the main thread to decide when to
3142
after_writing.wait()
3143
c1.store.save_without_locking = c1_save_without_locking
3147
t1 = threading.Thread(target=c1_set)
3148
# Collect the thread after the test
3149
self.addCleanup(t1.join)
3150
# Be ready to unblock the thread if the test goes wrong
3151
self.addCleanup(after_writing.set)
3153
before_writing.wait()
3154
self.assertRaises(errors.LockContention,
3155
c2.set, 'one', 'c2')
3156
self.assertEquals('c1', c1.get('one'))
3157
# Let the lock be released
3161
self.assertEquals('c2', c2.get('one'))
3163
def test_read_while_writing(self):
3165
# We spawn a thread that will pause *during* the write
3166
ready_to_write = threading.Event()
3167
do_writing = threading.Event()
3168
writing_done = threading.Event()
3169
# We override the _save implementation so we know the store is locked
3170
c1_save_without_locking_orig = c1.store.save_without_locking
3171
def c1_save_without_locking():
3172
ready_to_write.set()
3173
# The lock is held. We wait for the main thread to decide when to
3176
c1_save_without_locking_orig()
3178
c1.store.save_without_locking = c1_save_without_locking
3181
t1 = threading.Thread(target=c1_set)
3182
# Collect the thread after the test
3183
self.addCleanup(t1.join)
3184
# Be ready to unblock the thread if the test goes wrong
3185
self.addCleanup(do_writing.set)
3187
# Ensure the thread is ready to write
3188
ready_to_write.wait()
3189
self.assertEquals('c1', c1.get('one'))
3190
# If we read during the write, we get the old value
3191
c2 = self.get_stack(self)
3192
self.assertEquals('1', c2.get('one'))
3193
# Let the writing occur and ensure it occurred
3196
# Now we get the updated value
3197
c3 = self.get_stack(self)
3198
self.assertEquals('c1', c3.get('one'))
3200
# FIXME: It may be worth looking into removing the lock dir when it's not
3201
# needed anymore and look at possible fallouts for concurrent lockers. This
3202
# will matter if/when we use config files outside of bazaar directories
3203
# (.bazaar or .bzr) -- vila 20110-04-111
3206
class TestSectionMatcher(TestStore):
3208
scenarios = [('location', {'matcher': config.LocationMatcher}),
3209
('id', {'matcher': config.NameMatcher}),]
3212
super(TestSectionMatcher, self).setUp()
3213
# Any simple store is good enough
3214
self.get_store = config.test_store_builder_registry.get('configobj')
3216
def test_no_matches_for_empty_stores(self):
3217
store = self.get_store(self)
3218
store._load_from_string('')
3219
matcher = self.matcher(store, '/bar')
3220
self.assertEquals([], list(matcher.get_sections()))
3222
def test_build_doesnt_load_store(self):
3223
store = self.get_store(self)
3224
matcher = self.matcher(store, '/bar')
3225
self.assertFalse(store.is_loaded())
3228
class TestLocationSection(tests.TestCase):
3230
def get_section(self, options, extra_path):
3231
section = config.Section('foo', options)
3232
# We don't care about the length so we use '0'
3233
return config.LocationSection(section, 0, extra_path)
3235
def test_simple_option(self):
3236
section = self.get_section({'foo': 'bar'}, '')
3237
self.assertEquals('bar', section.get('foo'))
3239
def test_option_with_extra_path(self):
3240
section = self.get_section({'foo': 'bar', 'foo:policy': 'appendpath'},
3242
self.assertEquals('bar/baz', section.get('foo'))
3244
def test_invalid_policy(self):
3245
section = self.get_section({'foo': 'bar', 'foo:policy': 'die'},
3247
# invalid policies are ignored
3248
self.assertEquals('bar', section.get('foo'))
3251
class TestLocationMatcher(TestStore):
3254
super(TestLocationMatcher, self).setUp()
3255
# Any simple store is good enough
3256
self.get_store = config.test_store_builder_registry.get('configobj')
3258
def test_unrelated_section_excluded(self):
3259
store = self.get_store(self)
3260
store._load_from_string('''
3268
section=/foo/bar/baz
3272
self.assertEquals(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
3274
[section.id for _, section in store.get_sections()])
3275
matcher = config.LocationMatcher(store, '/foo/bar/quux')
3276
sections = [section for s, section in matcher.get_sections()]
3277
self.assertEquals([3, 2],
3278
[section.length for section in sections])
3279
self.assertEquals(['/foo/bar', '/foo'],
3280
[section.id for section in sections])
3281
self.assertEquals(['quux', 'bar/quux'],
3282
[section.extra_path for section in sections])
3284
def test_more_specific_sections_first(self):
3285
store = self.get_store(self)
3286
store._load_from_string('''
3292
self.assertEquals(['/foo', '/foo/bar'],
3293
[section.id for _, section in store.get_sections()])
3294
matcher = config.LocationMatcher(store, '/foo/bar/baz')
3295
sections = [section for s, section in matcher.get_sections()]
3296
self.assertEquals([3, 2],
3297
[section.length for section in sections])
3298
self.assertEquals(['/foo/bar', '/foo'],
3299
[section.id for section in sections])
3300
self.assertEquals(['baz', 'bar/baz'],
3301
[section.extra_path for section in sections])
3303
def test_appendpath_in_no_name_section(self):
3304
# It's a bit weird to allow appendpath in a no-name section, but
3305
# someone may found a use for it
3306
store = self.get_store(self)
3307
store._load_from_string('''
3309
foo:policy = appendpath
3311
matcher = config.LocationMatcher(store, 'dir/subdir')
3312
sections = list(matcher.get_sections())
3313
self.assertLength(1, sections)
3314
self.assertEquals('bar/dir/subdir', sections[0][1].get('foo'))
3316
def test_file_urls_are_normalized(self):
3317
store = self.get_store(self)
3318
if sys.platform == 'win32':
3319
expected_url = 'file:///C:/dir/subdir'
3320
expected_location = 'C:/dir/subdir'
3322
expected_url = 'file:///dir/subdir'
3323
expected_location = '/dir/subdir'
3324
matcher = config.LocationMatcher(store, expected_url)
3325
self.assertEquals(expected_location, matcher.location)
3328
class TestNameMatcher(TestStore):
3331
super(TestNameMatcher, self).setUp()
3332
self.matcher = config.NameMatcher
3333
# Any simple store is good enough
3334
self.get_store = config.test_store_builder_registry.get('configobj')
3336
def get_matching_sections(self, name):
3337
store = self.get_store(self)
3338
store._load_from_string('''
3346
matcher = self.matcher(store, name)
3347
return list(matcher.get_sections())
3349
def test_matching(self):
3350
sections = self.get_matching_sections('foo')
3351
self.assertLength(1, sections)
3352
self.assertSectionContent(('foo', {'option': 'foo'}), sections[0])
3354
def test_not_matching(self):
3355
sections = self.get_matching_sections('baz')
3356
self.assertLength(0, sections)
3359
class TestStackGet(tests.TestCase):
3361
# FIXME: This should be parametrized for all known Stack or dedicated
3362
# paramerized tests created to avoid bloating -- vila 2011-03-31
3364
def overrideOptionRegistry(self):
3365
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3367
def test_single_config_get(self):
3368
conf = dict(foo='bar')
3369
conf_stack = config.Stack([conf])
3370
self.assertEquals('bar', conf_stack.get('foo'))
3372
def test_get_with_registered_default_value(self):
3373
conf_stack = config.Stack([dict()])
3374
opt = config.Option('foo', default='bar')
3375
self.overrideOptionRegistry()
3376
config.option_registry.register('foo', opt)
3377
self.assertEquals('bar', conf_stack.get('foo'))
3379
def test_get_without_registered_default_value(self):
3380
conf_stack = config.Stack([dict()])
3381
opt = config.Option('foo')
3382
self.overrideOptionRegistry()
3383
config.option_registry.register('foo', opt)
3384
self.assertEquals(None, conf_stack.get('foo'))
3386
def test_get_without_default_value_for_not_registered(self):
3387
conf_stack = config.Stack([dict()])
3388
opt = config.Option('foo')
3389
self.overrideOptionRegistry()
3390
self.assertEquals(None, conf_stack.get('foo'))
3392
def test_get_first_definition(self):
3393
conf1 = dict(foo='bar')
3394
conf2 = dict(foo='baz')
3395
conf_stack = config.Stack([conf1, conf2])
3396
self.assertEquals('bar', conf_stack.get('foo'))
3398
def test_get_embedded_definition(self):
3399
conf1 = dict(yy='12')
3400
conf2 = config.Stack([dict(xx='42'), dict(foo='baz')])
3401
conf_stack = config.Stack([conf1, conf2])
3402
self.assertEquals('baz', conf_stack.get('foo'))
3404
def test_get_for_empty_section_callable(self):
3405
conf_stack = config.Stack([lambda : []])
3406
self.assertEquals(None, conf_stack.get('foo'))
3408
def test_get_for_broken_callable(self):
3409
# Trying to use and invalid callable raises an exception on first use
3410
conf_stack = config.Stack([lambda : object()])
3411
self.assertRaises(TypeError, conf_stack.get, 'foo')
3414
class TestStackWithTransport(tests.TestCaseWithTransport):
3416
scenarios = [(key, {'get_stack': builder}) for key, builder
3417
in config.test_stack_builder_registry.iteritems()]
3420
class TestConcreteStacks(TestStackWithTransport):
3422
def test_build_stack(self):
3423
# Just a smoke test to help debug builders
3424
stack = self.get_stack(self)
3427
class TestStackGet(TestStackWithTransport):
3430
super(TestStackGet, self).setUp()
3431
self.conf = self.get_stack(self)
3433
def test_get_for_empty_stack(self):
3434
self.assertEquals(None, self.conf.get('foo'))
3436
def test_get_hook(self):
3437
self.conf.set('foo', 'bar')
3441
config.ConfigHooks.install_named_hook('get', hook, None)
3442
self.assertLength(0, calls)
3443
value = self.conf.get('foo')
3444
self.assertEquals('bar', value)
3445
self.assertLength(1, calls)
3446
self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
3449
class TestStackGetWithConverter(tests.TestCaseWithTransport):
3452
super(TestStackGetWithConverter, self).setUp()
3453
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3454
self.registry = config.option_registry
3455
# We just want a simple stack with a simple store so we can inject
3456
# whatever content the tests need without caring about what section
3457
# names are valid for a given store/stack.
3458
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3459
self.conf = config.Stack([store.get_sections], store)
3461
def register_bool_option(self, name, default=None, default_from_env=None):
3462
b = config.Option(name, help='A boolean.',
3463
default=default, default_from_env=default_from_env,
3464
from_unicode=config.bool_from_store)
3465
self.registry.register(b)
3467
def test_get_default_bool_None(self):
3468
self.register_bool_option('foo')
3469
self.assertEquals(None, self.conf.get('foo'))
3471
def test_get_default_bool_True(self):
3472
self.register_bool_option('foo', u'True')
3473
self.assertEquals(True, self.conf.get('foo'))
3475
def test_get_default_bool_False(self):
3476
self.register_bool_option('foo', False)
3477
self.assertEquals(False, self.conf.get('foo'))
3479
def test_get_default_bool_False_as_string(self):
3480
self.register_bool_option('foo', u'False')
3481
self.assertEquals(False, self.conf.get('foo'))
3483
def test_get_default_bool_from_env_converted(self):
3484
self.register_bool_option('foo', u'True', default_from_env=['FOO'])
3485
self.overrideEnv('FOO', 'False')
3486
self.assertEquals(False, self.conf.get('foo'))
3488
def test_get_default_bool_when_conversion_fails(self):
3489
self.register_bool_option('foo', default='True')
3490
self.conf.store._load_from_string('foo=invalid boolean')
3491
self.assertEquals(True, self.conf.get('foo'))
3493
def register_integer_option(self, name,
3494
default=None, default_from_env=None):
3495
i = config.Option(name, help='An integer.',
3496
default=default, default_from_env=default_from_env,
3497
from_unicode=config.int_from_store)
3498
self.registry.register(i)
3500
def test_get_default_integer_None(self):
3501
self.register_integer_option('foo')
3502
self.assertEquals(None, self.conf.get('foo'))
3504
def test_get_default_integer(self):
3505
self.register_integer_option('foo', 42)
3506
self.assertEquals(42, self.conf.get('foo'))
3508
def test_get_default_integer_as_string(self):
3509
self.register_integer_option('foo', u'42')
3510
self.assertEquals(42, self.conf.get('foo'))
3512
def test_get_default_integer_from_env(self):
3513
self.register_integer_option('foo', default_from_env=['FOO'])
3514
self.overrideEnv('FOO', '18')
3515
self.assertEquals(18, self.conf.get('foo'))
3517
def test_get_default_integer_when_conversion_fails(self):
3518
self.register_integer_option('foo', default='12')
3519
self.conf.store._load_from_string('foo=invalid integer')
3520
self.assertEquals(12, self.conf.get('foo'))
3522
def register_list_option(self, name, default=None, default_from_env=None):
3523
l = config.Option(name, help='A list.',
3524
default=default, default_from_env=default_from_env,
3525
from_unicode=config.list_from_store)
3526
self.registry.register(l)
3528
def test_get_default_list_None(self):
3529
self.register_list_option('foo')
3530
self.assertEquals(None, self.conf.get('foo'))
3532
def test_get_default_list_empty(self):
3533
self.register_list_option('foo', '')
3534
self.assertEquals([], self.conf.get('foo'))
3536
def test_get_default_list_from_env(self):
3537
self.register_list_option('foo', default_from_env=['FOO'])
3538
self.overrideEnv('FOO', '')
3539
self.assertEquals([], self.conf.get('foo'))
3541
def test_get_with_list_converter_no_item(self):
3542
self.register_list_option('foo', None)
3543
self.conf.store._load_from_string('foo=,')
3544
self.assertEquals([], self.conf.get('foo'))
3546
def test_get_with_list_converter_many_items(self):
3547
self.register_list_option('foo', None)
3548
self.conf.store._load_from_string('foo=m,o,r,e')
3549
self.assertEquals(['m', 'o', 'r', 'e'], self.conf.get('foo'))
3551
def test_get_with_list_converter_embedded_spaces_many_items(self):
3552
self.register_list_option('foo', None)
3553
self.conf.store._load_from_string('foo=" bar", "baz "')
3554
self.assertEquals([' bar', 'baz '], self.conf.get('foo'))
3556
def test_get_with_list_converter_stripped_spaces_many_items(self):
3557
self.register_list_option('foo', None)
3558
self.conf.store._load_from_string('foo= bar , baz ')
3559
self.assertEquals(['bar', 'baz'], self.conf.get('foo'))
3562
class TestIterOptionRefs(tests.TestCase):
3563
"""iter_option_refs is a bit unusual, document some cases."""
3565
def assertRefs(self, expected, string):
3566
self.assertEquals(expected, list(config.iter_option_refs(string)))
3568
def test_empty(self):
3569
self.assertRefs([(False, '')], '')
3571
def test_no_refs(self):
3572
self.assertRefs([(False, 'foo bar')], 'foo bar')
3574
def test_single_ref(self):
3575
self.assertRefs([(False, ''), (True, '{foo}'), (False, '')], '{foo}')
3577
def test_broken_ref(self):
3578
self.assertRefs([(False, '{foo')], '{foo')
3580
def test_embedded_ref(self):
3581
self.assertRefs([(False, '{'), (True, '{foo}'), (False, '}')],
3584
def test_two_refs(self):
3585
self.assertRefs([(False, ''), (True, '{foo}'),
3586
(False, ''), (True, '{bar}'),
3590
def test_newline_in_refs_are_not_matched(self):
3591
self.assertRefs([(False, '{\nxx}{xx\n}{{\n}}')], '{\nxx}{xx\n}{{\n}}')
3594
class TestStackExpandOptions(tests.TestCaseWithTransport):
3597
super(TestStackExpandOptions, self).setUp()
3598
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3599
self.registry = config.option_registry
3600
self.conf = build_branch_stack(self)
3602
def assertExpansion(self, expected, string, env=None):
3603
self.assertEquals(expected, self.conf.expand_options(string, env))
3605
def test_no_expansion(self):
3606
self.assertExpansion('foo', 'foo')
3608
def test_expand_default_value(self):
3609
self.conf.store._load_from_string('bar=baz')
3610
self.registry.register(config.Option('foo', default=u'{bar}'))
3611
self.assertEquals('baz', self.conf.get('foo', expand=True))
3613
def test_expand_default_from_env(self):
3614
self.conf.store._load_from_string('bar=baz')
3615
self.registry.register(config.Option('foo', default_from_env=['FOO']))
3616
self.overrideEnv('FOO', '{bar}')
3617
self.assertEquals('baz', self.conf.get('foo', expand=True))
3619
def test_expand_default_on_failed_conversion(self):
3620
self.conf.store._load_from_string('baz=bogus\nbar=42\nfoo={baz}')
3621
self.registry.register(
3622
config.Option('foo', default=u'{bar}',
3623
from_unicode=config.int_from_store))
3624
self.assertEquals(42, self.conf.get('foo', expand=True))
3626
def test_env_adding_options(self):
3627
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3629
def test_env_overriding_options(self):
3630
self.conf.store._load_from_string('foo=baz')
3631
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3633
def test_simple_ref(self):
3634
self.conf.store._load_from_string('foo=xxx')
3635
self.assertExpansion('xxx', '{foo}')
3637
def test_unknown_ref(self):
3638
self.assertRaises(errors.ExpandingUnknownOption,
3639
self.conf.expand_options, '{foo}')
3641
def test_indirect_ref(self):
3642
self.conf.store._load_from_string('''
3646
self.assertExpansion('xxx', '{bar}')
3648
def test_embedded_ref(self):
3649
self.conf.store._load_from_string('''
3653
self.assertExpansion('xxx', '{{bar}}')
3655
def test_simple_loop(self):
3656
self.conf.store._load_from_string('foo={foo}')
3657
self.assertRaises(errors.OptionExpansionLoop,
3658
self.conf.expand_options, '{foo}')
3660
def test_indirect_loop(self):
3661
self.conf.store._load_from_string('''
3665
e = self.assertRaises(errors.OptionExpansionLoop,
3666
self.conf.expand_options, '{foo}')
3667
self.assertEquals('foo->bar->baz', e.refs)
3668
self.assertEquals('{foo}', e.string)
3670
def test_list(self):
3671
self.conf.store._load_from_string('''
3675
list={foo},{bar},{baz}
3677
self.registry.register(
3678
config.Option('list', from_unicode=config.list_from_store))
3679
self.assertEquals(['start', 'middle', 'end'],
3680
self.conf.get('list', expand=True))
3682
def test_cascading_list(self):
3683
self.conf.store._load_from_string('''
3689
self.registry.register(
3690
config.Option('list', from_unicode=config.list_from_store))
3691
self.assertEquals(['start', 'middle', 'end'],
3692
self.conf.get('list', expand=True))
3694
def test_pathologically_hidden_list(self):
3695
self.conf.store._load_from_string('''
3701
hidden={start}{middle}{end}
3703
# What matters is what the registration says, the conversion happens
3704
# only after all expansions have been performed
3705
self.registry.register(
3706
config.Option('hidden', from_unicode=config.list_from_store))
3707
self.assertEquals(['bin', 'go'],
3708
self.conf.get('hidden', expand=True))
3711
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
3714
super(TestStackCrossSectionsExpand, self).setUp()
3716
def get_config(self, location, string):
3719
# Since we don't save the config we won't strictly require to inherit
3720
# from TestCaseInTempDir, but an error occurs so quickly...
3721
c = config.LocationStack(location)
3722
c.store._load_from_string(string)
3725
def test_dont_cross_unrelated_section(self):
3726
c = self.get_config('/another/branch/path','''
3731
[/another/branch/path]
3734
self.assertRaises(errors.ExpandingUnknownOption,
3735
c.get, 'bar', expand=True)
3737
def test_cross_related_sections(self):
3738
c = self.get_config('/project/branch/path','''
3742
[/project/branch/path]
3745
self.assertEquals('quux', c.get('bar', expand=True))
3748
class TestStackCrossStoresExpand(tests.TestCaseWithTransport):
3750
def test_cross_global_locations(self):
3751
l_store = config.LocationStore()
3752
l_store._load_from_string('''
3758
g_store = config.GlobalStore()
3759
g_store._load_from_string('''
3765
stack = config.LocationStack('/branch')
3766
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
3767
self.assertEquals('loc-foo', stack.get('gfoo', expand=True))
3770
class TestStackExpandSectionLocals(tests.TestCaseWithTransport):
3772
def test_expand_locals_empty(self):
3773
l_store = config.LocationStore()
3774
l_store._load_from_string('''
3775
[/home/user/project]
3780
stack = config.LocationStack('/home/user/project/')
3781
self.assertEquals('', stack.get('base', expand=True))
3782
self.assertEquals('', stack.get('rel', expand=True))
3784
def test_expand_basename_locally(self):
3785
l_store = config.LocationStore()
3786
l_store._load_from_string('''
3787
[/home/user/project]
3791
stack = config.LocationStack('/home/user/project/branch')
3792
self.assertEquals('branch', stack.get('bfoo', expand=True))
3794
def test_expand_basename_locally_longer_path(self):
3795
l_store = config.LocationStore()
3796
l_store._load_from_string('''
3801
stack = config.LocationStack('/home/user/project/dir/branch')
3802
self.assertEquals('branch', stack.get('bfoo', expand=True))
3804
def test_expand_relpath_locally(self):
3805
l_store = config.LocationStore()
3806
l_store._load_from_string('''
3807
[/home/user/project]
3808
lfoo = loc-foo/{relpath}
3811
stack = config.LocationStack('/home/user/project/branch')
3812
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
3814
def test_expand_relpath_unknonw_in_global(self):
3815
g_store = config.GlobalStore()
3816
g_store._load_from_string('''
3821
stack = config.LocationStack('/home/user/project/branch')
3822
self.assertRaises(errors.ExpandingUnknownOption,
3823
stack.get, 'gfoo', expand=True)
3825
def test_expand_local_option_locally(self):
3826
l_store = config.LocationStore()
3827
l_store._load_from_string('''
3828
[/home/user/project]
3829
lfoo = loc-foo/{relpath}
3833
g_store = config.GlobalStore()
3834
g_store._load_from_string('''
3840
stack = config.LocationStack('/home/user/project/branch')
3841
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
3842
self.assertEquals('loc-foo/branch', stack.get('gfoo', expand=True))
3844
def test_locals_dont_leak(self):
3845
"""Make sure we chose the right local in presence of several sections.
3847
l_store = config.LocationStore()
3848
l_store._load_from_string('''
3850
lfoo = loc-foo/{relpath}
3851
[/home/user/project]
3852
lfoo = loc-foo/{relpath}
3855
stack = config.LocationStack('/home/user/project/branch')
3856
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
3857
stack = config.LocationStack('/home/user/bar/baz')
3858
self.assertEquals('loc-foo/bar/baz', stack.get('lfoo', expand=True))
3862
class TestStackSet(TestStackWithTransport):
3864
def test_simple_set(self):
3865
conf = self.get_stack(self)
3866
self.assertEquals(None, conf.get('foo'))
3867
conf.set('foo', 'baz')
3868
# Did we get it back ?
3869
self.assertEquals('baz', conf.get('foo'))
3871
def test_set_creates_a_new_section(self):
3872
conf = self.get_stack(self)
3873
conf.set('foo', 'baz')
3874
self.assertEquals, 'baz', conf.get('foo')
3876
def test_set_hook(self):
3880
config.ConfigHooks.install_named_hook('set', hook, None)
3881
self.assertLength(0, calls)
3882
conf = self.get_stack(self)
3883
conf.set('foo', 'bar')
3884
self.assertLength(1, calls)
3885
self.assertEquals((conf, 'foo', 'bar'), calls[0])
3888
class TestStackRemove(TestStackWithTransport):
3890
def test_remove_existing(self):
3891
conf = self.get_stack(self)
3892
conf.set('foo', 'bar')
3893
self.assertEquals('bar', conf.get('foo'))
3895
# Did we get it back ?
3896
self.assertEquals(None, conf.get('foo'))
3898
def test_remove_unknown(self):
3899
conf = self.get_stack(self)
3900
self.assertRaises(KeyError, conf.remove, 'I_do_not_exist')
3902
def test_remove_hook(self):
3906
config.ConfigHooks.install_named_hook('remove', hook, None)
3907
self.assertLength(0, calls)
3908
conf = self.get_stack(self)
3909
conf.set('foo', 'bar')
3911
self.assertLength(1, calls)
3912
self.assertEquals((conf, 'foo'), calls[0])
3915
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
3918
super(TestConfigGetOptions, self).setUp()
3919
create_configs(self)
3921
def test_no_variable(self):
3922
# Using branch should query branch, locations and bazaar
3923
self.assertOptions([], self.branch_config)
3925
def test_option_in_bazaar(self):
3926
self.bazaar_config.set_user_option('file', 'bazaar')
3927
self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
3930
def test_option_in_locations(self):
3931
self.locations_config.set_user_option('file', 'locations')
3933
[('file', 'locations', self.tree.basedir, 'locations')],
3934
self.locations_config)
3936
def test_option_in_branch(self):
3937
self.branch_config.set_user_option('file', 'branch')
3938
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
3941
def test_option_in_bazaar_and_branch(self):
3942
self.bazaar_config.set_user_option('file', 'bazaar')
3943
self.branch_config.set_user_option('file', 'branch')
3944
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
3945
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3948
def test_option_in_branch_and_locations(self):
3949
# Hmm, locations override branch :-/
3950
self.locations_config.set_user_option('file', 'locations')
3951
self.branch_config.set_user_option('file', 'branch')
3953
[('file', 'locations', self.tree.basedir, 'locations'),
3954
('file', 'branch', 'DEFAULT', 'branch'),],
3957
def test_option_in_bazaar_locations_and_branch(self):
3958
self.bazaar_config.set_user_option('file', 'bazaar')
3959
self.locations_config.set_user_option('file', 'locations')
3960
self.branch_config.set_user_option('file', 'branch')
3962
[('file', 'locations', self.tree.basedir, 'locations'),
3963
('file', 'branch', 'DEFAULT', 'branch'),
3964
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3968
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
3971
super(TestConfigRemoveOption, self).setUp()
3972
create_configs_with_file_option(self)
3974
def test_remove_in_locations(self):
3975
self.locations_config.remove_user_option('file', self.tree.basedir)
3977
[('file', 'branch', 'DEFAULT', 'branch'),
3978
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3981
def test_remove_in_branch(self):
3982
self.branch_config.remove_user_option('file')
3984
[('file', 'locations', self.tree.basedir, 'locations'),
3985
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3988
def test_remove_in_bazaar(self):
3989
self.bazaar_config.remove_user_option('file')
3991
[('file', 'locations', self.tree.basedir, 'locations'),
3992
('file', 'branch', 'DEFAULT', 'branch'),],
3996
class TestConfigGetSections(tests.TestCaseWithTransport):
3999
super(TestConfigGetSections, self).setUp()
4000
create_configs(self)
4002
def assertSectionNames(self, expected, conf, name=None):
4003
"""Check which sections are returned for a given config.
4005
If fallback configurations exist their sections can be included.
4007
:param expected: A list of section names.
4009
:param conf: The configuration that will be queried.
4011
:param name: An optional section name that will be passed to
4014
sections = list(conf._get_sections(name))
4015
self.assertLength(len(expected), sections)
4016
self.assertEqual(expected, [name for name, _, _ in sections])
4018
def test_bazaar_default_section(self):
4019
self.assertSectionNames(['DEFAULT'], self.bazaar_config)
4021
def test_locations_default_section(self):
4022
# No sections are defined in an empty file
4023
self.assertSectionNames([], self.locations_config)
4025
def test_locations_named_section(self):
4026
self.locations_config.set_user_option('file', 'locations')
4027
self.assertSectionNames([self.tree.basedir], self.locations_config)
4029
def test_locations_matching_sections(self):
4030
loc_config = self.locations_config
4031
loc_config.set_user_option('file', 'locations')
4032
# We need to cheat a bit here to create an option in sections above and
4033
# below the 'location' one.
4034
parser = loc_config._get_parser()
4035
# locations.cong deals with '/' ignoring native os.sep
4036
location_names = self.tree.basedir.split('/')
4037
parent = '/'.join(location_names[:-1])
4038
child = '/'.join(location_names + ['child'])
4040
parser[parent]['file'] = 'parent'
4042
parser[child]['file'] = 'child'
4043
self.assertSectionNames([self.tree.basedir, parent], loc_config)
4045
def test_branch_data_default_section(self):
4046
self.assertSectionNames([None],
4047
self.branch_config._get_branch_data_config())
4049
def test_branch_default_sections(self):
4050
# No sections are defined in an empty locations file
4051
self.assertSectionNames([None, 'DEFAULT'],
4053
# Unless we define an option
4054
self.branch_config._get_location_config().set_user_option(
4055
'file', 'locations')
4056
self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
4059
def test_bazaar_named_section(self):
4060
# We need to cheat as the API doesn't give direct access to sections
4061
# other than DEFAULT.
4062
self.bazaar_config.set_alias('bazaar', 'bzr')
4063
self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
4066
class TestAuthenticationConfigFile(tests.TestCase):
4067
"""Test the authentication.conf file matching"""
4069
def _got_user_passwd(self, expected_user, expected_password,
4070
config, *args, **kwargs):
4071
credentials = config.get_credentials(*args, **kwargs)
4072
if credentials is None:
4076
user = credentials['user']
4077
password = credentials['password']
4078
self.assertEquals(expected_user, user)
4079
self.assertEquals(expected_password, password)
4081
def test_empty_config(self):
4082
conf = config.AuthenticationConfig(_file=StringIO())
4083
self.assertEquals({}, conf._get_config())
4084
self._got_user_passwd(None, None, conf, 'http', 'foo.net')
4086
def test_non_utf8_config(self):
4087
conf = config.AuthenticationConfig(_file=StringIO(
4089
self.assertRaises(errors.ConfigContentError, conf._get_config)
4091
def test_missing_auth_section_header(self):
4092
conf = config.AuthenticationConfig(_file=StringIO('foo = bar'))
4093
self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
4095
def test_auth_section_header_not_closed(self):
4096
conf = config.AuthenticationConfig(_file=StringIO('[DEF'))
4097
self.assertRaises(errors.ParseConfigError, conf._get_config)
4099
def test_auth_value_not_boolean(self):
4100
conf = config.AuthenticationConfig(_file=StringIO(
4104
verify_certificates=askme # Error: Not a boolean
4106
self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
4108
def test_auth_value_not_int(self):
4109
conf = config.AuthenticationConfig(_file=StringIO(
4113
port=port # Error: Not an int
4115
self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
4117
def test_unknown_password_encoding(self):
4118
conf = config.AuthenticationConfig(_file=StringIO(
4122
password_encoding=unknown
4124
self.assertRaises(ValueError, conf.get_password,
4125
'ftp', 'foo.net', 'joe')
4127
def test_credentials_for_scheme_host(self):
4128
conf = config.AuthenticationConfig(_file=StringIO(
4129
"""# Identity on foo.net
4134
password=secret-pass
4137
self._got_user_passwd('joe', 'secret-pass', conf, 'ftp', 'foo.net')
4139
self._got_user_passwd(None, None, conf, 'http', 'foo.net')
4141
self._got_user_passwd(None, None, conf, 'ftp', 'bar.net')
4143
def test_credentials_for_host_port(self):
4144
conf = config.AuthenticationConfig(_file=StringIO(
4145
"""# Identity on foo.net
4151
password=secret-pass
4154
self._got_user_passwd('joe', 'secret-pass',
4155
conf, 'ftp', 'foo.net', port=10021)
4157
self._got_user_passwd(None, None, conf, 'ftp', 'foo.net')
4159
def test_for_matching_host(self):
4160
conf = config.AuthenticationConfig(_file=StringIO(
4161
"""# Identity on foo.net
4167
[sourceforge domain]
4174
self._got_user_passwd('georges', 'bendover',
4175
conf, 'bzr', 'foo.bzr.sf.net')
4177
self._got_user_passwd(None, None,
4178
conf, 'bzr', 'bbzr.sf.net')
4180
def test_for_matching_host_None(self):
4181
conf = config.AuthenticationConfig(_file=StringIO(
4182
"""# Identity on foo.net
4192
self._got_user_passwd('joe', 'joepass',
4193
conf, 'bzr', 'quux.net')
4194
# no host but different scheme
4195
self._got_user_passwd('georges', 'bendover',
4196
conf, 'ftp', 'quux.net')
4198
def test_credentials_for_path(self):
4199
conf = config.AuthenticationConfig(_file=StringIO(
4215
self._got_user_passwd(None, None,
4216
conf, 'http', host='bar.org', path='/dir3')
4218
self._got_user_passwd('georges', 'bendover',
4219
conf, 'http', host='bar.org', path='/dir2')
4221
self._got_user_passwd('jim', 'jimpass',
4222
conf, 'http', host='bar.org',path='/dir1/subdir')
4224
def test_credentials_for_user(self):
4225
conf = config.AuthenticationConfig(_file=StringIO(
4234
self._got_user_passwd('jim', 'jimpass',
4235
conf, 'http', 'bar.org')
4237
self._got_user_passwd('jim', 'jimpass',
4238
conf, 'http', 'bar.org', user='jim')
4239
# Don't get a different user if one is specified
4240
self._got_user_passwd(None, None,
4241
conf, 'http', 'bar.org', user='georges')
4243
def test_credentials_for_user_without_password(self):
4244
conf = config.AuthenticationConfig(_file=StringIO(
4251
# Get user but no password
4252
self._got_user_passwd('jim', None,
4253
conf, 'http', 'bar.org')
4255
def test_verify_certificates(self):
4256
conf = config.AuthenticationConfig(_file=StringIO(
4263
verify_certificates=False
4270
credentials = conf.get_credentials('https', 'bar.org')
4271
self.assertEquals(False, credentials.get('verify_certificates'))
4272
credentials = conf.get_credentials('https', 'foo.net')
4273
self.assertEquals(True, credentials.get('verify_certificates'))
4276
class TestAuthenticationStorage(tests.TestCaseInTempDir):
4278
def test_set_credentials(self):
4279
conf = config.AuthenticationConfig()
4280
conf.set_credentials('name', 'host', 'user', 'scheme', 'password',
4281
99, path='/foo', verify_certificates=False, realm='realm')
4282
credentials = conf.get_credentials(host='host', scheme='scheme',
4283
port=99, path='/foo',
4285
CREDENTIALS = {'name': 'name', 'user': 'user', 'password': 'password',
4286
'verify_certificates': False, 'scheme': 'scheme',
4287
'host': 'host', 'port': 99, 'path': '/foo',
4289
self.assertEqual(CREDENTIALS, credentials)
4290
credentials_from_disk = config.AuthenticationConfig().get_credentials(
4291
host='host', scheme='scheme', port=99, path='/foo', realm='realm')
4292
self.assertEqual(CREDENTIALS, credentials_from_disk)
4294
def test_reset_credentials_different_name(self):
4295
conf = config.AuthenticationConfig()
4296
conf.set_credentials('name', 'host', 'user', 'scheme', 'password'),
4297
conf.set_credentials('name2', 'host', 'user2', 'scheme', 'password'),
4298
self.assertIs(None, conf._get_config().get('name'))
4299
credentials = conf.get_credentials(host='host', scheme='scheme')
4300
CREDENTIALS = {'name': 'name2', 'user': 'user2', 'password':
4301
'password', 'verify_certificates': True,
4302
'scheme': 'scheme', 'host': 'host', 'port': None,
4303
'path': None, 'realm': None}
4304
self.assertEqual(CREDENTIALS, credentials)
4307
class TestAuthenticationConfig(tests.TestCase):
4308
"""Test AuthenticationConfig behaviour"""
4310
def _check_default_password_prompt(self, expected_prompt_format, scheme,
4311
host=None, port=None, realm=None,
4315
user, password = 'jim', 'precious'
4316
expected_prompt = expected_prompt_format % {
4317
'scheme': scheme, 'host': host, 'port': port,
4318
'user': user, 'realm': realm}
4320
stdout = tests.StringIOWrapper()
4321
stderr = tests.StringIOWrapper()
4322
ui.ui_factory = tests.TestUIFactory(stdin=password + '\n',
4323
stdout=stdout, stderr=stderr)
4324
# We use an empty conf so that the user is always prompted
4325
conf = config.AuthenticationConfig()
4326
self.assertEquals(password,
4327
conf.get_password(scheme, host, user, port=port,
4328
realm=realm, path=path))
4329
self.assertEquals(expected_prompt, stderr.getvalue())
4330
self.assertEquals('', stdout.getvalue())
4332
def _check_default_username_prompt(self, expected_prompt_format, scheme,
4333
host=None, port=None, realm=None,
4338
expected_prompt = expected_prompt_format % {
4339
'scheme': scheme, 'host': host, 'port': port,
4341
stdout = tests.StringIOWrapper()
4342
stderr = tests.StringIOWrapper()
4343
ui.ui_factory = tests.TestUIFactory(stdin=username+ '\n',
4344
stdout=stdout, stderr=stderr)
4345
# We use an empty conf so that the user is always prompted
4346
conf = config.AuthenticationConfig()
4347
self.assertEquals(username, conf.get_user(scheme, host, port=port,
4348
realm=realm, path=path, ask=True))
4349
self.assertEquals(expected_prompt, stderr.getvalue())
4350
self.assertEquals('', stdout.getvalue())
4352
def test_username_defaults_prompts(self):
4353
# HTTP prompts can't be tested here, see test_http.py
4354
self._check_default_username_prompt(u'FTP %(host)s username: ', 'ftp')
4355
self._check_default_username_prompt(
4356
u'FTP %(host)s:%(port)d username: ', 'ftp', port=10020)
4357
self._check_default_username_prompt(
4358
u'SSH %(host)s:%(port)d username: ', 'ssh', port=12345)
4360
def test_username_default_no_prompt(self):
4361
conf = config.AuthenticationConfig()
4362
self.assertEquals(None,
4363
conf.get_user('ftp', 'example.com'))
4364
self.assertEquals("explicitdefault",
4365
conf.get_user('ftp', 'example.com', default="explicitdefault"))
4367
def test_password_default_prompts(self):
4368
# HTTP prompts can't be tested here, see test_http.py
4369
self._check_default_password_prompt(
4370
u'FTP %(user)s@%(host)s password: ', 'ftp')
4371
self._check_default_password_prompt(
4372
u'FTP %(user)s@%(host)s:%(port)d password: ', 'ftp', port=10020)
4373
self._check_default_password_prompt(
4374
u'SSH %(user)s@%(host)s:%(port)d password: ', 'ssh', port=12345)
4375
# SMTP port handling is a bit special (it's handled if embedded in the
4377
# FIXME: should we: forbid that, extend it to other schemes, leave
4378
# things as they are that's fine thank you ?
4379
self._check_default_password_prompt(
4380
u'SMTP %(user)s@%(host)s password: ', 'smtp')
4381
self._check_default_password_prompt(
4382
u'SMTP %(user)s@%(host)s password: ', 'smtp', host='bar.org:10025')
4383
self._check_default_password_prompt(
4384
u'SMTP %(user)s@%(host)s:%(port)d password: ', 'smtp', port=10025)
4386
def test_ssh_password_emits_warning(self):
4387
conf = config.AuthenticationConfig(_file=StringIO(
4395
entered_password = 'typed-by-hand'
4396
stdout = tests.StringIOWrapper()
4397
stderr = tests.StringIOWrapper()
4398
ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
4399
stdout=stdout, stderr=stderr)
4401
# Since the password defined in the authentication config is ignored,
4402
# the user is prompted
4403
self.assertEquals(entered_password,
4404
conf.get_password('ssh', 'bar.org', user='jim'))
4405
self.assertContainsRe(
4407
'password ignored in section \[ssh with password\]')
4409
def test_ssh_without_password_doesnt_emit_warning(self):
4410
conf = config.AuthenticationConfig(_file=StringIO(
4417
entered_password = 'typed-by-hand'
4418
stdout = tests.StringIOWrapper()
4419
stderr = tests.StringIOWrapper()
4420
ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
4424
# Since the password defined in the authentication config is ignored,
4425
# the user is prompted
4426
self.assertEquals(entered_password,
4427
conf.get_password('ssh', 'bar.org', user='jim'))
4428
# No warning shoud be emitted since there is no password. We are only
4430
self.assertNotContainsRe(
4432
'password ignored in section \[ssh with password\]')
4434
def test_uses_fallback_stores(self):
4435
self.overrideAttr(config, 'credential_store_registry',
4436
config.CredentialStoreRegistry())
4437
store = StubCredentialStore()
4438
store.add_credentials("http", "example.com", "joe", "secret")
4439
config.credential_store_registry.register("stub", store, fallback=True)
4440
conf = config.AuthenticationConfig(_file=StringIO())
4441
creds = conf.get_credentials("http", "example.com")
4442
self.assertEquals("joe", creds["user"])
4443
self.assertEquals("secret", creds["password"])
4446
class StubCredentialStore(config.CredentialStore):
4452
def add_credentials(self, scheme, host, user, password=None):
4453
self._username[(scheme, host)] = user
4454
self._password[(scheme, host)] = password
4456
def get_credentials(self, scheme, host, port=None, user=None,
4457
path=None, realm=None):
4458
key = (scheme, host)
4459
if not key in self._username:
4461
return { "scheme": scheme, "host": host, "port": port,
4462
"user": self._username[key], "password": self._password[key]}
4465
class CountingCredentialStore(config.CredentialStore):
4470
def get_credentials(self, scheme, host, port=None, user=None,
4471
path=None, realm=None):
4476
class TestCredentialStoreRegistry(tests.TestCase):
4478
def _get_cs_registry(self):
4479
return config.credential_store_registry
4481
def test_default_credential_store(self):
4482
r = self._get_cs_registry()
4483
default = r.get_credential_store(None)
4484
self.assertIsInstance(default, config.PlainTextCredentialStore)
4486
def test_unknown_credential_store(self):
4487
r = self._get_cs_registry()
4488
# It's hard to imagine someone creating a credential store named
4489
# 'unknown' so we use that as an never registered key.
4490
self.assertRaises(KeyError, r.get_credential_store, 'unknown')
4492
def test_fallback_none_registered(self):
4493
r = config.CredentialStoreRegistry()
4494
self.assertEquals(None,
4495
r.get_fallback_credentials("http", "example.com"))
4497
def test_register(self):
4498
r = config.CredentialStoreRegistry()
4499
r.register("stub", StubCredentialStore(), fallback=False)
4500
r.register("another", StubCredentialStore(), fallback=True)
4501
self.assertEquals(["another", "stub"], r.keys())
4503
def test_register_lazy(self):
4504
r = config.CredentialStoreRegistry()
4505
r.register_lazy("stub", "bzrlib.tests.test_config",
4506
"StubCredentialStore", fallback=False)
4507
self.assertEquals(["stub"], r.keys())
4508
self.assertIsInstance(r.get_credential_store("stub"),
4509
StubCredentialStore)
4511
def test_is_fallback(self):
4512
r = config.CredentialStoreRegistry()
4513
r.register("stub1", None, fallback=False)
4514
r.register("stub2", None, fallback=True)
4515
self.assertEquals(False, r.is_fallback("stub1"))
4516
self.assertEquals(True, r.is_fallback("stub2"))
4518
def test_no_fallback(self):
4519
r = config.CredentialStoreRegistry()
4520
store = CountingCredentialStore()
4521
r.register("count", store, fallback=False)
4522
self.assertEquals(None,
4523
r.get_fallback_credentials("http", "example.com"))
4524
self.assertEquals(0, store._calls)
4526
def test_fallback_credentials(self):
4527
r = config.CredentialStoreRegistry()
4528
store = StubCredentialStore()
4529
store.add_credentials("http", "example.com",
4530
"somebody", "geheim")
4531
r.register("stub", store, fallback=True)
4532
creds = r.get_fallback_credentials("http", "example.com")
4533
self.assertEquals("somebody", creds["user"])
4534
self.assertEquals("geheim", creds["password"])
4536
def test_fallback_first_wins(self):
4537
r = config.CredentialStoreRegistry()
4538
stub1 = StubCredentialStore()
4539
stub1.add_credentials("http", "example.com",
4540
"somebody", "stub1")
4541
r.register("stub1", stub1, fallback=True)
4542
stub2 = StubCredentialStore()
4543
stub2.add_credentials("http", "example.com",
4544
"somebody", "stub2")
4545
r.register("stub2", stub1, fallback=True)
4546
creds = r.get_fallback_credentials("http", "example.com")
4547
self.assertEquals("somebody", creds["user"])
4548
self.assertEquals("stub1", creds["password"])
4551
class TestPlainTextCredentialStore(tests.TestCase):
4553
def test_decode_password(self):
4554
r = config.credential_store_registry
4555
plain_text = r.get_credential_store()
4556
decoded = plain_text.decode_password(dict(password='secret'))
4557
self.assertEquals('secret', decoded)
4560
# FIXME: Once we have a way to declare authentication to all test servers, we
4561
# can implement generic tests.
4562
# test_user_password_in_url
4563
# test_user_in_url_password_from_config
4564
# test_user_in_url_password_prompted
4565
# test_user_in_config
4566
# test_user_getpass.getuser
4567
# test_user_prompted ?
4568
class TestAuthenticationRing(tests.TestCaseWithTransport):
4572
class TestAutoUserId(tests.TestCase):
4573
"""Test inferring an automatic user name."""
4575
def test_auto_user_id(self):
4576
"""Automatic inference of user name.
4578
This is a bit hard to test in an isolated way, because it depends on
4579
system functions that go direct to /etc or perhaps somewhere else.
4580
But it's reasonable to say that on Unix, with an /etc/mailname, we ought
4581
to be able to choose a user name with no configuration.
4583
if sys.platform == 'win32':
4584
raise tests.TestSkipped(
4585
"User name inference not implemented on win32")
4586
realname, address = config._auto_user_id()
4587
if os.path.exists('/etc/mailname'):
4588
self.assertIsNot(None, realname)
4589
self.assertIsNot(None, address)
4591
self.assertEquals((None, None), (realname, address))
4594
class EmailOptionTests(tests.TestCase):
4596
def test_default_email_uses_BZR_EMAIL(self):
4597
# BZR_EMAIL takes precedence over EMAIL
4598
self.overrideEnv('BZR_EMAIL', 'jelmer@samba.org')
4599
self.overrideEnv('EMAIL', 'jelmer@apache.org')
4600
self.assertEquals('jelmer@samba.org', config.default_email())
4602
def test_default_email_uses_EMAIL(self):
4603
self.overrideEnv('BZR_EMAIL', None)
4604
self.overrideEnv('EMAIL', 'jelmer@apache.org')
4605
self.assertEquals('jelmer@apache.org', config.default_email())
4607
def test_BZR_EMAIL_overrides(self):
4608
self.overrideEnv('BZR_EMAIL', 'jelmer@apache.org')
4609
self.assertEquals('jelmer@apache.org',
4610
config.email_from_store('jelmer@debian.org'))
4611
self.overrideEnv('BZR_EMAIL', None)
4612
self.overrideEnv('EMAIL', 'jelmer@samba.org')
4613
self.assertEquals('jelmer@debian.org',
4614
config.email_from_store('jelmer@debian.org'))