1403
405
# This is testing the correct file names are provided.
1404
406
# TODO: consolidate with the test for GlobalConfigs filename checks.
1406
# replace the class that is constructed, to check its parameters
408
# replace the class that is constructured, to check its parameters
1407
409
oldparserclass = config.ConfigObj
1408
410
config.ConfigObj = InstrumentedConfigObj
411
my_config = config.LocationConfig('http://www.example.com')
1410
my_config = config.LocationConfig('http://www.example.com')
1411
413
parser = my_config._get_parser()
1413
415
config.ConfigObj = oldparserclass
1414
self.assertIsInstance(parser, InstrumentedConfigObj)
416
self.failUnless(isinstance(parser, InstrumentedConfigObj))
1415
417
self.assertEqual(parser._calls,
1416
[('__init__', config.locations_config_filename(),
418
[('__init__', config.branches_config_filename())])
1419
420
def test_get_global_config(self):
1420
my_config = config.BranchConfig(FakeBranch('http://example.com'))
421
my_config = config.LocationConfig('http://example.com')
1421
422
global_config = my_config._get_global_config()
1422
self.assertIsInstance(global_config, config.GlobalConfig)
1423
self.assertIs(global_config, my_config._get_global_config())
1425
def assertLocationMatching(self, expected):
1426
self.assertEqual(expected,
1427
list(self.my_location_config._get_matching_sections()))
1429
def test__get_matching_sections_no_match(self):
1430
self.get_branch_config('/')
1431
self.assertLocationMatching([])
1433
def test__get_matching_sections_exact(self):
1434
self.get_branch_config('http://www.example.com')
1435
self.assertLocationMatching([('http://www.example.com', '')])
1437
def test__get_matching_sections_suffix_does_not(self):
1438
self.get_branch_config('http://www.example.com-com')
1439
self.assertLocationMatching([])
1441
def test__get_matching_sections_subdir_recursive(self):
1442
self.get_branch_config('http://www.example.com/com')
1443
self.assertLocationMatching([('http://www.example.com', 'com')])
1445
def test__get_matching_sections_ignoreparent(self):
1446
self.get_branch_config('http://www.example.com/ignoreparent')
1447
self.assertLocationMatching([('http://www.example.com/ignoreparent',
1450
def test__get_matching_sections_ignoreparent_subdir(self):
1451
self.get_branch_config(
1452
'http://www.example.com/ignoreparent/childbranch')
1453
self.assertLocationMatching([('http://www.example.com/ignoreparent',
1456
def test__get_matching_sections_subdir_trailing_slash(self):
1457
self.get_branch_config('/b')
1458
self.assertLocationMatching([('/b/', '')])
1460
def test__get_matching_sections_subdir_child(self):
1461
self.get_branch_config('/a/foo')
1462
self.assertLocationMatching([('/a/*', ''), ('/a/', 'foo')])
1464
def test__get_matching_sections_subdir_child_child(self):
1465
self.get_branch_config('/a/foo/bar')
1466
self.assertLocationMatching([('/a/*', 'bar'), ('/a/', 'foo/bar')])
1468
def test__get_matching_sections_trailing_slash_with_children(self):
1469
self.get_branch_config('/a/')
1470
self.assertLocationMatching([('/a/', '')])
1472
def test__get_matching_sections_explicit_over_glob(self):
1473
# XXX: 2006-09-08 jamesh
1474
# This test only passes because ord('c') > ord('*'). If there
1475
# was a config section for '/a/?', it would get precedence
1477
self.get_branch_config('/a/c')
1478
self.assertLocationMatching([('/a/c', ''), ('/a/*', ''), ('/a/', 'c')])
1480
def test__get_option_policy_normal(self):
1481
self.get_branch_config('http://www.example.com')
1483
self.my_location_config._get_config_policy(
1484
'http://www.example.com', 'normal_option'),
1487
def test__get_option_policy_norecurse(self):
1488
self.get_branch_config('http://www.example.com')
1490
self.my_location_config._get_option_policy(
1491
'http://www.example.com', 'norecurse_option'),
1492
config.POLICY_NORECURSE)
1493
# Test old recurse=False setting:
1495
self.my_location_config._get_option_policy(
1496
'http://www.example.com/norecurse', 'normal_option'),
1497
config.POLICY_NORECURSE)
1499
def test__get_option_policy_normal(self):
1500
self.get_branch_config('http://www.example.com')
1502
self.my_location_config._get_option_policy(
1503
'http://www.example.com', 'appendpath_option'),
1504
config.POLICY_APPENDPATH)
1506
def test__get_options_with_policy(self):
1507
self.get_branch_config('/dir/subdir',
1508
location_config="""\
1510
other_url = /other-dir
1511
other_url:policy = appendpath
1513
other_url = /other-subdir
1516
[(u'other_url', u'/other-subdir', u'/dir/subdir', 'locations'),
1517
(u'other_url', u'/other-dir', u'/dir', 'locations'),
1518
(u'other_url:policy', u'appendpath', u'/dir', 'locations')],
1519
self.my_location_config)
423
self.failUnless(isinstance(global_config, config.GlobalConfig))
424
self.failUnless(global_config is my_config._get_global_config())
426
def test__get_section_no_match(self):
427
self.get_location_config('/')
428
self.assertEqual(None, self.my_config._get_section())
430
def test__get_section_exact(self):
431
self.get_location_config('http://www.example.com')
432
self.assertEqual('http://www.example.com',
433
self.my_config._get_section())
435
def test__get_section_suffix_does_not(self):
436
self.get_location_config('http://www.example.com-com')
437
self.assertEqual(None, self.my_config._get_section())
439
def test__get_section_subdir_recursive(self):
440
self.get_location_config('http://www.example.com/com')
441
self.assertEqual('http://www.example.com',
442
self.my_config._get_section())
444
def test__get_section_subdir_matches(self):
445
self.get_location_config('http://www.example.com/useglobal')
446
self.assertEqual('http://www.example.com/useglobal',
447
self.my_config._get_section())
449
def test__get_section_subdir_nonrecursive(self):
450
self.get_location_config(
451
'http://www.example.com/useglobal/childbranch')
452
self.assertEqual('http://www.example.com',
453
self.my_config._get_section())
455
def test__get_section_subdir_trailing_slash(self):
456
self.get_location_config('/b')
457
self.assertEqual('/b/', self.my_config._get_section())
459
def test__get_section_subdir_child(self):
460
self.get_location_config('/a/foo')
461
self.assertEqual('/a/*', self.my_config._get_section())
463
def test__get_section_subdir_child_child(self):
464
self.get_location_config('/a/foo/bar')
465
self.assertEqual('/a/', self.my_config._get_section())
467
def test__get_section_trailing_slash_with_children(self):
468
self.get_location_config('/a/')
469
self.assertEqual('/a/', self.my_config._get_section())
471
def test__get_section_explicit_over_glob(self):
472
self.get_location_config('/a/c')
473
self.assertEqual('/a/c', self.my_config._get_section())
475
def get_location_config(self, location, global_config=None):
476
if global_config is None:
477
global_file = StringIO(sample_config_text)
479
global_file = StringIO(global_config)
480
branches_file = StringIO(sample_branches_text)
481
self.my_config = config.LocationConfig(location)
482
self.my_config._get_parser(branches_file)
483
self.my_config._get_global_config()._get_parser(global_file)
1521
485
def test_location_without_username(self):
1522
self.get_branch_config('http://www.example.com/ignoreparent')
1523
self.assertEqual(u'Erik B\u00e5gfors <erik@bagfors.nu>',
486
self.get_location_config('http://www.example.com/useglobal')
487
self.assertEqual('Robert Collins <robertc@example.com>',
1524
488
self.my_config.username())
1526
490
def test_location_not_listed(self):
1527
"""Test that the global username is used when no location matches"""
1528
self.get_branch_config('/home/robertc/sources')
1529
self.assertEqual(u'Erik B\u00e5gfors <erik@bagfors.nu>',
491
self.get_location_config('/home/robertc/sources')
492
self.assertEqual('Robert Collins <robertc@example.com>',
1530
493
self.my_config.username())
1532
495
def test_overriding_location(self):
1533
self.get_branch_config('http://www.example.com/foo')
496
self.get_location_config('http://www.example.com/foo')
1534
497
self.assertEqual('Robert Collins <robertc@example.org>',
1535
498
self.my_config.username())
1537
500
def test_signatures_not_set(self):
1538
self.get_branch_config('http://www.example.com',
501
self.get_location_config('http://www.example.com',
1539
502
global_config=sample_ignore_signatures)
1540
self.assertEqual(config.CHECK_ALWAYS,
503
self.assertEqual(config.CHECK_NEVER,
1541
504
self.my_config.signature_checking())
1542
self.assertEqual(config.SIGN_NEVER,
1543
self.my_config.signing_policy())
1545
506
def test_signatures_never(self):
1546
self.get_branch_config('/a/c')
507
self.get_location_config('/a/c')
1547
508
self.assertEqual(config.CHECK_NEVER,
1548
509
self.my_config.signature_checking())
1550
511
def test_signatures_when_available(self):
1551
self.get_branch_config('/a/', global_config=sample_ignore_signatures)
512
self.get_location_config('/a/', global_config=sample_ignore_signatures)
1552
513
self.assertEqual(config.CHECK_IF_POSSIBLE,
1553
514
self.my_config.signature_checking())
1555
516
def test_signatures_always(self):
1556
self.get_branch_config('/b')
517
self.get_location_config('/b')
1557
518
self.assertEqual(config.CHECK_ALWAYS,
1558
519
self.my_config.signature_checking())
1560
521
def test_gpg_signing_command(self):
1561
self.get_branch_config('/b')
522
self.get_location_config('/b')
1562
523
self.assertEqual("gnome-gpg", self.my_config.gpg_signing_command())
1564
525
def test_gpg_signing_command_missing(self):
1565
self.get_branch_config('/a')
526
self.get_location_config('/a')
1566
527
self.assertEqual("false", self.my_config.gpg_signing_command())
1568
def test_gpg_signing_key(self):
1569
self.get_branch_config('/b')
1570
self.assertEqual("DD4D5088", self.my_config.gpg_signing_key())
1572
def test_gpg_signing_key_default(self):
1573
self.get_branch_config('/a')
1574
self.assertEqual("erik@bagfors.nu", self.my_config.gpg_signing_key())
1576
529
def test_get_user_option_global(self):
1577
self.get_branch_config('/a')
530
self.get_location_config('/a')
1578
531
self.assertEqual('something',
1579
532
self.my_config.get_user_option('user_global_option'))
1581
534
def test_get_user_option_local(self):
1582
self.get_branch_config('/a')
535
self.get_location_config('/a')
1583
536
self.assertEqual('local',
1584
537
self.my_config.get_user_option('user_local_option'))
1586
def test_get_user_option_appendpath(self):
1587
# returned as is for the base path:
1588
self.get_branch_config('http://www.example.com')
1589
self.assertEqual('append',
1590
self.my_config.get_user_option('appendpath_option'))
1591
# Extra path components get appended:
1592
self.get_branch_config('http://www.example.com/a/b/c')
1593
self.assertEqual('append/a/b/c',
1594
self.my_config.get_user_option('appendpath_option'))
1595
# Overriden for http://www.example.com/dir, where it is a
1597
self.get_branch_config('http://www.example.com/dir/a/b/c')
1598
self.assertEqual('normal',
1599
self.my_config.get_user_option('appendpath_option'))
1601
def test_get_user_option_norecurse(self):
1602
self.get_branch_config('http://www.example.com')
1603
self.assertEqual('norecurse',
1604
self.my_config.get_user_option('norecurse_option'))
1605
self.get_branch_config('http://www.example.com/dir')
1606
self.assertEqual(None,
1607
self.my_config.get_user_option('norecurse_option'))
1608
# http://www.example.com/norecurse is a recurse=False section
1609
# that redefines normal_option. Subdirectories do not pick up
1610
# this redefinition.
1611
self.get_branch_config('http://www.example.com/norecurse')
1612
self.assertEqual('norecurse',
1613
self.my_config.get_user_option('normal_option'))
1614
self.get_branch_config('http://www.example.com/norecurse/subdir')
1615
self.assertEqual('normal',
1616
self.my_config.get_user_option('normal_option'))
1618
def test_set_user_option_norecurse(self):
1619
self.get_branch_config('http://www.example.com')
1620
self.my_config.set_user_option('foo', 'bar',
1621
store=config.STORE_LOCATION_NORECURSE)
1623
self.my_location_config._get_option_policy(
1624
'http://www.example.com', 'foo'),
1625
config.POLICY_NORECURSE)
1627
def test_set_user_option_appendpath(self):
1628
self.get_branch_config('http://www.example.com')
1629
self.my_config.set_user_option('foo', 'bar',
1630
store=config.STORE_LOCATION_APPENDPATH)
1632
self.my_location_config._get_option_policy(
1633
'http://www.example.com', 'foo'),
1634
config.POLICY_APPENDPATH)
1636
def test_set_user_option_change_policy(self):
1637
self.get_branch_config('http://www.example.com')
1638
self.my_config.set_user_option('norecurse_option', 'normal',
1639
store=config.STORE_LOCATION)
1641
self.my_location_config._get_option_policy(
1642
'http://www.example.com', 'norecurse_option'),
1645
def test_set_user_option_recurse_false_section(self):
1646
# The following section has recurse=False set. The test is to
1647
# make sure that a normal option can be added to the section,
1648
# converting recurse=False to the norecurse policy.
1649
self.get_branch_config('http://www.example.com/norecurse')
1650
self.callDeprecated(['The recurse option is deprecated as of 0.14. '
1651
'The section "http://www.example.com/norecurse" '
1652
'has been converted to use policies.'],
1653
self.my_config.set_user_option,
1654
'foo', 'bar', store=config.STORE_LOCATION)
1656
self.my_location_config._get_option_policy(
1657
'http://www.example.com/norecurse', 'foo'),
1659
# The previously existing option is still norecurse:
1661
self.my_location_config._get_option_policy(
1662
'http://www.example.com/norecurse', 'normal_option'),
1663
config.POLICY_NORECURSE)
1665
539
def test_post_commit_default(self):
1666
self.get_branch_config('/a/c')
540
self.get_location_config('/a/c')
1667
541
self.assertEqual('bzrlib.tests.test_config.post_commit',
1668
542
self.my_config.post_commit())
1670
def get_branch_config(self, location, global_config=None,
1671
location_config=None):
1672
my_branch = FakeBranch(location)
545
class TestLocationConfig(TestCaseInTempDir):
547
def get_location_config(self, location, global_config=None):
1673
548
if global_config is None:
1674
global_config = sample_config_text
1675
if location_config is None:
1676
location_config = sample_branches_text
1678
my_global_config = config.GlobalConfig.from_string(global_config,
1680
my_location_config = config.LocationConfig.from_string(
1681
location_config, my_branch.base, save=True)
1682
my_config = config.BranchConfig(my_branch)
1683
self.my_config = my_config
1684
self.my_location_config = my_config._get_location_config()
549
global_file = StringIO(sample_config_text)
551
global_file = StringIO(global_config)
552
branches_file = StringIO(sample_branches_text)
553
self.my_config = config.LocationConfig(location)
554
self.my_config._get_parser(branches_file)
555
self.my_config._get_global_config()._get_parser(global_file)
1686
557
def test_set_user_setting_sets_and_saves(self):
1687
self.get_branch_config('/a/c')
558
self.get_location_config('/a/c')
1688
559
record = InstrumentedConfigObj("foo")
1689
self.my_location_config._parser = record
1691
self.callDeprecated(['The recurse option is deprecated as of '
1692
'0.14. The section "/a/c" has been '
1693
'converted to use policies.'],
1694
self.my_config.set_user_option,
1695
'foo', 'bar', store=config.STORE_LOCATION)
1696
self.assertEqual([('reload',),
1697
('__contains__', '/a/c'),
560
self.my_config._parser = record
562
real_mkdir = os.mkdir
564
def checked_mkdir(path, mode=0777):
565
self.log('making directory: %s', path)
566
real_mkdir(path, mode)
569
os.mkdir = checked_mkdir
571
self.my_config.set_user_option('foo', 'bar')
573
os.mkdir = real_mkdir
575
self.failUnless(self.created, 'Failed to create ~/.bazaar')
576
self.assertEqual([('__contains__', '/a/c'),
1698
577
('__contains__', '/a/c/'),
1699
578
('__setitem__', '/a/c', {}),
1700
579
('__getitem__', '/a/c'),
1701
580
('__setitem__', 'foo', 'bar'),
1702
('__getitem__', '/a/c'),
1703
('as_bool', 'recurse'),
1704
('__getitem__', '/a/c'),
1705
('__delitem__', 'recurse'),
1706
('__getitem__', '/a/c'),
1708
('__getitem__', '/a/c'),
1709
('__contains__', 'foo:policy'),
1711
582
record._calls[1:])
1713
def test_set_user_setting_sets_and_saves2(self):
1714
self.get_branch_config('/a/c')
1715
self.assertIs(self.my_config.get_user_option('foo'), None)
1716
self.my_config.set_user_option('foo', 'bar')
1718
self.my_config.branch.control_files.files['branch.conf'].strip(),
1720
self.assertEqual(self.my_config.get_user_option('foo'), 'bar')
1721
self.my_config.set_user_option('foo', 'baz',
1722
store=config.STORE_LOCATION)
1723
self.assertEqual(self.my_config.get_user_option('foo'), 'baz')
1724
self.my_config.set_user_option('foo', 'qux')
1725
self.assertEqual(self.my_config.get_user_option('foo'), 'baz')
1727
def test_get_bzr_remote_path(self):
1728
my_config = config.LocationConfig('/a/c')
1729
self.assertEqual('bzr', my_config.get_bzr_remote_path())
1730
my_config.set_user_option('bzr_remote_path', '/path-bzr')
1731
self.assertEqual('/path-bzr', my_config.get_bzr_remote_path())
1732
self.overrideEnv('BZR_REMOTE_PATH', '/environ-bzr')
1733
self.assertEqual('/environ-bzr', my_config.get_bzr_remote_path())
1736
precedence_global = 'option = global'
1737
precedence_branch = 'option = branch'
1738
precedence_location = """
1742
[http://example.com/specific]
1746
class TestBranchConfigItems(tests.TestCaseInTempDir):
1748
def get_branch_config(self, global_config=None, location=None,
1749
location_config=None, branch_data_config=None):
1750
my_branch = FakeBranch(location)
1751
if global_config is not None:
1752
my_global_config = config.GlobalConfig.from_string(global_config,
1754
if location_config is not None:
1755
my_location_config = config.LocationConfig.from_string(
1756
location_config, my_branch.base, save=True)
1757
my_config = config.BranchConfig(my_branch)
1758
if branch_data_config is not None:
1759
my_config.branch.control_files.files['branch.conf'] = \
585
class TestBranchConfigItems(TestCase):
1763
587
def test_user_id(self):
1764
branch = FakeBranch(user_id='Robert Collins <robertc@example.net>')
588
branch = FakeBranch()
1765
589
my_config = config.BranchConfig(branch)
1766
590
self.assertEqual("Robert Collins <robertc@example.net>",
1767
my_config.username())
1768
my_config.branch.control_files.files['email'] = "John"
1769
my_config.set_user_option('email',
1770
"Robert Collins <robertc@example.org>")
1771
self.assertEqual("John", my_config.username())
1772
del my_config.branch.control_files.files['email']
1773
self.assertEqual("Robert Collins <robertc@example.org>",
1774
my_config.username())
591
my_config._get_user_id())
592
branch.control_files.email = "John"
593
self.assertEqual("John", my_config._get_user_id())
1776
595
def test_not_set_in_branch(self):
1777
my_config = self.get_branch_config(global_config=sample_config_text)
1778
self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
596
branch = FakeBranch()
597
my_config = config.BranchConfig(branch)
598
branch.control_files.email = None
599
config_file = StringIO(sample_config_text)
600
(my_config._get_location_config().
601
_get_global_config()._get_parser(config_file))
602
self.assertEqual("Robert Collins <robertc@example.com>",
1779
603
my_config._get_user_id())
1780
my_config.branch.control_files.files['email'] = "John"
604
branch.control_files.email = "John"
1781
605
self.assertEqual("John", my_config._get_user_id())
1783
def test_BZR_EMAIL_OVERRIDES(self):
1784
self.overrideEnv('BZR_EMAIL', "Robert Collins <robertc@example.org>")
607
def test_BZREMAIL_OVERRIDES(self):
608
os.environ['BZREMAIL'] = "Robert Collins <robertc@example.org>"
1785
609
branch = FakeBranch()
1786
610
my_config = config.BranchConfig(branch)
1787
611
self.assertEqual("Robert Collins <robertc@example.org>",
1788
612
my_config.username())
1790
614
def test_signatures_forced(self):
1791
my_config = self.get_branch_config(
1792
global_config=sample_always_signatures)
1793
self.assertEqual(config.CHECK_NEVER, my_config.signature_checking())
1794
self.assertEqual(config.SIGN_ALWAYS, my_config.signing_policy())
1795
self.assertTrue(my_config.signature_needed())
1797
def test_signatures_forced_branch(self):
1798
my_config = self.get_branch_config(
1799
global_config=sample_ignore_signatures,
1800
branch_data_config=sample_always_signatures)
1801
self.assertEqual(config.CHECK_NEVER, my_config.signature_checking())
1802
self.assertEqual(config.SIGN_ALWAYS, my_config.signing_policy())
1803
self.assertTrue(my_config.signature_needed())
615
branch = FakeBranch()
616
my_config = config.BranchConfig(branch)
617
config_file = StringIO(sample_always_signatures)
618
(my_config._get_location_config().
619
_get_global_config()._get_parser(config_file))
620
self.assertEqual(config.CHECK_ALWAYS, my_config.signature_checking())
1805
622
def test_gpg_signing_command(self):
1806
my_config = self.get_branch_config(
1807
global_config=sample_config_text,
1808
# branch data cannot set gpg_signing_command
1809
branch_data_config="gpg_signing_command=pgp")
623
branch = FakeBranch()
624
my_config = config.BranchConfig(branch)
625
config_file = StringIO(sample_config_text)
626
(my_config._get_location_config().
627
_get_global_config()._get_parser(config_file))
1810
628
self.assertEqual('gnome-gpg', my_config.gpg_signing_command())
1812
630
def test_get_user_option_global(self):
1813
my_config = self.get_branch_config(global_config=sample_config_text)
631
branch = FakeBranch()
632
my_config = config.BranchConfig(branch)
633
config_file = StringIO(sample_config_text)
634
(my_config._get_location_config().
635
_get_global_config()._get_parser(config_file))
1814
636
self.assertEqual('something',
1815
637
my_config.get_user_option('user_global_option'))
1817
639
def test_post_commit_default(self):
1818
my_config = self.get_branch_config(global_config=sample_config_text,
1820
location_config=sample_branches_text)
1821
self.assertEqual(my_config.branch.base, '/a/c')
1822
self.assertEqual('bzrlib.tests.test_config.post_commit',
1823
my_config.post_commit())
1824
my_config.set_user_option('post_commit', 'rmtree_root')
1825
# post-commit is ignored when present in branch data
1826
self.assertEqual('bzrlib.tests.test_config.post_commit',
1827
my_config.post_commit())
1828
my_config.set_user_option('post_commit', 'rmtree_root',
1829
store=config.STORE_LOCATION)
1830
self.assertEqual('rmtree_root', my_config.post_commit())
1832
def test_config_precedence(self):
1833
# FIXME: eager test, luckily no persitent config file makes it fail
1835
my_config = self.get_branch_config(global_config=precedence_global)
1836
self.assertEqual(my_config.get_user_option('option'), 'global')
1837
my_config = self.get_branch_config(global_config=precedence_global,
1838
branch_data_config=precedence_branch)
1839
self.assertEqual(my_config.get_user_option('option'), 'branch')
1840
my_config = self.get_branch_config(
1841
global_config=precedence_global,
1842
branch_data_config=precedence_branch,
1843
location_config=precedence_location)
1844
self.assertEqual(my_config.get_user_option('option'), 'recurse')
1845
my_config = self.get_branch_config(
1846
global_config=precedence_global,
1847
branch_data_config=precedence_branch,
1848
location_config=precedence_location,
1849
location='http://example.com/specific')
1850
self.assertEqual(my_config.get_user_option('option'), 'exact')
1852
def test_get_mail_client(self):
1853
config = self.get_branch_config()
1854
client = config.get_mail_client()
1855
self.assertIsInstance(client, mail_client.DefaultMail)
1858
config.set_user_option('mail_client', 'evolution')
1859
client = config.get_mail_client()
1860
self.assertIsInstance(client, mail_client.Evolution)
1862
config.set_user_option('mail_client', 'kmail')
1863
client = config.get_mail_client()
1864
self.assertIsInstance(client, mail_client.KMail)
1866
config.set_user_option('mail_client', 'mutt')
1867
client = config.get_mail_client()
1868
self.assertIsInstance(client, mail_client.Mutt)
1870
config.set_user_option('mail_client', 'thunderbird')
1871
client = config.get_mail_client()
1872
self.assertIsInstance(client, mail_client.Thunderbird)
1875
config.set_user_option('mail_client', 'default')
1876
client = config.get_mail_client()
1877
self.assertIsInstance(client, mail_client.DefaultMail)
1879
config.set_user_option('mail_client', 'editor')
1880
client = config.get_mail_client()
1881
self.assertIsInstance(client, mail_client.Editor)
1883
config.set_user_option('mail_client', 'mapi')
1884
client = config.get_mail_client()
1885
self.assertIsInstance(client, mail_client.MAPIClient)
1887
config.set_user_option('mail_client', 'xdg-email')
1888
client = config.get_mail_client()
1889
self.assertIsInstance(client, mail_client.XDGEmail)
1891
config.set_user_option('mail_client', 'firebird')
1892
self.assertRaises(errors.UnknownMailClient, config.get_mail_client)
1895
class TestMailAddressExtraction(tests.TestCase):
640
branch = FakeBranch()
642
my_config = config.BranchConfig(branch)
643
config_file = StringIO(sample_config_text)
644
(my_config._get_location_config().
645
_get_global_config()._get_parser(config_file))
646
branch_file = StringIO(sample_branches_text)
647
my_config._get_location_config()._get_parser(branch_file)
648
self.assertEqual('bzrlib.tests.test_config.post_commit',
649
my_config.post_commit())
652
class TestMailAddressExtraction(TestCase):
1897
654
def test_extract_email_address(self):
1898
655
self.assertEqual('jane@test.com',
1899
656
config.extract_email_address('Jane <jane@test.com>'))
1900
self.assertRaises(errors.NoEmailInUsername,
657
self.assertRaises(errors.BzrError,
1901
658
config.extract_email_address, 'Jane Tester')
1903
def test_parse_username(self):
1904
self.assertEqual(('', 'jdoe@example.com'),
1905
config.parse_username('jdoe@example.com'))
1906
self.assertEqual(('', 'jdoe@example.com'),
1907
config.parse_username('<jdoe@example.com>'))
1908
self.assertEqual(('John Doe', 'jdoe@example.com'),
1909
config.parse_username('John Doe <jdoe@example.com>'))
1910
self.assertEqual(('John Doe', ''),
1911
config.parse_username('John Doe'))
1912
self.assertEqual(('John Doe', 'jdoe@example.com'),
1913
config.parse_username('John Doe jdoe@example.com'))
1915
class TestTreeConfig(tests.TestCaseWithTransport):
1917
def test_get_value(self):
1918
"""Test that retreiving a value from a section is possible"""
1919
branch = self.make_branch('.')
1920
tree_config = config.TreeConfig(branch)
1921
tree_config.set_option('value', 'key', 'SECTION')
1922
tree_config.set_option('value2', 'key2')
1923
tree_config.set_option('value3-top', 'key3')
1924
tree_config.set_option('value3-section', 'key3', 'SECTION')
1925
value = tree_config.get_option('key', 'SECTION')
1926
self.assertEqual(value, 'value')
1927
value = tree_config.get_option('key2')
1928
self.assertEqual(value, 'value2')
1929
self.assertEqual(tree_config.get_option('non-existant'), None)
1930
value = tree_config.get_option('non-existant', 'SECTION')
1931
self.assertEqual(value, None)
1932
value = tree_config.get_option('non-existant', default='default')
1933
self.assertEqual(value, 'default')
1934
self.assertEqual(tree_config.get_option('key2', 'NOSECTION'), None)
1935
value = tree_config.get_option('key2', 'NOSECTION', default='default')
1936
self.assertEqual(value, 'default')
1937
value = tree_config.get_option('key3')
1938
self.assertEqual(value, 'value3-top')
1939
value = tree_config.get_option('key3', 'SECTION')
1940
self.assertEqual(value, 'value3-section')
1943
class TestTransportConfig(tests.TestCaseWithTransport):
1945
def test_load_utf8(self):
1946
"""Ensure we can load an utf8-encoded file."""
1947
t = self.get_transport()
1948
unicode_user = u'b\N{Euro Sign}ar'
1949
unicode_content = u'user=%s' % (unicode_user,)
1950
utf8_content = unicode_content.encode('utf8')
1951
# Store the raw content in the config file
1952
t.put_bytes('foo.conf', utf8_content)
1953
conf = config.TransportConfig(t, 'foo.conf')
1954
self.assertEquals(unicode_user, conf.get_option('user'))
1956
def test_load_non_ascii(self):
1957
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
1958
t = self.get_transport()
1959
t.put_bytes('foo.conf', 'user=foo\n#\xff\n')
1960
conf = config.TransportConfig(t, 'foo.conf')
1961
self.assertRaises(errors.ConfigContentError, conf._get_configobj)
1963
def test_load_erroneous_content(self):
1964
"""Ensure we display a proper error on content that can't be parsed."""
1965
t = self.get_transport()
1966
t.put_bytes('foo.conf', '[open_section\n')
1967
conf = config.TransportConfig(t, 'foo.conf')
1968
self.assertRaises(errors.ParseConfigError, conf._get_configobj)
1970
def test_load_permission_denied(self):
1971
"""Ensure we get an empty config file if the file is inaccessible."""
1974
warnings.append(args[0] % args[1:])
1975
self.overrideAttr(trace, 'warning', warning)
1977
class DenyingTransport(object):
1979
def __init__(self, base):
1982
def get_bytes(self, relpath):
1983
raise errors.PermissionDenied(relpath, "")
1985
cfg = config.TransportConfig(
1986
DenyingTransport("nonexisting://"), 'control.conf')
1987
self.assertIs(None, cfg.get_option('non-existant', 'SECTION'))
1990
[u'Permission denied while trying to open configuration file '
1991
u'nonexisting:///control.conf.'])
1993
def test_get_value(self):
1994
"""Test that retreiving a value from a section is possible"""
1995
bzrdir_config = config.TransportConfig(self.get_transport('.'),
1997
bzrdir_config.set_option('value', 'key', 'SECTION')
1998
bzrdir_config.set_option('value2', 'key2')
1999
bzrdir_config.set_option('value3-top', 'key3')
2000
bzrdir_config.set_option('value3-section', 'key3', 'SECTION')
2001
value = bzrdir_config.get_option('key', 'SECTION')
2002
self.assertEqual(value, 'value')
2003
value = bzrdir_config.get_option('key2')
2004
self.assertEqual(value, 'value2')
2005
self.assertEqual(bzrdir_config.get_option('non-existant'), None)
2006
value = bzrdir_config.get_option('non-existant', 'SECTION')
2007
self.assertEqual(value, None)
2008
value = bzrdir_config.get_option('non-existant', default='default')
2009
self.assertEqual(value, 'default')
2010
self.assertEqual(bzrdir_config.get_option('key2', 'NOSECTION'), None)
2011
value = bzrdir_config.get_option('key2', 'NOSECTION',
2013
self.assertEqual(value, 'default')
2014
value = bzrdir_config.get_option('key3')
2015
self.assertEqual(value, 'value3-top')
2016
value = bzrdir_config.get_option('key3', 'SECTION')
2017
self.assertEqual(value, 'value3-section')
2019
def test_set_unset_default_stack_on(self):
2020
my_dir = self.make_bzrdir('.')
2021
bzrdir_config = config.BzrDirConfig(my_dir)
2022
self.assertIs(None, bzrdir_config.get_default_stack_on())
2023
bzrdir_config.set_default_stack_on('Foo')
2024
self.assertEqual('Foo', bzrdir_config._config.get_option(
2025
'default_stack_on'))
2026
self.assertEqual('Foo', bzrdir_config.get_default_stack_on())
2027
bzrdir_config.set_default_stack_on(None)
2028
self.assertIs(None, bzrdir_config.get_default_stack_on())
2031
class TestOldConfigHooks(tests.TestCaseWithTransport):
2034
super(TestOldConfigHooks, self).setUp()
2035
create_configs_with_file_option(self)
2037
def assertGetHook(self, conf, name, value):
2041
config.OldConfigHooks.install_named_hook('get', hook, None)
2043
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2044
self.assertLength(0, calls)
2045
actual_value = conf.get_user_option(name)
2046
self.assertEquals(value, actual_value)
2047
self.assertLength(1, calls)
2048
self.assertEquals((conf, name, value), calls[0])
2050
def test_get_hook_bazaar(self):
2051
self.assertGetHook(self.bazaar_config, 'file', 'bazaar')
2053
def test_get_hook_locations(self):
2054
self.assertGetHook(self.locations_config, 'file', 'locations')
2056
def test_get_hook_branch(self):
2057
# Since locations masks branch, we define a different option
2058
self.branch_config.set_user_option('file2', 'branch')
2059
self.assertGetHook(self.branch_config, 'file2', 'branch')
2061
def assertSetHook(self, conf, name, value):
2065
config.OldConfigHooks.install_named_hook('set', hook, None)
2067
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2068
self.assertLength(0, calls)
2069
conf.set_user_option(name, value)
2070
self.assertLength(1, calls)
2071
# We can't assert the conf object below as different configs use
2072
# different means to implement set_user_option and we care only about
2074
self.assertEquals((name, value), calls[0][1:])
2076
def test_set_hook_bazaar(self):
2077
self.assertSetHook(self.bazaar_config, 'foo', 'bazaar')
2079
def test_set_hook_locations(self):
2080
self.assertSetHook(self.locations_config, 'foo', 'locations')
2082
def test_set_hook_branch(self):
2083
self.assertSetHook(self.branch_config, 'foo', 'branch')
2085
def assertRemoveHook(self, conf, name, section_name=None):
2089
config.OldConfigHooks.install_named_hook('remove', hook, None)
2091
config.OldConfigHooks.uninstall_named_hook, 'remove', None)
2092
self.assertLength(0, calls)
2093
conf.remove_user_option(name, section_name)
2094
self.assertLength(1, calls)
2095
# We can't assert the conf object below as different configs use
2096
# different means to implement remove_user_option and we care only about
2098
self.assertEquals((name,), calls[0][1:])
2100
def test_remove_hook_bazaar(self):
2101
self.assertRemoveHook(self.bazaar_config, 'file')
2103
def test_remove_hook_locations(self):
2104
self.assertRemoveHook(self.locations_config, 'file',
2105
self.locations_config.location)
2107
def test_remove_hook_branch(self):
2108
self.assertRemoveHook(self.branch_config, 'file')
2110
def assertLoadHook(self, name, conf_class, *conf_args):
2114
config.OldConfigHooks.install_named_hook('load', hook, None)
2116
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2117
self.assertLength(0, calls)
2119
conf = conf_class(*conf_args)
2120
# Access an option to trigger a load
2121
conf.get_user_option(name)
2122
self.assertLength(1, calls)
2123
# Since we can't assert about conf, we just use the number of calls ;-/
2125
def test_load_hook_bazaar(self):
2126
self.assertLoadHook('file', config.GlobalConfig)
2128
def test_load_hook_locations(self):
2129
self.assertLoadHook('file', config.LocationConfig, self.tree.basedir)
2131
def test_load_hook_branch(self):
2132
self.assertLoadHook('file', config.BranchConfig, self.tree.branch)
2134
def assertSaveHook(self, conf):
2138
config.OldConfigHooks.install_named_hook('save', hook, None)
2140
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2141
self.assertLength(0, calls)
2142
# Setting an option triggers a save
2143
conf.set_user_option('foo', 'bar')
2144
self.assertLength(1, calls)
2145
# Since we can't assert about conf, we just use the number of calls ;-/
2147
def test_save_hook_bazaar(self):
2148
self.assertSaveHook(self.bazaar_config)
2150
def test_save_hook_locations(self):
2151
self.assertSaveHook(self.locations_config)
2153
def test_save_hook_branch(self):
2154
self.assertSaveHook(self.branch_config)
2157
class TestOldConfigHooksForRemote(tests.TestCaseWithTransport):
2158
"""Tests config hooks for remote configs.
2160
No tests for the remove hook as this is not implemented there.
2164
super(TestOldConfigHooksForRemote, self).setUp()
2165
self.transport_server = test_server.SmartTCPServer_for_testing
2166
create_configs_with_file_option(self)
2168
def assertGetHook(self, conf, name, value):
2172
config.OldConfigHooks.install_named_hook('get', hook, None)
2174
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2175
self.assertLength(0, calls)
2176
actual_value = conf.get_option(name)
2177
self.assertEquals(value, actual_value)
2178
self.assertLength(1, calls)
2179
self.assertEquals((conf, name, value), calls[0])
2181
def test_get_hook_remote_branch(self):
2182
remote_branch = branch.Branch.open(self.get_url('tree'))
2183
self.assertGetHook(remote_branch._get_config(), 'file', 'branch')
2185
def test_get_hook_remote_bzrdir(self):
2186
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2187
conf = remote_bzrdir._get_config()
2188
conf.set_option('remotedir', 'file')
2189
self.assertGetHook(conf, 'file', 'remotedir')
2191
def assertSetHook(self, conf, name, value):
2195
config.OldConfigHooks.install_named_hook('set', hook, None)
2197
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2198
self.assertLength(0, calls)
2199
conf.set_option(value, name)
2200
self.assertLength(1, calls)
2201
# We can't assert the conf object below as different configs use
2202
# different means to implement set_user_option and we care only about
2204
self.assertEquals((name, value), calls[0][1:])
2206
def test_set_hook_remote_branch(self):
2207
remote_branch = branch.Branch.open(self.get_url('tree'))
2208
self.addCleanup(remote_branch.lock_write().unlock)
2209
self.assertSetHook(remote_branch._get_config(), 'file', 'remote')
2211
def test_set_hook_remote_bzrdir(self):
2212
remote_branch = branch.Branch.open(self.get_url('tree'))
2213
self.addCleanup(remote_branch.lock_write().unlock)
2214
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2215
self.assertSetHook(remote_bzrdir._get_config(), 'file', 'remotedir')
2217
def assertLoadHook(self, expected_nb_calls, name, conf_class, *conf_args):
2221
config.OldConfigHooks.install_named_hook('load', hook, None)
2223
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2224
self.assertLength(0, calls)
2226
conf = conf_class(*conf_args)
2227
# Access an option to trigger a load
2228
conf.get_option(name)
2229
self.assertLength(expected_nb_calls, calls)
2230
# Since we can't assert about conf, we just use the number of calls ;-/
2232
def test_load_hook_remote_branch(self):
2233
remote_branch = branch.Branch.open(self.get_url('tree'))
2234
self.assertLoadHook(1, 'file', remote.RemoteBranchConfig, remote_branch)
2236
def test_load_hook_remote_bzrdir(self):
2237
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2238
# The config file doesn't exist, set an option to force its creation
2239
conf = remote_bzrdir._get_config()
2240
conf.set_option('remotedir', 'file')
2241
# We get one call for the server and one call for the client, this is
2242
# caused by the differences in implementations betwen
2243
# SmartServerBzrDirRequestConfigFile (in smart/bzrdir.py) and
2244
# SmartServerBranchGetConfigFile (in smart/branch.py)
2245
self.assertLoadHook(2 ,'file', remote.RemoteBzrDirConfig, remote_bzrdir)
2247
def assertSaveHook(self, conf):
2251
config.OldConfigHooks.install_named_hook('save', hook, None)
2253
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2254
self.assertLength(0, calls)
2255
# Setting an option triggers a save
2256
conf.set_option('foo', 'bar')
2257
self.assertLength(1, calls)
2258
# Since we can't assert about conf, we just use the number of calls ;-/
2260
def test_save_hook_remote_branch(self):
2261
remote_branch = branch.Branch.open(self.get_url('tree'))
2262
self.addCleanup(remote_branch.lock_write().unlock)
2263
self.assertSaveHook(remote_branch._get_config())
2265
def test_save_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.assertSaveHook(remote_bzrdir._get_config())
2272
class TestOption(tests.TestCase):
2274
def test_default_value(self):
2275
opt = config.Option('foo', default='bar')
2276
self.assertEquals('bar', opt.get_default())
2278
def test_default_value_from_env(self):
2279
opt = config.Option('foo', default='bar', default_from_env=['FOO'])
2280
self.overrideEnv('FOO', 'quux')
2281
# Env variable provides a default taking over the option one
2282
self.assertEquals('quux', opt.get_default())
2284
def test_first_default_value_from_env_wins(self):
2285
opt = config.Option('foo', default='bar',
2286
default_from_env=['NO_VALUE', 'FOO', 'BAZ'])
2287
self.overrideEnv('FOO', 'foo')
2288
self.overrideEnv('BAZ', 'baz')
2289
# The first env var set wins
2290
self.assertEquals('foo', opt.get_default())
2292
def test_not_supported_list_default_value(self):
2293
self.assertRaises(AssertionError, config.Option, 'foo', default=[1])
2295
def test_not_supported_object_default_value(self):
2296
self.assertRaises(AssertionError, config.Option, 'foo',
2300
class TestOptionConverterMixin(object):
2302
def assertConverted(self, expected, opt, value):
2303
self.assertEquals(expected, opt.convert_from_unicode(value))
2305
def assertWarns(self, opt, value):
2308
warnings.append(args[0] % args[1:])
2309
self.overrideAttr(trace, 'warning', warning)
2310
self.assertEquals(None, opt.convert_from_unicode(value))
2311
self.assertLength(1, warnings)
2313
'Value "%s" is not valid for "%s"' % (value, opt.name),
2316
def assertErrors(self, opt, value):
2317
self.assertRaises(errors.ConfigOptionValueError,
2318
opt.convert_from_unicode, value)
2320
def assertConvertInvalid(self, opt, invalid_value):
2322
self.assertEquals(None, opt.convert_from_unicode(invalid_value))
2323
opt.invalid = 'warning'
2324
self.assertWarns(opt, invalid_value)
2325
opt.invalid = 'error'
2326
self.assertErrors(opt, invalid_value)
2329
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2331
def get_option(self):
2332
return config.Option('foo', help='A boolean.',
2333
from_unicode=config.bool_from_store)
2335
def test_convert_invalid(self):
2336
opt = self.get_option()
2337
# A string that is not recognized as a boolean
2338
self.assertConvertInvalid(opt, u'invalid-boolean')
2339
# A list of strings is never recognized as a boolean
2340
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2342
def test_convert_valid(self):
2343
opt = self.get_option()
2344
self.assertConverted(True, opt, u'True')
2345
self.assertConverted(True, opt, u'1')
2346
self.assertConverted(False, opt, u'False')
2349
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2351
def get_option(self):
2352
return config.Option('foo', help='An integer.',
2353
from_unicode=config.int_from_store)
2355
def test_convert_invalid(self):
2356
opt = self.get_option()
2357
# A string that is not recognized as an integer
2358
self.assertConvertInvalid(opt, u'forty-two')
2359
# A list of strings is never recognized as an integer
2360
self.assertConvertInvalid(opt, [u'a', u'list'])
2362
def test_convert_valid(self):
2363
opt = self.get_option()
2364
self.assertConverted(16, opt, u'16')
2366
class TestOptionWithListConverter(tests.TestCase, TestOptionConverterMixin):
2368
def get_option(self):
2369
return config.Option('foo', help='A list.',
2370
from_unicode=config.list_from_store)
2372
def test_convert_invalid(self):
2373
# No string is invalid as all forms can be converted to a list
2376
def test_convert_valid(self):
2377
opt = self.get_option()
2378
# An empty string is an empty list
2379
self.assertConverted([], opt, '') # Using a bare str() just in case
2380
self.assertConverted([], opt, u'')
2382
self.assertConverted([u'True'], opt, u'True')
2384
self.assertConverted([u'42'], opt, u'42')
2386
self.assertConverted([u'bar'], opt, u'bar')
2387
# A list remains a list (configObj will turn a string containing commas
2388
# into a list, but that's not what we're testing here)
2389
self.assertConverted([u'foo', u'1', u'True'],
2390
opt, [u'foo', u'1', u'True'])
2393
class TestOptionConverterMixin(object):
2395
def assertConverted(self, expected, opt, value):
2396
self.assertEquals(expected, opt.convert_from_unicode(value))
2398
def assertWarns(self, opt, value):
2401
warnings.append(args[0] % args[1:])
2402
self.overrideAttr(trace, 'warning', warning)
2403
self.assertEquals(None, opt.convert_from_unicode(value))
2404
self.assertLength(1, warnings)
2406
'Value "%s" is not valid for "%s"' % (value, opt.name),
2409
def assertErrors(self, opt, value):
2410
self.assertRaises(errors.ConfigOptionValueError,
2411
opt.convert_from_unicode, value)
2413
def assertConvertInvalid(self, opt, invalid_value):
2415
self.assertEquals(None, opt.convert_from_unicode(invalid_value))
2416
opt.invalid = 'warning'
2417
self.assertWarns(opt, invalid_value)
2418
opt.invalid = 'error'
2419
self.assertErrors(opt, invalid_value)
2422
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2424
def get_option(self):
2425
return config.Option('foo', help='A boolean.',
2426
from_unicode=config.bool_from_store)
2428
def test_convert_invalid(self):
2429
opt = self.get_option()
2430
# A string that is not recognized as a boolean
2431
self.assertConvertInvalid(opt, u'invalid-boolean')
2432
# A list of strings is never recognized as a boolean
2433
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2435
def test_convert_valid(self):
2436
opt = self.get_option()
2437
self.assertConverted(True, opt, u'True')
2438
self.assertConverted(True, opt, u'1')
2439
self.assertConverted(False, opt, u'False')
2442
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2444
def get_option(self):
2445
return config.Option('foo', help='An integer.',
2446
from_unicode=config.int_from_store)
2448
def test_convert_invalid(self):
2449
opt = self.get_option()
2450
# A string that is not recognized as an integer
2451
self.assertConvertInvalid(opt, u'forty-two')
2452
# A list of strings is never recognized as an integer
2453
self.assertConvertInvalid(opt, [u'a', u'list'])
2455
def test_convert_valid(self):
2456
opt = self.get_option()
2457
self.assertConverted(16, opt, u'16')
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
opt = self.get_option()
2468
# We don't even try to convert a list into a list, we only expect
2470
self.assertConvertInvalid(opt, [1])
2471
# No string is invalid as all forms can be converted to a list
2473
def test_convert_valid(self):
2474
opt = self.get_option()
2475
# An empty string is an empty list
2476
self.assertConverted([], opt, '') # Using a bare str() just in case
2477
self.assertConverted([], opt, u'')
2479
self.assertConverted([u'True'], opt, u'True')
2481
self.assertConverted([u'42'], opt, u'42')
2483
self.assertConverted([u'bar'], opt, u'bar')
2486
class TestOptionRegistry(tests.TestCase):
2489
super(TestOptionRegistry, self).setUp()
2490
# Always start with an empty registry
2491
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2492
self.registry = config.option_registry
2494
def test_register(self):
2495
opt = config.Option('foo')
2496
self.registry.register(opt)
2497
self.assertIs(opt, self.registry.get('foo'))
2499
def test_registered_help(self):
2500
opt = config.Option('foo', help='A simple option')
2501
self.registry.register(opt)
2502
self.assertEquals('A simple option', self.registry.get_help('foo'))
2504
lazy_option = config.Option('lazy_foo', help='Lazy help')
2506
def test_register_lazy(self):
2507
self.registry.register_lazy('lazy_foo', self.__module__,
2508
'TestOptionRegistry.lazy_option')
2509
self.assertIs(self.lazy_option, self.registry.get('lazy_foo'))
2511
def test_registered_lazy_help(self):
2512
self.registry.register_lazy('lazy_foo', self.__module__,
2513
'TestOptionRegistry.lazy_option')
2514
self.assertEquals('Lazy help', self.registry.get_help('lazy_foo'))
2517
class TestRegisteredOptions(tests.TestCase):
2518
"""All registered options should verify some constraints."""
2520
scenarios = [(key, {'option_name': key, 'option': option}) for key, option
2521
in config.option_registry.iteritems()]
2524
super(TestRegisteredOptions, self).setUp()
2525
self.registry = config.option_registry
2527
def test_proper_name(self):
2528
# An option should be registered under its own name, this can't be
2529
# checked at registration time for the lazy ones.
2530
self.assertEquals(self.option_name, self.option.name)
2532
def test_help_is_set(self):
2533
option_help = self.registry.get_help(self.option_name)
2534
self.assertNotEquals(None, option_help)
2535
# Come on, think about the user, he really wants to know what the
2537
self.assertIsNot(None, option_help)
2538
self.assertNotEquals('', option_help)
2541
class TestSection(tests.TestCase):
2543
# FIXME: Parametrize so that all sections produced by Stores run these
2544
# tests -- vila 2011-04-01
2546
def test_get_a_value(self):
2547
a_dict = dict(foo='bar')
2548
section = config.Section('myID', a_dict)
2549
self.assertEquals('bar', section.get('foo'))
2551
def test_get_unknown_option(self):
2553
section = config.Section(None, a_dict)
2554
self.assertEquals('out of thin air',
2555
section.get('foo', 'out of thin air'))
2557
def test_options_is_shared(self):
2559
section = config.Section(None, a_dict)
2560
self.assertIs(a_dict, section.options)
2563
class TestMutableSection(tests.TestCase):
2565
scenarios = [('mutable',
2567
lambda opts: config.MutableSection('myID', opts)},),
2570
lambda opts: config.CommandLineSection(opts)},),
2574
a_dict = dict(foo='bar')
2575
section = self.get_section(a_dict)
2576
section.set('foo', 'new_value')
2577
self.assertEquals('new_value', section.get('foo'))
2578
# The change appears in the shared section
2579
self.assertEquals('new_value', a_dict.get('foo'))
2580
# We keep track of the change
2581
self.assertTrue('foo' in section.orig)
2582
self.assertEquals('bar', section.orig.get('foo'))
2584
def test_set_preserve_original_once(self):
2585
a_dict = dict(foo='bar')
2586
section = self.get_section(a_dict)
2587
section.set('foo', 'first_value')
2588
section.set('foo', 'second_value')
2589
# We keep track of the original value
2590
self.assertTrue('foo' in section.orig)
2591
self.assertEquals('bar', section.orig.get('foo'))
2593
def test_remove(self):
2594
a_dict = dict(foo='bar')
2595
section = self.get_section(a_dict)
2596
section.remove('foo')
2597
# We get None for unknown options via the default value
2598
self.assertEquals(None, section.get('foo'))
2599
# Or we just get the default value
2600
self.assertEquals('unknown', section.get('foo', 'unknown'))
2601
self.assertFalse('foo' in section.options)
2602
# We keep track of the deletion
2603
self.assertTrue('foo' in section.orig)
2604
self.assertEquals('bar', section.orig.get('foo'))
2606
def test_remove_new_option(self):
2608
section = self.get_section(a_dict)
2609
section.set('foo', 'bar')
2610
section.remove('foo')
2611
self.assertFalse('foo' in section.options)
2612
# The option didn't exist initially so it we need to keep track of it
2613
# with a special value
2614
self.assertTrue('foo' in section.orig)
2615
self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
2618
class TestCommandLineSection(tests.TestCase):
2621
super(TestCommandLineSection, self).setUp()
2622
self.section = config.CommandLineSection()
2624
def test_no_override(self):
2625
self.section._from_cmdline([])
2626
# FIXME: we want some iterator over all options, failing that, we peek
2627
# under the cover -- vila 2011-09026
2628
self.assertLength(0, self.section.options)
2630
def test_simple_override(self):
2631
self.section._from_cmdline(['a=b'])
2632
self.assertEqual('b', self.section.get('a'))
2634
def test_list_override(self):
2635
self.section._from_cmdline(['l=1,2,3'])
2636
val = self.section.get('l')
2637
self.assertEqual('1,2,3', val)
2638
# Reminder: lists should registered as such explicitely, otherwise the
2639
# conversion needs to be done afterwards.
2640
self.assertEqual(['1', '2', '3'], config.list_from_store(val))
2642
def test_multiple_overrides(self):
2643
self.section._from_cmdline(['a=b', 'x=y'])
2644
self.assertEquals('b', self.section.get('a'))
2645
self.assertEquals('y', self.section.get('x'))
2647
def test_wrong_syntax(self):
2648
self.assertRaises(errors.BzrCommandError,
2649
self.section._from_cmdline, ['a=b', 'c'])
2652
class TestStore(tests.TestCaseWithTransport):
2654
def assertSectionContent(self, expected, section):
2655
"""Assert that some options have the proper values in a section."""
2656
expected_name, expected_options = expected
2657
self.assertEquals(expected_name, section.id)
2660
dict([(k, section.get(k)) for k in expected_options.keys()]))
2663
class TestReadonlyStore(TestStore):
2665
scenarios = [(key, {'get_store': builder}) for key, builder
2666
in config.test_store_builder_registry.iteritems()]
2668
def test_building_delays_load(self):
2669
store = self.get_store(self)
2670
self.assertEquals(False, store.is_loaded())
2671
store._load_from_string('')
2672
self.assertEquals(True, store.is_loaded())
2674
def test_get_no_sections_for_empty(self):
2675
store = self.get_store(self)
2676
store._load_from_string('')
2677
self.assertEquals([], list(store.get_sections()))
2679
def test_get_default_section(self):
2680
store = self.get_store(self)
2681
store._load_from_string('foo=bar')
2682
sections = list(store.get_sections())
2683
self.assertLength(1, sections)
2684
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2686
def test_get_named_section(self):
2687
store = self.get_store(self)
2688
store._load_from_string('[baz]\nfoo=bar')
2689
sections = list(store.get_sections())
2690
self.assertLength(1, sections)
2691
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2693
def test_load_from_string_fails_for_non_empty_store(self):
2694
store = self.get_store(self)
2695
store._load_from_string('foo=bar')
2696
self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2699
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2700
"""Simulate loading a config store with content of various encodings.
2702
All files produced by bzr are in utf8 content.
2704
Users may modify them manually and end up with a file that can't be
2705
loaded. We need to issue proper error messages in this case.
2708
invalid_utf8_char = '\xff'
2710
def test_load_utf8(self):
2711
"""Ensure we can load an utf8-encoded file."""
2712
t = self.get_transport()
2713
# From http://pad.lv/799212
2714
unicode_user = u'b\N{Euro Sign}ar'
2715
unicode_content = u'user=%s' % (unicode_user,)
2716
utf8_content = unicode_content.encode('utf8')
2717
# Store the raw content in the config file
2718
t.put_bytes('foo.conf', utf8_content)
2719
store = config.IniFileStore(t, 'foo.conf')
2721
stack = config.Stack([store.get_sections], store)
2722
self.assertEquals(unicode_user, stack.get('user'))
2724
def test_load_non_ascii(self):
2725
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2726
t = self.get_transport()
2727
t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2728
store = config.IniFileStore(t, 'foo.conf')
2729
self.assertRaises(errors.ConfigContentError, store.load)
2731
def test_load_erroneous_content(self):
2732
"""Ensure we display a proper error on content that can't be parsed."""
2733
t = self.get_transport()
2734
t.put_bytes('foo.conf', '[open_section\n')
2735
store = config.IniFileStore(t, 'foo.conf')
2736
self.assertRaises(errors.ParseConfigError, store.load)
2738
def test_load_permission_denied(self):
2739
"""Ensure we get warned when trying to load an inaccessible file."""
2742
warnings.append(args[0] % args[1:])
2743
self.overrideAttr(trace, 'warning', warning)
2745
t = self.get_transport()
2747
def get_bytes(relpath):
2748
raise errors.PermissionDenied(relpath, "")
2749
t.get_bytes = get_bytes
2750
store = config.IniFileStore(t, 'foo.conf')
2751
self.assertRaises(errors.PermissionDenied, store.load)
2754
[u'Permission denied while trying to load configuration store %s.'
2755
% store.external_url()])
2758
class TestIniConfigContent(tests.TestCaseWithTransport):
2759
"""Simulate loading a IniBasedConfig with content of various encodings.
2761
All files produced by bzr are in utf8 content.
2763
Users may modify them manually and end up with a file that can't be
2764
loaded. We need to issue proper error messages in this case.
2767
invalid_utf8_char = '\xff'
2769
def test_load_utf8(self):
2770
"""Ensure we can load an utf8-encoded file."""
2771
# From http://pad.lv/799212
2772
unicode_user = u'b\N{Euro Sign}ar'
2773
unicode_content = u'user=%s' % (unicode_user,)
2774
utf8_content = unicode_content.encode('utf8')
2775
# Store the raw content in the config file
2776
with open('foo.conf', 'wb') as f:
2777
f.write(utf8_content)
2778
conf = config.IniBasedConfig(file_name='foo.conf')
2779
self.assertEquals(unicode_user, conf.get_user_option('user'))
2781
def test_load_badly_encoded_content(self):
2782
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2783
with open('foo.conf', 'wb') as f:
2784
f.write('user=foo\n#%s\n' % (self.invalid_utf8_char,))
2785
conf = config.IniBasedConfig(file_name='foo.conf')
2786
self.assertRaises(errors.ConfigContentError, conf._get_parser)
2788
def test_load_erroneous_content(self):
2789
"""Ensure we display a proper error on content that can't be parsed."""
2790
with open('foo.conf', 'wb') as f:
2791
f.write('[open_section\n')
2792
conf = config.IniBasedConfig(file_name='foo.conf')
2793
self.assertRaises(errors.ParseConfigError, conf._get_parser)
2796
class TestMutableStore(TestStore):
2798
scenarios = [(key, {'store_id': key, 'get_store': builder}) for key, builder
2799
in config.test_store_builder_registry.iteritems()]
2802
super(TestMutableStore, self).setUp()
2803
self.transport = self.get_transport()
2805
def has_store(self, store):
2806
store_basename = urlutils.relative_url(self.transport.external_url(),
2807
store.external_url())
2808
return self.transport.has(store_basename)
2810
def test_save_empty_creates_no_file(self):
2811
# FIXME: There should be a better way than relying on the test
2812
# parametrization to identify branch.conf -- vila 2011-0526
2813
if self.store_id in ('branch', 'remote_branch'):
2814
raise tests.TestNotApplicable(
2815
'branch.conf is *always* created when a branch is initialized')
2816
store = self.get_store(self)
2818
self.assertEquals(False, self.has_store(store))
2820
def test_save_emptied_succeeds(self):
2821
store = self.get_store(self)
2822
store._load_from_string('foo=bar\n')
2823
section = store.get_mutable_section(None)
2824
section.remove('foo')
2826
self.assertEquals(True, self.has_store(store))
2827
modified_store = self.get_store(self)
2828
sections = list(modified_store.get_sections())
2829
self.assertLength(0, sections)
2831
def test_save_with_content_succeeds(self):
2832
# FIXME: There should be a better way than relying on the test
2833
# parametrization to identify branch.conf -- vila 2011-0526
2834
if self.store_id in ('branch', 'remote_branch'):
2835
raise tests.TestNotApplicable(
2836
'branch.conf is *always* created when a branch is initialized')
2837
store = self.get_store(self)
2838
store._load_from_string('foo=bar\n')
2839
self.assertEquals(False, self.has_store(store))
2841
self.assertEquals(True, self.has_store(store))
2842
modified_store = self.get_store(self)
2843
sections = list(modified_store.get_sections())
2844
self.assertLength(1, sections)
2845
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2847
def test_set_option_in_empty_store(self):
2848
store = self.get_store(self)
2849
section = store.get_mutable_section(None)
2850
section.set('foo', 'bar')
2852
modified_store = self.get_store(self)
2853
sections = list(modified_store.get_sections())
2854
self.assertLength(1, sections)
2855
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2857
def test_set_option_in_default_section(self):
2858
store = self.get_store(self)
2859
store._load_from_string('')
2860
section = store.get_mutable_section(None)
2861
section.set('foo', 'bar')
2863
modified_store = self.get_store(self)
2864
sections = list(modified_store.get_sections())
2865
self.assertLength(1, sections)
2866
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2868
def test_set_option_in_named_section(self):
2869
store = self.get_store(self)
2870
store._load_from_string('')
2871
section = store.get_mutable_section('baz')
2872
section.set('foo', 'bar')
2874
modified_store = self.get_store(self)
2875
sections = list(modified_store.get_sections())
2876
self.assertLength(1, sections)
2877
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2879
def test_load_hook(self):
2880
# We first needs to ensure that the store exists
2881
store = self.get_store(self)
2882
section = store.get_mutable_section('baz')
2883
section.set('foo', 'bar')
2885
# Now we can try to load it
2886
store = self.get_store(self)
2890
config.ConfigHooks.install_named_hook('load', hook, None)
2891
self.assertLength(0, calls)
2893
self.assertLength(1, calls)
2894
self.assertEquals((store,), calls[0])
2896
def test_save_hook(self):
2900
config.ConfigHooks.install_named_hook('save', hook, None)
2901
self.assertLength(0, calls)
2902
store = self.get_store(self)
2903
section = store.get_mutable_section('baz')
2904
section.set('foo', 'bar')
2906
self.assertLength(1, calls)
2907
self.assertEquals((store,), calls[0])
2910
class TestIniFileStore(TestStore):
2912
def test_loading_unknown_file_fails(self):
2913
store = config.IniFileStore(self.get_transport(), 'I-do-not-exist')
2914
self.assertRaises(errors.NoSuchFile, store.load)
2916
def test_invalid_content(self):
2917
store = config.IniFileStore(self.get_transport(), 'foo.conf', )
2918
self.assertEquals(False, store.is_loaded())
2919
exc = self.assertRaises(
2920
errors.ParseConfigError, store._load_from_string,
2921
'this is invalid !')
2922
self.assertEndsWith(exc.filename, 'foo.conf')
2923
# And the load failed
2924
self.assertEquals(False, store.is_loaded())
2926
def test_get_embedded_sections(self):
2927
# A more complicated example (which also shows that section names and
2928
# option names share the same name space...)
2929
# FIXME: This should be fixed by forbidding dicts as values ?
2930
# -- vila 2011-04-05
2931
store = config.IniFileStore(self.get_transport(), 'foo.conf', )
2932
store._load_from_string('''
2936
foo_in_DEFAULT=foo_DEFAULT
2944
sections = list(store.get_sections())
2945
self.assertLength(4, sections)
2946
# The default section has no name.
2947
# List values are provided as strings and need to be explicitly
2948
# converted by specifying from_unicode=list_from_store at option
2950
self.assertSectionContent((None, {'foo': 'bar', 'l': u'1,2'}),
2952
self.assertSectionContent(
2953
('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
2954
self.assertSectionContent(
2955
('bar', {'foo_in_bar': 'barbar'}), sections[2])
2956
# sub sections are provided as embedded dicts.
2957
self.assertSectionContent(
2958
('baz', {'foo_in_baz': 'barbaz', 'qux': {'foo_in_qux': 'quux'}}),
2962
class TestLockableIniFileStore(TestStore):
2964
def test_create_store_in_created_dir(self):
2965
self.assertPathDoesNotExist('dir')
2966
t = self.get_transport('dir/subdir')
2967
store = config.LockableIniFileStore(t, 'foo.conf')
2968
store.get_mutable_section(None).set('foo', 'bar')
2970
self.assertPathExists('dir/subdir')
2973
class TestConcurrentStoreUpdates(TestStore):
2974
"""Test that Stores properly handle conccurent updates.
2976
New Store implementation may fail some of these tests but until such
2977
implementations exist it's hard to properly filter them from the scenarios
2978
applied here. If you encounter such a case, contact the bzr devs.
2981
scenarios = [(key, {'get_stack': builder}) for key, builder
2982
in config.test_stack_builder_registry.iteritems()]
2985
super(TestConcurrentStoreUpdates, self).setUp()
2986
self._content = 'one=1\ntwo=2\n'
2987
self.stack = self.get_stack(self)
2988
if not isinstance(self.stack, config._CompatibleStack):
2989
raise tests.TestNotApplicable(
2990
'%s is not meant to be compatible with the old config design'
2992
self.stack.store._load_from_string(self._content)
2994
self.stack.store.save()
2996
def test_simple_read_access(self):
2997
self.assertEquals('1', self.stack.get('one'))
2999
def test_simple_write_access(self):
3000
self.stack.set('one', 'one')
3001
self.assertEquals('one', self.stack.get('one'))
3003
def test_listen_to_the_last_speaker(self):
3005
c2 = self.get_stack(self)
3006
c1.set('one', 'ONE')
3007
c2.set('two', 'TWO')
3008
self.assertEquals('ONE', c1.get('one'))
3009
self.assertEquals('TWO', c2.get('two'))
3010
# The second update respect the first one
3011
self.assertEquals('ONE', c2.get('one'))
3013
def test_last_speaker_wins(self):
3014
# If the same config is not shared, the same variable modified twice
3015
# can only see a single result.
3017
c2 = self.get_stack(self)
3020
self.assertEquals('c2', c2.get('one'))
3021
# The first modification is still available until another refresh
3023
self.assertEquals('c1', c1.get('one'))
3024
c1.set('two', 'done')
3025
self.assertEquals('c2', c1.get('one'))
3027
def test_writes_are_serialized(self):
3029
c2 = self.get_stack(self)
3031
# We spawn a thread that will pause *during* the config saving.
3032
before_writing = threading.Event()
3033
after_writing = threading.Event()
3034
writing_done = threading.Event()
3035
c1_save_without_locking_orig = c1.store.save_without_locking
3036
def c1_save_without_locking():
3037
before_writing.set()
3038
c1_save_without_locking_orig()
3039
# The lock is held. We wait for the main thread to decide when to
3041
after_writing.wait()
3042
c1.store.save_without_locking = c1_save_without_locking
3046
t1 = threading.Thread(target=c1_set)
3047
# Collect the thread after the test
3048
self.addCleanup(t1.join)
3049
# Be ready to unblock the thread if the test goes wrong
3050
self.addCleanup(after_writing.set)
3052
before_writing.wait()
3053
self.assertRaises(errors.LockContention,
3054
c2.set, 'one', 'c2')
3055
self.assertEquals('c1', c1.get('one'))
3056
# Let the lock be released
3060
self.assertEquals('c2', c2.get('one'))
3062
def test_read_while_writing(self):
3064
# We spawn a thread that will pause *during* the write
3065
ready_to_write = threading.Event()
3066
do_writing = threading.Event()
3067
writing_done = threading.Event()
3068
# We override the _save implementation so we know the store is locked
3069
c1_save_without_locking_orig = c1.store.save_without_locking
3070
def c1_save_without_locking():
3071
ready_to_write.set()
3072
# The lock is held. We wait for the main thread to decide when to
3075
c1_save_without_locking_orig()
3077
c1.store.save_without_locking = c1_save_without_locking
3080
t1 = threading.Thread(target=c1_set)
3081
# Collect the thread after the test
3082
self.addCleanup(t1.join)
3083
# Be ready to unblock the thread if the test goes wrong
3084
self.addCleanup(do_writing.set)
3086
# Ensure the thread is ready to write
3087
ready_to_write.wait()
3088
self.assertEquals('c1', c1.get('one'))
3089
# If we read during the write, we get the old value
3090
c2 = self.get_stack(self)
3091
self.assertEquals('1', c2.get('one'))
3092
# Let the writing occur and ensure it occurred
3095
# Now we get the updated value
3096
c3 = self.get_stack(self)
3097
self.assertEquals('c1', c3.get('one'))
3099
# FIXME: It may be worth looking into removing the lock dir when it's not
3100
# needed anymore and look at possible fallouts for concurrent lockers. This
3101
# will matter if/when we use config files outside of bazaar directories
3102
# (.bazaar or .bzr) -- vila 20110-04-111
3105
class TestSectionMatcher(TestStore):
3107
scenarios = [('location', {'matcher': config.LocationMatcher}),
3108
('id', {'matcher': config.NameMatcher}),]
3111
super(TestSectionMatcher, self).setUp()
3112
# Any simple store is good enough
3113
self.get_store = config.test_store_builder_registry.get('configobj')
3115
def test_no_matches_for_empty_stores(self):
3116
store = self.get_store(self)
3117
store._load_from_string('')
3118
matcher = self.matcher(store, '/bar')
3119
self.assertEquals([], list(matcher.get_sections()))
3121
def test_build_doesnt_load_store(self):
3122
store = self.get_store(self)
3123
matcher = self.matcher(store, '/bar')
3124
self.assertFalse(store.is_loaded())
3127
class TestLocationSection(tests.TestCase):
3129
def get_section(self, options, extra_path):
3130
section = config.Section('foo', options)
3131
# We don't care about the length so we use '0'
3132
return config.LocationSection(section, 0, extra_path)
3134
def test_simple_option(self):
3135
section = self.get_section({'foo': 'bar'}, '')
3136
self.assertEquals('bar', section.get('foo'))
3138
def test_option_with_extra_path(self):
3139
section = self.get_section({'foo': 'bar', 'foo:policy': 'appendpath'},
3141
self.assertEquals('bar/baz', section.get('foo'))
3143
def test_invalid_policy(self):
3144
section = self.get_section({'foo': 'bar', 'foo:policy': 'die'},
3146
# invalid policies are ignored
3147
self.assertEquals('bar', section.get('foo'))
3150
class TestLocationMatcher(TestStore):
3153
super(TestLocationMatcher, self).setUp()
3154
# Any simple store is good enough
3155
self.get_store = config.test_store_builder_registry.get('configobj')
3157
def test_unrelated_section_excluded(self):
3158
store = self.get_store(self)
3159
store._load_from_string('''
3167
section=/foo/bar/baz
3171
self.assertEquals(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
3173
[section.id for section in store.get_sections()])
3174
matcher = config.LocationMatcher(store, '/foo/bar/quux')
3175
sections = list(matcher.get_sections())
3176
self.assertEquals([3, 2],
3177
[section.length for section in sections])
3178
self.assertEquals(['/foo/bar', '/foo'],
3179
[section.id for section in sections])
3180
self.assertEquals(['quux', 'bar/quux'],
3181
[section.extra_path for section in sections])
3183
def test_more_specific_sections_first(self):
3184
store = self.get_store(self)
3185
store._load_from_string('''
3191
self.assertEquals(['/foo', '/foo/bar'],
3192
[section.id for section in store.get_sections()])
3193
matcher = config.LocationMatcher(store, '/foo/bar/baz')
3194
sections = list(matcher.get_sections())
3195
self.assertEquals([3, 2],
3196
[section.length for section in sections])
3197
self.assertEquals(['/foo/bar', '/foo'],
3198
[section.id for section in sections])
3199
self.assertEquals(['baz', 'bar/baz'],
3200
[section.extra_path for section in sections])
3202
def test_appendpath_in_no_name_section(self):
3203
# It's a bit weird to allow appendpath in a no-name section, but
3204
# someone may found a use for it
3205
store = self.get_store(self)
3206
store._load_from_string('''
3208
foo:policy = appendpath
3210
matcher = config.LocationMatcher(store, 'dir/subdir')
3211
sections = list(matcher.get_sections())
3212
self.assertLength(1, sections)
3213
self.assertEquals('bar/dir/subdir', sections[0].get('foo'))
3215
def test_file_urls_are_normalized(self):
3216
store = self.get_store(self)
3217
if sys.platform == 'win32':
3218
expected_url = 'file:///C:/dir/subdir'
3219
expected_location = 'C:/dir/subdir'
3221
expected_url = 'file:///dir/subdir'
3222
expected_location = '/dir/subdir'
3223
matcher = config.LocationMatcher(store, expected_url)
3224
self.assertEquals(expected_location, matcher.location)
3227
class TestNameMatcher(TestStore):
3230
super(TestNameMatcher, self).setUp()
3231
self.matcher = config.NameMatcher
3232
# Any simple store is good enough
3233
self.get_store = config.test_store_builder_registry.get('configobj')
3235
def get_matching_sections(self, name):
3236
store = self.get_store(self)
3237
store._load_from_string('''
3245
matcher = self.matcher(store, name)
3246
return list(matcher.get_sections())
3248
def test_matching(self):
3249
sections = self.get_matching_sections('foo')
3250
self.assertLength(1, sections)
3251
self.assertSectionContent(('foo', {'option': 'foo'}), sections[0])
3253
def test_not_matching(self):
3254
sections = self.get_matching_sections('baz')
3255
self.assertLength(0, sections)
3258
class TestStackGet(tests.TestCase):
3260
# FIXME: This should be parametrized for all known Stack or dedicated
3261
# paramerized tests created to avoid bloating -- vila 2011-03-31
3263
def overrideOptionRegistry(self):
3264
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3266
def test_single_config_get(self):
3267
conf = dict(foo='bar')
3268
conf_stack = config.Stack([conf])
3269
self.assertEquals('bar', conf_stack.get('foo'))
3271
def test_get_with_registered_default_value(self):
3272
conf_stack = config.Stack([dict()])
3273
opt = config.Option('foo', default='bar')
3274
self.overrideOptionRegistry()
3275
config.option_registry.register('foo', opt)
3276
self.assertEquals('bar', conf_stack.get('foo'))
3278
def test_get_without_registered_default_value(self):
3279
conf_stack = config.Stack([dict()])
3280
opt = config.Option('foo')
3281
self.overrideOptionRegistry()
3282
config.option_registry.register('foo', opt)
3283
self.assertEquals(None, conf_stack.get('foo'))
3285
def test_get_without_default_value_for_not_registered(self):
3286
conf_stack = config.Stack([dict()])
3287
opt = config.Option('foo')
3288
self.overrideOptionRegistry()
3289
self.assertEquals(None, conf_stack.get('foo'))
3291
def test_get_first_definition(self):
3292
conf1 = dict(foo='bar')
3293
conf2 = dict(foo='baz')
3294
conf_stack = config.Stack([conf1, conf2])
3295
self.assertEquals('bar', conf_stack.get('foo'))
3297
def test_get_embedded_definition(self):
3298
conf1 = dict(yy='12')
3299
conf2 = config.Stack([dict(xx='42'), dict(foo='baz')])
3300
conf_stack = config.Stack([conf1, conf2])
3301
self.assertEquals('baz', conf_stack.get('foo'))
3303
def test_get_for_empty_section_callable(self):
3304
conf_stack = config.Stack([lambda : []])
3305
self.assertEquals(None, conf_stack.get('foo'))
3307
def test_get_for_broken_callable(self):
3308
# Trying to use and invalid callable raises an exception on first use
3309
conf_stack = config.Stack([lambda : object()])
3310
self.assertRaises(TypeError, conf_stack.get, 'foo')
3313
class TestStackWithTransport(tests.TestCaseWithTransport):
3315
scenarios = [(key, {'get_stack': builder}) for key, builder
3316
in config.test_stack_builder_registry.iteritems()]
3319
class TestConcreteStacks(TestStackWithTransport):
3321
def test_build_stack(self):
3322
# Just a smoke test to help debug builders
3323
stack = self.get_stack(self)
3326
class TestStackGet(TestStackWithTransport):
3329
super(TestStackGet, self).setUp()
3330
self.conf = self.get_stack(self)
3332
def test_get_for_empty_stack(self):
3333
self.assertEquals(None, self.conf.get('foo'))
3335
def test_get_hook(self):
3336
self.conf.store._load_from_string('foo=bar')
3340
config.ConfigHooks.install_named_hook('get', hook, None)
3341
self.assertLength(0, calls)
3342
value = self.conf.get('foo')
3343
self.assertEquals('bar', value)
3344
self.assertLength(1, calls)
3345
self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
3348
class TestStackGetWithConverter(TestStackGet):
3351
super(TestStackGetWithConverter, self).setUp()
3352
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3353
self.registry = config.option_registry
3355
def register_bool_option(self, name, default=None, default_from_env=None):
3356
b = config.Option(name, help='A boolean.',
3357
default=default, default_from_env=default_from_env,
3358
from_unicode=config.bool_from_store)
3359
self.registry.register(b)
3361
def test_get_default_bool_None(self):
3362
self.register_bool_option('foo')
3363
self.assertEquals(None, self.conf.get('foo'))
3365
def test_get_default_bool_True(self):
3366
self.register_bool_option('foo', u'True')
3367
self.assertEquals(True, self.conf.get('foo'))
3369
def test_get_default_bool_False(self):
3370
self.register_bool_option('foo', False)
3371
self.assertEquals(False, self.conf.get('foo'))
3373
def test_get_default_bool_False_as_string(self):
3374
self.register_bool_option('foo', u'False')
3375
self.assertEquals(False, self.conf.get('foo'))
3377
def test_get_default_bool_from_env_converted(self):
3378
self.register_bool_option('foo', u'True', default_from_env=['FOO'])
3379
self.overrideEnv('FOO', 'False')
3380
self.assertEquals(False, self.conf.get('foo'))
3382
def test_get_default_bool_when_conversion_fails(self):
3383
self.register_bool_option('foo', default='True')
3384
self.conf.store._load_from_string('foo=invalid boolean')
3385
self.assertEquals(True, self.conf.get('foo'))
3387
def register_integer_option(self, name,
3388
default=None, default_from_env=None):
3389
i = config.Option(name, help='An integer.',
3390
default=default, default_from_env=default_from_env,
3391
from_unicode=config.int_from_store)
3392
self.registry.register(i)
3394
def test_get_default_integer_None(self):
3395
self.register_integer_option('foo')
3396
self.assertEquals(None, self.conf.get('foo'))
3398
def test_get_default_integer(self):
3399
self.register_integer_option('foo', 42)
3400
self.assertEquals(42, self.conf.get('foo'))
3402
def test_get_default_integer_as_string(self):
3403
self.register_integer_option('foo', u'42')
3404
self.assertEquals(42, self.conf.get('foo'))
3406
def test_get_default_integer_from_env(self):
3407
self.register_integer_option('foo', default_from_env=['FOO'])
3408
self.overrideEnv('FOO', '18')
3409
self.assertEquals(18, self.conf.get('foo'))
3411
def test_get_default_integer_when_conversion_fails(self):
3412
self.register_integer_option('foo', default='12')
3413
self.conf.store._load_from_string('foo=invalid integer')
3414
self.assertEquals(12, self.conf.get('foo'))
3416
def register_list_option(self, name, default=None, default_from_env=None):
3417
l = config.Option(name, help='A list.',
3418
default=default, default_from_env=default_from_env,
3419
from_unicode=config.list_from_store)
3420
self.registry.register(l)
3422
def test_get_default_list_None(self):
3423
self.register_list_option('foo')
3424
self.assertEquals(None, self.conf.get('foo'))
3426
def test_get_default_list_empty(self):
3427
self.register_list_option('foo', '')
3428
self.assertEquals([], self.conf.get('foo'))
3430
def test_get_default_list_from_env(self):
3431
self.register_list_option('foo', default_from_env=['FOO'])
3432
self.overrideEnv('FOO', '')
3433
self.assertEquals([], self.conf.get('foo'))
3435
def test_get_with_list_converter_no_item(self):
3436
self.register_list_option('foo', None)
3437
self.conf.store._load_from_string('foo=,')
3438
self.assertEquals([], self.conf.get('foo'))
3440
def test_get_with_list_converter_many_items(self):
3441
self.register_list_option('foo', None)
3442
self.conf.store._load_from_string('foo=m,o,r,e')
3443
self.assertEquals(['m', 'o', 'r', 'e'], self.conf.get('foo'))
3445
def test_get_with_list_converter_embedded_spaces_many_items(self):
3446
self.register_list_option('foo', None)
3447
self.conf.store._load_from_string('foo=" bar", "baz "')
3448
self.assertEquals([' bar', 'baz '], self.conf.get('foo'))
3450
def test_get_with_list_converter_stripped_spaces_many_items(self):
3451
self.register_list_option('foo', None)
3452
self.conf.store._load_from_string('foo= bar , baz ')
3453
self.assertEquals(['bar', 'baz'], self.conf.get('foo'))
3456
class TestStackExpandOptions(tests.TestCaseWithTransport):
3459
super(TestStackExpandOptions, self).setUp()
3460
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3461
self.registry = config.option_registry
3462
self.conf = build_branch_stack(self)
3464
def assertExpansion(self, expected, string, env=None):
3465
self.assertEquals(expected, self.conf.expand_options(string, env))
3467
def test_no_expansion(self):
3468
self.assertExpansion('foo', 'foo')
3470
def test_expand_default_value(self):
3471
self.conf.store._load_from_string('bar=baz')
3472
self.registry.register(config.Option('foo', default=u'{bar}'))
3473
self.assertEquals('baz', self.conf.get('foo', expand=True))
3475
def test_expand_default_from_env(self):
3476
self.conf.store._load_from_string('bar=baz')
3477
self.registry.register(config.Option('foo', default_from_env=['FOO']))
3478
self.overrideEnv('FOO', '{bar}')
3479
self.assertEquals('baz', self.conf.get('foo', expand=True))
3481
def test_expand_default_on_failed_conversion(self):
3482
self.conf.store._load_from_string('baz=bogus\nbar=42\nfoo={baz}')
3483
self.registry.register(
3484
config.Option('foo', default=u'{bar}',
3485
from_unicode=config.int_from_store))
3486
self.assertEquals(42, self.conf.get('foo', expand=True))
3488
def test_env_adding_options(self):
3489
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3491
def test_env_overriding_options(self):
3492
self.conf.store._load_from_string('foo=baz')
3493
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3495
def test_simple_ref(self):
3496
self.conf.store._load_from_string('foo=xxx')
3497
self.assertExpansion('xxx', '{foo}')
3499
def test_unknown_ref(self):
3500
self.assertRaises(errors.ExpandingUnknownOption,
3501
self.conf.expand_options, '{foo}')
3503
def test_indirect_ref(self):
3504
self.conf.store._load_from_string('''
3508
self.assertExpansion('xxx', '{bar}')
3510
def test_embedded_ref(self):
3511
self.conf.store._load_from_string('''
3515
self.assertExpansion('xxx', '{{bar}}')
3517
def test_simple_loop(self):
3518
self.conf.store._load_from_string('foo={foo}')
3519
self.assertRaises(errors.OptionExpansionLoop,
3520
self.conf.expand_options, '{foo}')
3522
def test_indirect_loop(self):
3523
self.conf.store._load_from_string('''
3527
e = self.assertRaises(errors.OptionExpansionLoop,
3528
self.conf.expand_options, '{foo}')
3529
self.assertEquals('foo->bar->baz', e.refs)
3530
self.assertEquals('{foo}', e.string)
3532
def test_list(self):
3533
self.conf.store._load_from_string('''
3537
list={foo},{bar},{baz}
3539
self.registry.register(
3540
config.Option('list', from_unicode=config.list_from_store))
3541
self.assertEquals(['start', 'middle', 'end'],
3542
self.conf.get('list', expand=True))
3544
def test_cascading_list(self):
3545
self.conf.store._load_from_string('''
3551
self.registry.register(
3552
config.Option('list', from_unicode=config.list_from_store))
3553
self.assertEquals(['start', 'middle', 'end'],
3554
self.conf.get('list', expand=True))
3556
def test_pathologically_hidden_list(self):
3557
self.conf.store._load_from_string('''
3563
hidden={start}{middle}{end}
3565
# What matters is what the registration says, the conversion happens
3566
# only after all expansions have been performed
3567
self.registry.register(
3568
config.Option('hidden', from_unicode=config.list_from_store))
3569
self.assertEquals(['bin', 'go'],
3570
self.conf.get('hidden', expand=True))
3573
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
3576
super(TestStackCrossSectionsExpand, self).setUp()
3578
def get_config(self, location, string):
3581
# Since we don't save the config we won't strictly require to inherit
3582
# from TestCaseInTempDir, but an error occurs so quickly...
3583
c = config.LocationStack(location)
3584
c.store._load_from_string(string)
3587
def test_dont_cross_unrelated_section(self):
3588
c = self.get_config('/another/branch/path','''
3593
[/another/branch/path]
3596
self.assertRaises(errors.ExpandingUnknownOption,
3597
c.get, 'bar', expand=True)
3599
def test_cross_related_sections(self):
3600
c = self.get_config('/project/branch/path','''
3604
[/project/branch/path]
3607
self.assertEquals('quux', c.get('bar', expand=True))
3610
class TestStackSet(TestStackWithTransport):
3612
def test_simple_set(self):
3613
conf = self.get_stack(self)
3614
conf.store._load_from_string('foo=bar')
3615
self.assertEquals('bar', conf.get('foo'))
3616
conf.set('foo', 'baz')
3617
# Did we get it back ?
3618
self.assertEquals('baz', conf.get('foo'))
3620
def test_set_creates_a_new_section(self):
3621
conf = self.get_stack(self)
3622
conf.set('foo', 'baz')
3623
self.assertEquals, 'baz', conf.get('foo')
3625
def test_set_hook(self):
3629
config.ConfigHooks.install_named_hook('set', hook, None)
3630
self.assertLength(0, calls)
3631
conf = self.get_stack(self)
3632
conf.set('foo', 'bar')
3633
self.assertLength(1, calls)
3634
self.assertEquals((conf, 'foo', 'bar'), calls[0])
3637
class TestStackRemove(TestStackWithTransport):
3639
def test_remove_existing(self):
3640
conf = self.get_stack(self)
3641
conf.set('foo', 'bar')
3642
self.assertEquals('bar', conf.get('foo'))
3644
# Did we get it back ?
3645
self.assertEquals(None, conf.get('foo'))
3647
def test_remove_unknown(self):
3648
conf = self.get_stack(self)
3649
self.assertRaises(KeyError, conf.remove, 'I_do_not_exist')
3651
def test_remove_hook(self):
3655
config.ConfigHooks.install_named_hook('remove', hook, None)
3656
self.assertLength(0, calls)
3657
conf = self.get_stack(self)
3658
conf.set('foo', 'bar')
3660
self.assertLength(1, calls)
3661
self.assertEquals((conf, 'foo'), calls[0])
3664
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
3667
super(TestConfigGetOptions, self).setUp()
3668
create_configs(self)
3670
def test_no_variable(self):
3671
# Using branch should query branch, locations and bazaar
3672
self.assertOptions([], self.branch_config)
3674
def test_option_in_bazaar(self):
3675
self.bazaar_config.set_user_option('file', 'bazaar')
3676
self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
3679
def test_option_in_locations(self):
3680
self.locations_config.set_user_option('file', 'locations')
3682
[('file', 'locations', self.tree.basedir, 'locations')],
3683
self.locations_config)
3685
def test_option_in_branch(self):
3686
self.branch_config.set_user_option('file', 'branch')
3687
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
3690
def test_option_in_bazaar_and_branch(self):
3691
self.bazaar_config.set_user_option('file', 'bazaar')
3692
self.branch_config.set_user_option('file', 'branch')
3693
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
3694
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3697
def test_option_in_branch_and_locations(self):
3698
# Hmm, locations override branch :-/
3699
self.locations_config.set_user_option('file', 'locations')
3700
self.branch_config.set_user_option('file', 'branch')
3702
[('file', 'locations', self.tree.basedir, 'locations'),
3703
('file', 'branch', 'DEFAULT', 'branch'),],
3706
def test_option_in_bazaar_locations_and_branch(self):
3707
self.bazaar_config.set_user_option('file', 'bazaar')
3708
self.locations_config.set_user_option('file', 'locations')
3709
self.branch_config.set_user_option('file', 'branch')
3711
[('file', 'locations', self.tree.basedir, 'locations'),
3712
('file', 'branch', 'DEFAULT', 'branch'),
3713
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3717
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
3720
super(TestConfigRemoveOption, self).setUp()
3721
create_configs_with_file_option(self)
3723
def test_remove_in_locations(self):
3724
self.locations_config.remove_user_option('file', self.tree.basedir)
3726
[('file', 'branch', 'DEFAULT', 'branch'),
3727
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3730
def test_remove_in_branch(self):
3731
self.branch_config.remove_user_option('file')
3733
[('file', 'locations', self.tree.basedir, 'locations'),
3734
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3737
def test_remove_in_bazaar(self):
3738
self.bazaar_config.remove_user_option('file')
3740
[('file', 'locations', self.tree.basedir, 'locations'),
3741
('file', 'branch', 'DEFAULT', 'branch'),],
3745
class TestConfigGetSections(tests.TestCaseWithTransport):
3748
super(TestConfigGetSections, self).setUp()
3749
create_configs(self)
3751
def assertSectionNames(self, expected, conf, name=None):
3752
"""Check which sections are returned for a given config.
3754
If fallback configurations exist their sections can be included.
3756
:param expected: A list of section names.
3758
:param conf: The configuration that will be queried.
3760
:param name: An optional section name that will be passed to
3763
sections = list(conf._get_sections(name))
3764
self.assertLength(len(expected), sections)
3765
self.assertEqual(expected, [name for name, _, _ in sections])
3767
def test_bazaar_default_section(self):
3768
self.assertSectionNames(['DEFAULT'], self.bazaar_config)
3770
def test_locations_default_section(self):
3771
# No sections are defined in an empty file
3772
self.assertSectionNames([], self.locations_config)
3774
def test_locations_named_section(self):
3775
self.locations_config.set_user_option('file', 'locations')
3776
self.assertSectionNames([self.tree.basedir], self.locations_config)
3778
def test_locations_matching_sections(self):
3779
loc_config = self.locations_config
3780
loc_config.set_user_option('file', 'locations')
3781
# We need to cheat a bit here to create an option in sections above and
3782
# below the 'location' one.
3783
parser = loc_config._get_parser()
3784
# locations.cong deals with '/' ignoring native os.sep
3785
location_names = self.tree.basedir.split('/')
3786
parent = '/'.join(location_names[:-1])
3787
child = '/'.join(location_names + ['child'])
3789
parser[parent]['file'] = 'parent'
3791
parser[child]['file'] = 'child'
3792
self.assertSectionNames([self.tree.basedir, parent], loc_config)
3794
def test_branch_data_default_section(self):
3795
self.assertSectionNames([None],
3796
self.branch_config._get_branch_data_config())
3798
def test_branch_default_sections(self):
3799
# No sections are defined in an empty locations file
3800
self.assertSectionNames([None, 'DEFAULT'],
3802
# Unless we define an option
3803
self.branch_config._get_location_config().set_user_option(
3804
'file', 'locations')
3805
self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
3808
def test_bazaar_named_section(self):
3809
# We need to cheat as the API doesn't give direct access to sections
3810
# other than DEFAULT.
3811
self.bazaar_config.set_alias('bazaar', 'bzr')
3812
self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
3815
class TestAuthenticationConfigFile(tests.TestCase):
3816
"""Test the authentication.conf file matching"""
3818
def _got_user_passwd(self, expected_user, expected_password,
3819
config, *args, **kwargs):
3820
credentials = config.get_credentials(*args, **kwargs)
3821
if credentials is None:
3825
user = credentials['user']
3826
password = credentials['password']
3827
self.assertEquals(expected_user, user)
3828
self.assertEquals(expected_password, password)
3830
def test_empty_config(self):
3831
conf = config.AuthenticationConfig(_file=StringIO())
3832
self.assertEquals({}, conf._get_config())
3833
self._got_user_passwd(None, None, conf, 'http', 'foo.net')
3835
def test_non_utf8_config(self):
3836
conf = config.AuthenticationConfig(_file=StringIO(
3838
self.assertRaises(errors.ConfigContentError, conf._get_config)
3840
def test_missing_auth_section_header(self):
3841
conf = config.AuthenticationConfig(_file=StringIO('foo = bar'))
3842
self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
3844
def test_auth_section_header_not_closed(self):
3845
conf = config.AuthenticationConfig(_file=StringIO('[DEF'))
3846
self.assertRaises(errors.ParseConfigError, conf._get_config)
3848
def test_auth_value_not_boolean(self):
3849
conf = config.AuthenticationConfig(_file=StringIO(
3853
verify_certificates=askme # Error: Not a boolean
3855
self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
3857
def test_auth_value_not_int(self):
3858
conf = config.AuthenticationConfig(_file=StringIO(
3862
port=port # Error: Not an int
3864
self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
3866
def test_unknown_password_encoding(self):
3867
conf = config.AuthenticationConfig(_file=StringIO(
3871
password_encoding=unknown
3873
self.assertRaises(ValueError, conf.get_password,
3874
'ftp', 'foo.net', 'joe')
3876
def test_credentials_for_scheme_host(self):
3877
conf = config.AuthenticationConfig(_file=StringIO(
3878
"""# Identity on foo.net
3883
password=secret-pass
3886
self._got_user_passwd('joe', 'secret-pass', conf, 'ftp', 'foo.net')
3888
self._got_user_passwd(None, None, conf, 'http', 'foo.net')
3890
self._got_user_passwd(None, None, conf, 'ftp', 'bar.net')
3892
def test_credentials_for_host_port(self):
3893
conf = config.AuthenticationConfig(_file=StringIO(
3894
"""# Identity on foo.net
3900
password=secret-pass
3903
self._got_user_passwd('joe', 'secret-pass',
3904
conf, 'ftp', 'foo.net', port=10021)
3906
self._got_user_passwd(None, None, conf, 'ftp', 'foo.net')
3908
def test_for_matching_host(self):
3909
conf = config.AuthenticationConfig(_file=StringIO(
3910
"""# Identity on foo.net
3916
[sourceforge domain]
3923
self._got_user_passwd('georges', 'bendover',
3924
conf, 'bzr', 'foo.bzr.sf.net')
3926
self._got_user_passwd(None, None,
3927
conf, 'bzr', 'bbzr.sf.net')
3929
def test_for_matching_host_None(self):
3930
conf = config.AuthenticationConfig(_file=StringIO(
3931
"""# Identity on foo.net
3941
self._got_user_passwd('joe', 'joepass',
3942
conf, 'bzr', 'quux.net')
3943
# no host but different scheme
3944
self._got_user_passwd('georges', 'bendover',
3945
conf, 'ftp', 'quux.net')
3947
def test_credentials_for_path(self):
3948
conf = config.AuthenticationConfig(_file=StringIO(
3964
self._got_user_passwd(None, None,
3965
conf, 'http', host='bar.org', path='/dir3')
3967
self._got_user_passwd('georges', 'bendover',
3968
conf, 'http', host='bar.org', path='/dir2')
3970
self._got_user_passwd('jim', 'jimpass',
3971
conf, 'http', host='bar.org',path='/dir1/subdir')
3973
def test_credentials_for_user(self):
3974
conf = config.AuthenticationConfig(_file=StringIO(
3983
self._got_user_passwd('jim', 'jimpass',
3984
conf, 'http', 'bar.org')
3986
self._got_user_passwd('jim', 'jimpass',
3987
conf, 'http', 'bar.org', user='jim')
3988
# Don't get a different user if one is specified
3989
self._got_user_passwd(None, None,
3990
conf, 'http', 'bar.org', user='georges')
3992
def test_credentials_for_user_without_password(self):
3993
conf = config.AuthenticationConfig(_file=StringIO(
4000
# Get user but no password
4001
self._got_user_passwd('jim', None,
4002
conf, 'http', 'bar.org')
4004
def test_verify_certificates(self):
4005
conf = config.AuthenticationConfig(_file=StringIO(
4012
verify_certificates=False
4019
credentials = conf.get_credentials('https', 'bar.org')
4020
self.assertEquals(False, credentials.get('verify_certificates'))
4021
credentials = conf.get_credentials('https', 'foo.net')
4022
self.assertEquals(True, credentials.get('verify_certificates'))
4025
class TestAuthenticationStorage(tests.TestCaseInTempDir):
4027
def test_set_credentials(self):
4028
conf = config.AuthenticationConfig()
4029
conf.set_credentials('name', 'host', 'user', 'scheme', 'password',
4030
99, path='/foo', verify_certificates=False, realm='realm')
4031
credentials = conf.get_credentials(host='host', scheme='scheme',
4032
port=99, path='/foo',
4034
CREDENTIALS = {'name': 'name', 'user': 'user', 'password': 'password',
4035
'verify_certificates': False, 'scheme': 'scheme',
4036
'host': 'host', 'port': 99, 'path': '/foo',
4038
self.assertEqual(CREDENTIALS, credentials)
4039
credentials_from_disk = config.AuthenticationConfig().get_credentials(
4040
host='host', scheme='scheme', port=99, path='/foo', realm='realm')
4041
self.assertEqual(CREDENTIALS, credentials_from_disk)
4043
def test_reset_credentials_different_name(self):
4044
conf = config.AuthenticationConfig()
4045
conf.set_credentials('name', 'host', 'user', 'scheme', 'password'),
4046
conf.set_credentials('name2', 'host', 'user2', 'scheme', 'password'),
4047
self.assertIs(None, conf._get_config().get('name'))
4048
credentials = conf.get_credentials(host='host', scheme='scheme')
4049
CREDENTIALS = {'name': 'name2', 'user': 'user2', 'password':
4050
'password', 'verify_certificates': True,
4051
'scheme': 'scheme', 'host': 'host', 'port': None,
4052
'path': None, 'realm': None}
4053
self.assertEqual(CREDENTIALS, credentials)
4056
class TestAuthenticationConfig(tests.TestCase):
4057
"""Test AuthenticationConfig behaviour"""
4059
def _check_default_password_prompt(self, expected_prompt_format, scheme,
4060
host=None, port=None, realm=None,
4064
user, password = 'jim', 'precious'
4065
expected_prompt = expected_prompt_format % {
4066
'scheme': scheme, 'host': host, 'port': port,
4067
'user': user, 'realm': realm}
4069
stdout = tests.StringIOWrapper()
4070
stderr = tests.StringIOWrapper()
4071
ui.ui_factory = tests.TestUIFactory(stdin=password + '\n',
4072
stdout=stdout, stderr=stderr)
4073
# We use an empty conf so that the user is always prompted
4074
conf = config.AuthenticationConfig()
4075
self.assertEquals(password,
4076
conf.get_password(scheme, host, user, port=port,
4077
realm=realm, path=path))
4078
self.assertEquals(expected_prompt, stderr.getvalue())
4079
self.assertEquals('', stdout.getvalue())
4081
def _check_default_username_prompt(self, expected_prompt_format, scheme,
4082
host=None, port=None, realm=None,
4087
expected_prompt = expected_prompt_format % {
4088
'scheme': scheme, 'host': host, 'port': port,
4090
stdout = tests.StringIOWrapper()
4091
stderr = tests.StringIOWrapper()
4092
ui.ui_factory = tests.TestUIFactory(stdin=username+ '\n',
4093
stdout=stdout, stderr=stderr)
4094
# We use an empty conf so that the user is always prompted
4095
conf = config.AuthenticationConfig()
4096
self.assertEquals(username, conf.get_user(scheme, host, port=port,
4097
realm=realm, path=path, ask=True))
4098
self.assertEquals(expected_prompt, stderr.getvalue())
4099
self.assertEquals('', stdout.getvalue())
4101
def test_username_defaults_prompts(self):
4102
# HTTP prompts can't be tested here, see test_http.py
4103
self._check_default_username_prompt(u'FTP %(host)s username: ', 'ftp')
4104
self._check_default_username_prompt(
4105
u'FTP %(host)s:%(port)d username: ', 'ftp', port=10020)
4106
self._check_default_username_prompt(
4107
u'SSH %(host)s:%(port)d username: ', 'ssh', port=12345)
4109
def test_username_default_no_prompt(self):
4110
conf = config.AuthenticationConfig()
4111
self.assertEquals(None,
4112
conf.get_user('ftp', 'example.com'))
4113
self.assertEquals("explicitdefault",
4114
conf.get_user('ftp', 'example.com', default="explicitdefault"))
4116
def test_password_default_prompts(self):
4117
# HTTP prompts can't be tested here, see test_http.py
4118
self._check_default_password_prompt(
4119
u'FTP %(user)s@%(host)s password: ', 'ftp')
4120
self._check_default_password_prompt(
4121
u'FTP %(user)s@%(host)s:%(port)d password: ', 'ftp', port=10020)
4122
self._check_default_password_prompt(
4123
u'SSH %(user)s@%(host)s:%(port)d password: ', 'ssh', port=12345)
4124
# SMTP port handling is a bit special (it's handled if embedded in the
4126
# FIXME: should we: forbid that, extend it to other schemes, leave
4127
# things as they are that's fine thank you ?
4128
self._check_default_password_prompt(
4129
u'SMTP %(user)s@%(host)s password: ', 'smtp')
4130
self._check_default_password_prompt(
4131
u'SMTP %(user)s@%(host)s password: ', 'smtp', host='bar.org:10025')
4132
self._check_default_password_prompt(
4133
u'SMTP %(user)s@%(host)s:%(port)d password: ', 'smtp', port=10025)
4135
def test_ssh_password_emits_warning(self):
4136
conf = config.AuthenticationConfig(_file=StringIO(
4144
entered_password = 'typed-by-hand'
4145
stdout = tests.StringIOWrapper()
4146
stderr = tests.StringIOWrapper()
4147
ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
4148
stdout=stdout, stderr=stderr)
4150
# Since the password defined in the authentication config is ignored,
4151
# the user is prompted
4152
self.assertEquals(entered_password,
4153
conf.get_password('ssh', 'bar.org', user='jim'))
4154
self.assertContainsRe(
4156
'password ignored in section \[ssh with password\]')
4158
def test_ssh_without_password_doesnt_emit_warning(self):
4159
conf = config.AuthenticationConfig(_file=StringIO(
4166
entered_password = 'typed-by-hand'
4167
stdout = tests.StringIOWrapper()
4168
stderr = tests.StringIOWrapper()
4169
ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
4173
# Since the password defined in the authentication config is ignored,
4174
# the user is prompted
4175
self.assertEquals(entered_password,
4176
conf.get_password('ssh', 'bar.org', user='jim'))
4177
# No warning shoud be emitted since there is no password. We are only
4179
self.assertNotContainsRe(
4181
'password ignored in section \[ssh with password\]')
4183
def test_uses_fallback_stores(self):
4184
self.overrideAttr(config, 'credential_store_registry',
4185
config.CredentialStoreRegistry())
4186
store = StubCredentialStore()
4187
store.add_credentials("http", "example.com", "joe", "secret")
4188
config.credential_store_registry.register("stub", store, fallback=True)
4189
conf = config.AuthenticationConfig(_file=StringIO())
4190
creds = conf.get_credentials("http", "example.com")
4191
self.assertEquals("joe", creds["user"])
4192
self.assertEquals("secret", creds["password"])
4195
class StubCredentialStore(config.CredentialStore):
4201
def add_credentials(self, scheme, host, user, password=None):
4202
self._username[(scheme, host)] = user
4203
self._password[(scheme, host)] = password
4205
def get_credentials(self, scheme, host, port=None, user=None,
4206
path=None, realm=None):
4207
key = (scheme, host)
4208
if not key in self._username:
4210
return { "scheme": scheme, "host": host, "port": port,
4211
"user": self._username[key], "password": self._password[key]}
4214
class CountingCredentialStore(config.CredentialStore):
4219
def get_credentials(self, scheme, host, port=None, user=None,
4220
path=None, realm=None):
4225
class TestCredentialStoreRegistry(tests.TestCase):
4227
def _get_cs_registry(self):
4228
return config.credential_store_registry
4230
def test_default_credential_store(self):
4231
r = self._get_cs_registry()
4232
default = r.get_credential_store(None)
4233
self.assertIsInstance(default, config.PlainTextCredentialStore)
4235
def test_unknown_credential_store(self):
4236
r = self._get_cs_registry()
4237
# It's hard to imagine someone creating a credential store named
4238
# 'unknown' so we use that as an never registered key.
4239
self.assertRaises(KeyError, r.get_credential_store, 'unknown')
4241
def test_fallback_none_registered(self):
4242
r = config.CredentialStoreRegistry()
4243
self.assertEquals(None,
4244
r.get_fallback_credentials("http", "example.com"))
4246
def test_register(self):
4247
r = config.CredentialStoreRegistry()
4248
r.register("stub", StubCredentialStore(), fallback=False)
4249
r.register("another", StubCredentialStore(), fallback=True)
4250
self.assertEquals(["another", "stub"], r.keys())
4252
def test_register_lazy(self):
4253
r = config.CredentialStoreRegistry()
4254
r.register_lazy("stub", "bzrlib.tests.test_config",
4255
"StubCredentialStore", fallback=False)
4256
self.assertEquals(["stub"], r.keys())
4257
self.assertIsInstance(r.get_credential_store("stub"),
4258
StubCredentialStore)
4260
def test_is_fallback(self):
4261
r = config.CredentialStoreRegistry()
4262
r.register("stub1", None, fallback=False)
4263
r.register("stub2", None, fallback=True)
4264
self.assertEquals(False, r.is_fallback("stub1"))
4265
self.assertEquals(True, r.is_fallback("stub2"))
4267
def test_no_fallback(self):
4268
r = config.CredentialStoreRegistry()
4269
store = CountingCredentialStore()
4270
r.register("count", store, fallback=False)
4271
self.assertEquals(None,
4272
r.get_fallback_credentials("http", "example.com"))
4273
self.assertEquals(0, store._calls)
4275
def test_fallback_credentials(self):
4276
r = config.CredentialStoreRegistry()
4277
store = StubCredentialStore()
4278
store.add_credentials("http", "example.com",
4279
"somebody", "geheim")
4280
r.register("stub", store, fallback=True)
4281
creds = r.get_fallback_credentials("http", "example.com")
4282
self.assertEquals("somebody", creds["user"])
4283
self.assertEquals("geheim", creds["password"])
4285
def test_fallback_first_wins(self):
4286
r = config.CredentialStoreRegistry()
4287
stub1 = StubCredentialStore()
4288
stub1.add_credentials("http", "example.com",
4289
"somebody", "stub1")
4290
r.register("stub1", stub1, fallback=True)
4291
stub2 = StubCredentialStore()
4292
stub2.add_credentials("http", "example.com",
4293
"somebody", "stub2")
4294
r.register("stub2", stub1, fallback=True)
4295
creds = r.get_fallback_credentials("http", "example.com")
4296
self.assertEquals("somebody", creds["user"])
4297
self.assertEquals("stub1", creds["password"])
4300
class TestPlainTextCredentialStore(tests.TestCase):
4302
def test_decode_password(self):
4303
r = config.credential_store_registry
4304
plain_text = r.get_credential_store()
4305
decoded = plain_text.decode_password(dict(password='secret'))
4306
self.assertEquals('secret', decoded)
4309
# FIXME: Once we have a way to declare authentication to all test servers, we
4310
# can implement generic tests.
4311
# test_user_password_in_url
4312
# test_user_in_url_password_from_config
4313
# test_user_in_url_password_prompted
4314
# test_user_in_config
4315
# test_user_getpass.getuser
4316
# test_user_prompted ?
4317
class TestAuthenticationRing(tests.TestCaseWithTransport):
4321
class TestAutoUserId(tests.TestCase):
4322
"""Test inferring an automatic user name."""
4324
def test_auto_user_id(self):
4325
"""Automatic inference of user name.
4327
This is a bit hard to test in an isolated way, because it depends on
4328
system functions that go direct to /etc or perhaps somewhere else.
4329
But it's reasonable to say that on Unix, with an /etc/mailname, we ought
4330
to be able to choose a user name with no configuration.
4332
if sys.platform == 'win32':
4333
raise tests.TestSkipped(
4334
"User name inference not implemented on win32")
4335
realname, address = config._auto_user_id()
4336
if os.path.exists('/etc/mailname'):
4337
self.assertIsNot(None, realname)
4338
self.assertIsNot(None, address)
4340
self.assertEquals((None, None), (realname, address))