~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_config.py

  • Committer: Jelmer Vernooij
  • Date: 2012-02-20 12:19:29 UTC
  • mfrom: (6437.23.11 2.5)
  • mto: (6581.1.1 trunk)
  • mto: This revision was merged to the branch mainline in revision 6582.
  • Revision ID: jelmer@samba.org-20120220121929-7ni2psvjoatm1yp4
Merge bzr/2.5.

Show diffs side-by-side

added added

removed removed

Lines of Context:
33
33
    errors,
34
34
    osutils,
35
35
    mail_client,
36
 
    mergetools,
37
36
    ui,
38
37
    urlutils,
39
 
    registry,
40
38
    remote,
41
39
    tests,
42
40
    trace,
43
 
    transport,
44
41
    )
45
42
from bzrlib.symbol_versioning import (
46
43
    deprecated_in,
47
 
    deprecated_method,
48
44
    )
49
45
from bzrlib.transport import remote as transport_remote
50
46
from bzrlib.tests import (
71
67
 
72
68
# Register helpers to build stores
73
69
config.test_store_builder_registry.register(
74
 
    'configobj', lambda test: config.IniFileStore(test.get_transport(),
75
 
                                                  'configobj.conf'))
 
70
    'configobj', lambda test: config.TransportIniFileStore(
 
71
        test.get_transport(), 'configobj.conf'))
76
72
config.test_store_builder_registry.register(
77
73
    'bazaar', lambda test: config.GlobalStore())
78
74
config.test_store_builder_registry.register(
148
144
config.test_stack_builder_registry.register('branch', build_branch_stack)
149
145
 
150
146
 
151
 
def build_remote_branch_stack(test):
 
147
def build_branch_only_stack(test):
152
148
    # There is only one permutation (but we won't be able to handle more with
153
149
    # this design anyway)
154
150
    (transport_class,
155
151
     server_class) = transport_remote.get_test_permutations()[0]
156
152
    build_backing_branch(test, 'branch', transport_class, server_class)
157
153
    b = branch.Branch.open(test.get_url('branch'))
158
 
    return config.RemoteBranchStack(b)
159
 
config.test_stack_builder_registry.register('remote_branch',
160
 
                                            build_remote_branch_stack)
 
154
    return config.BranchOnlyStack(b)
 
155
config.test_stack_builder_registry.register('branch_only',
 
156
                                            build_branch_only_stack)
161
157
 
162
158
def build_remote_control_stack(test):
163
159
    # There is only one permutation (but we won't be able to handle more with
187
183
user_global_option=something
188
184
bzr.mergetool.sometool=sometool {base} {this} {other} -o {result}
189
185
bzr.mergetool.funkytool=funkytool "arg with spaces" {this_temp}
 
186
bzr.mergetool.newtool='"newtool with spaces" {this_temp}'
190
187
bzr.default_mergetool=sometool
191
188
[ALIASES]
192
189
h=help
331
328
 
332
329
class FakeBranch(object):
333
330
 
334
 
    def __init__(self, base=None, user_id=None):
 
331
    def __init__(self, base=None):
335
332
        if base is None:
336
333
            self.base = "http://example.com/branches/demo"
337
334
        else:
338
335
            self.base = base
339
336
        self._transport = self.control_files = \
340
 
            FakeControlFilesAndTransport(user_id=user_id)
 
337
            FakeControlFilesAndTransport()
341
338
 
342
339
    def _get_config(self):
343
340
        return config.TransportConfig(self._transport, 'branch.conf')
351
348
 
352
349
class FakeControlFilesAndTransport(object):
353
350
 
354
 
    def __init__(self, user_id=None):
 
351
    def __init__(self):
355
352
        self.files = {}
356
 
        if user_id:
357
 
            self.files['email'] = user_id
358
353
        self._transport = self
359
354
 
360
 
    def get_utf8(self, filename):
361
 
        # from LockableFiles
362
 
        raise AssertionError("get_utf8 should no longer be used")
363
 
 
364
355
    def get(self, filename):
365
356
        # from Transport
366
357
        try:
503
494
 
504
495
    def test_signatures_default(self):
505
496
        my_config = config.Config()
506
 
        self.assertFalse(my_config.signature_needed())
 
497
        self.assertFalse(
 
498
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
499
                my_config.signature_needed))
507
500
        self.assertEqual(config.CHECK_IF_POSSIBLE,
508
 
                         my_config.signature_checking())
 
501
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
502
                my_config.signature_checking))
509
503
        self.assertEqual(config.SIGN_WHEN_REQUIRED,
510
 
                         my_config.signing_policy())
 
504
                self.applyDeprecated(deprecated_in((2, 5, 0)),
 
505
                    my_config.signing_policy))
511
506
 
512
507
    def test_signatures_template_method(self):
513
508
        my_config = InstrumentedConfig()
514
 
        self.assertEqual(config.CHECK_NEVER, my_config.signature_checking())
 
509
        self.assertEqual(config.CHECK_NEVER,
 
510
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
511
                my_config.signature_checking))
515
512
        self.assertEqual(['_get_signature_checking'], my_config._calls)
516
513
 
517
514
    def test_signatures_template_method_none(self):
518
515
        my_config = InstrumentedConfig()
519
516
        my_config._signatures = None
520
517
        self.assertEqual(config.CHECK_IF_POSSIBLE,
521
 
                         my_config.signature_checking())
 
518
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
519
                             my_config.signature_checking))
522
520
        self.assertEqual(['_get_signature_checking'], my_config._calls)
523
521
 
524
522
    def test_gpg_signing_command_default(self):
525
523
        my_config = config.Config()
526
 
        self.assertEqual('gpg', my_config.gpg_signing_command())
 
524
        self.assertEqual('gpg',
 
525
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
526
                my_config.gpg_signing_command))
527
527
 
528
528
    def test_get_user_option_default(self):
529
529
        my_config = config.Config()
531
531
 
532
532
    def test_post_commit_default(self):
533
533
        my_config = config.Config()
534
 
        self.assertEqual(None, my_config.post_commit())
 
534
        self.assertEqual(None, self.applyDeprecated(deprecated_in((2, 5, 0)),
 
535
                                                    my_config.post_commit))
 
536
 
535
537
 
536
538
    def test_log_format_default(self):
537
539
        my_config = config.Config()
538
 
        self.assertEqual('long', my_config.log_format())
 
540
        self.assertEqual('long',
 
541
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
542
                                              my_config.log_format))
539
543
 
540
544
    def test_acceptable_keys_default(self):
541
545
        my_config = config.Config()
542
 
        self.assertEqual(None, my_config.acceptable_keys())
 
546
        self.assertEqual(None, self.applyDeprecated(deprecated_in((2, 5, 0)),
 
547
            my_config.acceptable_keys))
543
548
 
544
549
    def test_validate_signatures_in_log_default(self):
545
550
        my_config = config.Config()
571
576
    def test_config_dir(self):
572
577
        self.assertEqual(config.config_dir(), self.bzr_home)
573
578
 
 
579
    def test_config_dir_is_unicode(self):
 
580
        self.assertIsInstance(config.config_dir(), unicode)
 
581
 
574
582
    def test_config_filename(self):
575
583
        self.assertEqual(config.config_filename(),
576
584
                         self.bzr_home + '/bazaar.conf')
839
847
        self.assertEquals(['{foo', '}', '{', 'bar}'],
840
848
                          conf.get_user_option('hidden', expand=True))
841
849
 
 
850
 
842
851
class TestLocationConfigOptionExpansion(tests.TestCaseInTempDir):
843
852
 
844
853
    def get_config(self, location, string=None):
1065
1074
si_g = 5g,
1066
1075
si_gb = 5gB,
1067
1076
""")
1068
 
        get_si = conf.get_user_option_as_int_from_SI
 
1077
        def get_si(s, default=None):
 
1078
            return self.applyDeprecated(
 
1079
                deprecated_in((2, 5, 0)),
 
1080
                conf.get_user_option_as_int_from_SI, s, default)
1069
1081
        self.assertEqual(100, get_si('plain'))
1070
1082
        self.assertEqual(5000, get_si('si_k'))
1071
1083
        self.assertEqual(5000, get_si('si_kb'))
1076
1088
        self.assertEqual(None, get_si('non-exist'))
1077
1089
        self.assertEqual(42, get_si('non-exist-with-default',  42))
1078
1090
 
 
1091
 
1079
1092
class TestSupressWarning(TestIniConfig):
1080
1093
 
1081
1094
    def make_warnings_config(self, s):
1183
1196
        b = self.make_branch('!repo')
1184
1197
        self.assertEqual('!repo', b.get_config().get_nickname())
1185
1198
 
 
1199
    def test_autonick_uses_branch_name(self):
 
1200
        b = self.make_branch('foo', name='bar')
 
1201
        self.assertEqual('bar', b.get_config().get_nickname())
 
1202
 
1186
1203
    def test_warn_if_masked(self):
1187
1204
        warnings = []
1188
1205
        def warning(*args):
1237
1254
    def test_signatures_always(self):
1238
1255
        my_config = config.GlobalConfig.from_string(sample_always_signatures)
1239
1256
        self.assertEqual(config.CHECK_NEVER,
1240
 
                         my_config.signature_checking())
 
1257
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1258
                             my_config.signature_checking))
1241
1259
        self.assertEqual(config.SIGN_ALWAYS,
1242
 
                         my_config.signing_policy())
1243
 
        self.assertEqual(True, my_config.signature_needed())
 
1260
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1261
                             my_config.signing_policy))
 
1262
        self.assertEqual(True,
 
1263
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1264
                my_config.signature_needed))
1244
1265
 
1245
1266
    def test_signatures_if_possible(self):
1246
1267
        my_config = config.GlobalConfig.from_string(sample_maybe_signatures)
1247
1268
        self.assertEqual(config.CHECK_NEVER,
1248
 
                         my_config.signature_checking())
 
1269
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1270
                             my_config.signature_checking))
1249
1271
        self.assertEqual(config.SIGN_WHEN_REQUIRED,
1250
 
                         my_config.signing_policy())
1251
 
        self.assertEqual(False, my_config.signature_needed())
 
1272
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1273
                             my_config.signing_policy))
 
1274
        self.assertEqual(False, self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1275
            my_config.signature_needed))
1252
1276
 
1253
1277
    def test_signatures_ignore(self):
1254
1278
        my_config = config.GlobalConfig.from_string(sample_ignore_signatures)
1255
1279
        self.assertEqual(config.CHECK_ALWAYS,
1256
 
                         my_config.signature_checking())
 
1280
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1281
                             my_config.signature_checking))
1257
1282
        self.assertEqual(config.SIGN_NEVER,
1258
 
                         my_config.signing_policy())
1259
 
        self.assertEqual(False, my_config.signature_needed())
 
1283
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1284
                             my_config.signing_policy))
 
1285
        self.assertEqual(False, self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1286
            my_config.signature_needed))
1260
1287
 
1261
1288
    def _get_sample_config(self):
1262
1289
        my_config = config.GlobalConfig.from_string(sample_config_text)
1264
1291
 
1265
1292
    def test_gpg_signing_command(self):
1266
1293
        my_config = self._get_sample_config()
1267
 
        self.assertEqual("gnome-gpg", my_config.gpg_signing_command())
1268
 
        self.assertEqual(False, my_config.signature_needed())
 
1294
        self.assertEqual("gnome-gpg",
 
1295
            self.applyDeprecated(
 
1296
                deprecated_in((2, 5, 0)), my_config.gpg_signing_command))
 
1297
        self.assertEqual(False, self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1298
            my_config.signature_needed))
1269
1299
 
1270
1300
    def test_gpg_signing_key(self):
1271
1301
        my_config = self._get_sample_config()
1272
 
        self.assertEqual("DD4D5088", my_config.gpg_signing_key())
 
1302
        self.assertEqual("DD4D5088",
 
1303
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1304
                my_config.gpg_signing_key))
1273
1305
 
1274
1306
    def _get_empty_config(self):
1275
1307
        my_config = config.GlobalConfig()
1277
1309
 
1278
1310
    def test_gpg_signing_command_unset(self):
1279
1311
        my_config = self._get_empty_config()
1280
 
        self.assertEqual("gpg", my_config.gpg_signing_command())
 
1312
        self.assertEqual("gpg",
 
1313
            self.applyDeprecated(
 
1314
                deprecated_in((2, 5, 0)), my_config.gpg_signing_command))
1281
1315
 
1282
1316
    def test_get_user_option_default(self):
1283
1317
        my_config = self._get_empty_config()
1290
1324
 
1291
1325
    def test_post_commit_default(self):
1292
1326
        my_config = self._get_sample_config()
1293
 
        self.assertEqual(None, my_config.post_commit())
 
1327
        self.assertEqual(None,
 
1328
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1329
                                              my_config.post_commit))
1294
1330
 
1295
1331
    def test_configured_logformat(self):
1296
1332
        my_config = self._get_sample_config()
1297
 
        self.assertEqual("short", my_config.log_format())
 
1333
        self.assertEqual("short",
 
1334
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1335
                                              my_config.log_format))
1298
1336
 
1299
1337
    def test_configured_acceptable_keys(self):
1300
1338
        my_config = self._get_sample_config()
1301
 
        self.assertEqual("amy", my_config.acceptable_keys())
 
1339
        self.assertEqual("amy",
 
1340
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1341
                my_config.acceptable_keys))
1302
1342
 
1303
1343
    def test_configured_validate_signatures_in_log(self):
1304
1344
        my_config = self._get_sample_config()
1342
1382
        self.log(repr(tools))
1343
1383
        self.assertEqual(
1344
1384
            {u'funkytool' : u'funkytool "arg with spaces" {this_temp}',
1345
 
            u'sometool' : u'sometool {base} {this} {other} -o {result}'},
 
1385
            u'sometool' : u'sometool {base} {this} {other} -o {result}',
 
1386
            u'newtool' : u'"newtool with spaces" {this_temp}'},
1346
1387
            tools)
1347
1388
 
1348
1389
    def test_get_merge_tools_empty(self):
1539
1580
        self.get_branch_config('http://www.example.com',
1540
1581
                                 global_config=sample_ignore_signatures)
1541
1582
        self.assertEqual(config.CHECK_ALWAYS,
1542
 
                         self.my_config.signature_checking())
 
1583
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1584
                             self.my_config.signature_checking))
1543
1585
        self.assertEqual(config.SIGN_NEVER,
1544
 
                         self.my_config.signing_policy())
 
1586
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1587
                             self.my_config.signing_policy))
1545
1588
 
1546
1589
    def test_signatures_never(self):
1547
1590
        self.get_branch_config('/a/c')
1548
1591
        self.assertEqual(config.CHECK_NEVER,
1549
 
                         self.my_config.signature_checking())
 
1592
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1593
                             self.my_config.signature_checking))
1550
1594
 
1551
1595
    def test_signatures_when_available(self):
1552
1596
        self.get_branch_config('/a/', global_config=sample_ignore_signatures)
1553
1597
        self.assertEqual(config.CHECK_IF_POSSIBLE,
1554
 
                         self.my_config.signature_checking())
 
1598
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1599
                             self.my_config.signature_checking))
1555
1600
 
1556
1601
    def test_signatures_always(self):
1557
1602
        self.get_branch_config('/b')
1558
1603
        self.assertEqual(config.CHECK_ALWAYS,
1559
 
                         self.my_config.signature_checking())
 
1604
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1605
                         self.my_config.signature_checking))
1560
1606
 
1561
1607
    def test_gpg_signing_command(self):
1562
1608
        self.get_branch_config('/b')
1563
 
        self.assertEqual("gnome-gpg", self.my_config.gpg_signing_command())
 
1609
        self.assertEqual("gnome-gpg",
 
1610
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1611
                self.my_config.gpg_signing_command))
1564
1612
 
1565
1613
    def test_gpg_signing_command_missing(self):
1566
1614
        self.get_branch_config('/a')
1567
 
        self.assertEqual("false", self.my_config.gpg_signing_command())
 
1615
        self.assertEqual("false",
 
1616
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1617
                self.my_config.gpg_signing_command))
1568
1618
 
1569
1619
    def test_gpg_signing_key(self):
1570
1620
        self.get_branch_config('/b')
1571
 
        self.assertEqual("DD4D5088", self.my_config.gpg_signing_key())
 
1621
        self.assertEqual("DD4D5088", self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1622
            self.my_config.gpg_signing_key))
1572
1623
 
1573
1624
    def test_gpg_signing_key_default(self):
1574
1625
        self.get_branch_config('/a')
1575
 
        self.assertEqual("erik@bagfors.nu", self.my_config.gpg_signing_key())
 
1626
        self.assertEqual("erik@bagfors.nu",
 
1627
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1628
                self.my_config.gpg_signing_key))
1576
1629
 
1577
1630
    def test_get_user_option_global(self):
1578
1631
        self.get_branch_config('/a')
1666
1719
    def test_post_commit_default(self):
1667
1720
        self.get_branch_config('/a/c')
1668
1721
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1669
 
                         self.my_config.post_commit())
 
1722
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1723
                                              self.my_config.post_commit))
1670
1724
 
1671
1725
    def get_branch_config(self, location, global_config=None,
1672
1726
                          location_config=None):
1762
1816
        return my_config
1763
1817
 
1764
1818
    def test_user_id(self):
1765
 
        branch = FakeBranch(user_id='Robert Collins <robertc@example.net>')
 
1819
        branch = FakeBranch()
1766
1820
        my_config = config.BranchConfig(branch)
1767
 
        self.assertEqual("Robert Collins <robertc@example.net>",
1768
 
                         my_config.username())
 
1821
        self.assertIsNot(None, my_config.username())
1769
1822
        my_config.branch.control_files.files['email'] = "John"
1770
1823
        my_config.set_user_option('email',
1771
1824
                                  "Robert Collins <robertc@example.org>")
1772
 
        self.assertEqual("John", my_config.username())
1773
 
        del my_config.branch.control_files.files['email']
1774
1825
        self.assertEqual("Robert Collins <robertc@example.org>",
1775
 
                         my_config.username())
1776
 
 
1777
 
    def test_not_set_in_branch(self):
1778
 
        my_config = self.get_branch_config(global_config=sample_config_text)
1779
 
        self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
1780
 
                         my_config._get_user_id())
1781
 
        my_config.branch.control_files.files['email'] = "John"
1782
 
        self.assertEqual("John", my_config._get_user_id())
 
1826
                        my_config.username())
1783
1827
 
1784
1828
    def test_BZR_EMAIL_OVERRIDES(self):
1785
1829
        self.overrideEnv('BZR_EMAIL', "Robert Collins <robertc@example.org>")
1791
1835
    def test_signatures_forced(self):
1792
1836
        my_config = self.get_branch_config(
1793
1837
            global_config=sample_always_signatures)
1794
 
        self.assertEqual(config.CHECK_NEVER, my_config.signature_checking())
1795
 
        self.assertEqual(config.SIGN_ALWAYS, my_config.signing_policy())
1796
 
        self.assertTrue(my_config.signature_needed())
 
1838
        self.assertEqual(config.CHECK_NEVER,
 
1839
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1840
                my_config.signature_checking))
 
1841
        self.assertEqual(config.SIGN_ALWAYS,
 
1842
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1843
                my_config.signing_policy))
 
1844
        self.assertTrue(self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1845
            my_config.signature_needed))
1797
1846
 
1798
1847
    def test_signatures_forced_branch(self):
1799
1848
        my_config = self.get_branch_config(
1800
1849
            global_config=sample_ignore_signatures,
1801
1850
            branch_data_config=sample_always_signatures)
1802
 
        self.assertEqual(config.CHECK_NEVER, my_config.signature_checking())
1803
 
        self.assertEqual(config.SIGN_ALWAYS, my_config.signing_policy())
1804
 
        self.assertTrue(my_config.signature_needed())
 
1851
        self.assertEqual(config.CHECK_NEVER,
 
1852
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1853
                my_config.signature_checking))
 
1854
        self.assertEqual(config.SIGN_ALWAYS,
 
1855
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1856
                my_config.signing_policy))
 
1857
        self.assertTrue(self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1858
            my_config.signature_needed))
1805
1859
 
1806
1860
    def test_gpg_signing_command(self):
1807
1861
        my_config = self.get_branch_config(
1808
1862
            global_config=sample_config_text,
1809
1863
            # branch data cannot set gpg_signing_command
1810
1864
            branch_data_config="gpg_signing_command=pgp")
1811
 
        self.assertEqual('gnome-gpg', my_config.gpg_signing_command())
 
1865
        self.assertEqual('gnome-gpg',
 
1866
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1867
                my_config.gpg_signing_command))
1812
1868
 
1813
1869
    def test_get_user_option_global(self):
1814
1870
        my_config = self.get_branch_config(global_config=sample_config_text)
1821
1877
                                      location_config=sample_branches_text)
1822
1878
        self.assertEqual(my_config.branch.base, '/a/c')
1823
1879
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1824
 
                         my_config.post_commit())
 
1880
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1881
                                              my_config.post_commit))
1825
1882
        my_config.set_user_option('post_commit', 'rmtree_root')
1826
1883
        # post-commit is ignored when present in branch data
1827
1884
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1828
 
                         my_config.post_commit())
 
1885
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1886
                                              my_config.post_commit))
1829
1887
        my_config.set_user_option('post_commit', 'rmtree_root',
1830
1888
                                  store=config.STORE_LOCATION)
1831
 
        self.assertEqual('rmtree_root', my_config.post_commit())
 
1889
        self.assertEqual('rmtree_root',
 
1890
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1891
                                              my_config.post_commit))
1832
1892
 
1833
1893
    def test_config_precedence(self):
1834
1894
        # FIXME: eager test, luckily no persitent config file makes it fail
1968
2028
        conf = config.TransportConfig(t, 'foo.conf')
1969
2029
        self.assertRaises(errors.ParseConfigError, conf._get_configobj)
1970
2030
 
 
2031
    def test_load_permission_denied(self):
 
2032
        """Ensure we get an empty config file if the file is inaccessible."""
 
2033
        warnings = []
 
2034
        def warning(*args):
 
2035
            warnings.append(args[0] % args[1:])
 
2036
        self.overrideAttr(trace, 'warning', warning)
 
2037
 
 
2038
        class DenyingTransport(object):
 
2039
 
 
2040
            def __init__(self, base):
 
2041
                self.base = base
 
2042
 
 
2043
            def get_bytes(self, relpath):
 
2044
                raise errors.PermissionDenied(relpath, "")
 
2045
 
 
2046
        cfg = config.TransportConfig(
 
2047
            DenyingTransport("nonexisting://"), 'control.conf')
 
2048
        self.assertIs(None, cfg.get_option('non-existant', 'SECTION'))
 
2049
        self.assertEquals(
 
2050
            warnings,
 
2051
            [u'Permission denied while trying to open configuration file '
 
2052
             u'nonexisting:///control.conf.'])
 
2053
 
1971
2054
    def test_get_value(self):
1972
2055
        """Test that retreiving a value from a section is possible"""
1973
2056
        bzrdir_config = config.TransportConfig(self.get_transport('.'),
2253
2336
        opt = config.Option('foo', default='bar')
2254
2337
        self.assertEquals('bar', opt.get_default())
2255
2338
 
 
2339
    def test_callable_default_value(self):
 
2340
        def bar_as_unicode():
 
2341
            return u'bar'
 
2342
        opt = config.Option('foo', default=bar_as_unicode)
 
2343
        self.assertEquals('bar', opt.get_default())
 
2344
 
2256
2345
    def test_default_value_from_env(self):
2257
2346
        opt = config.Option('foo', default='bar', default_from_env=['FOO'])
2258
2347
        self.overrideEnv('FOO', 'quux')
2259
2348
        # Env variable provides a default taking over the option one
2260
2349
        self.assertEquals('quux', opt.get_default())
2261
 
        
 
2350
 
2262
2351
    def test_first_default_value_from_env_wins(self):
2263
2352
        opt = config.Option('foo', default='bar',
2264
2353
                            default_from_env=['NO_VALUE', 'FOO', 'BAZ'])
2267
2356
        # The first env var set wins
2268
2357
        self.assertEquals('foo', opt.get_default())
2269
2358
 
 
2359
    def test_not_supported_list_default_value(self):
 
2360
        self.assertRaises(AssertionError, config.Option, 'foo', default=[1])
 
2361
 
 
2362
    def test_not_supported_object_default_value(self):
 
2363
        self.assertRaises(AssertionError, config.Option, 'foo',
 
2364
                          default=object())
 
2365
 
 
2366
    def test_not_supported_callable_default_value_not_unicode(self):
 
2367
        def bar_not_unicode():
 
2368
            return 'bar'
 
2369
        opt = config.Option('foo', default=bar_not_unicode)
 
2370
        self.assertRaises(AssertionError, opt.get_default)
 
2371
 
 
2372
 
 
2373
class TestOptionConverterMixin(object):
 
2374
 
 
2375
    def assertConverted(self, expected, opt, value):
 
2376
        self.assertEquals(expected, opt.convert_from_unicode(None, value))
 
2377
 
 
2378
    def assertWarns(self, opt, value):
 
2379
        warnings = []
 
2380
        def warning(*args):
 
2381
            warnings.append(args[0] % args[1:])
 
2382
        self.overrideAttr(trace, 'warning', warning)
 
2383
        self.assertEquals(None, opt.convert_from_unicode(None, value))
 
2384
        self.assertLength(1, warnings)
 
2385
        self.assertEquals(
 
2386
            'Value "%s" is not valid for "%s"' % (value, opt.name),
 
2387
            warnings[0])
 
2388
 
 
2389
    def assertErrors(self, opt, value):
 
2390
        self.assertRaises(errors.ConfigOptionValueError,
 
2391
                          opt.convert_from_unicode, None, value)
 
2392
 
 
2393
    def assertConvertInvalid(self, opt, invalid_value):
 
2394
        opt.invalid = None
 
2395
        self.assertEquals(None, opt.convert_from_unicode(None, invalid_value))
 
2396
        opt.invalid = 'warning'
 
2397
        self.assertWarns(opt, invalid_value)
 
2398
        opt.invalid = 'error'
 
2399
        self.assertErrors(opt, invalid_value)
 
2400
 
 
2401
 
 
2402
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
 
2403
 
 
2404
    def get_option(self):
 
2405
        return config.Option('foo', help='A boolean.',
 
2406
                             from_unicode=config.bool_from_store)
 
2407
 
 
2408
    def test_convert_invalid(self):
 
2409
        opt = self.get_option()
 
2410
        # A string that is not recognized as a boolean
 
2411
        self.assertConvertInvalid(opt, u'invalid-boolean')
 
2412
        # A list of strings is never recognized as a boolean
 
2413
        self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
 
2414
 
 
2415
    def test_convert_valid(self):
 
2416
        opt = self.get_option()
 
2417
        self.assertConverted(True, opt, u'True')
 
2418
        self.assertConverted(True, opt, u'1')
 
2419
        self.assertConverted(False, opt, u'False')
 
2420
 
 
2421
 
 
2422
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
 
2423
 
 
2424
    def get_option(self):
 
2425
        return config.Option('foo', help='An integer.',
 
2426
                             from_unicode=config.int_from_store)
 
2427
 
 
2428
    def test_convert_invalid(self):
 
2429
        opt = self.get_option()
 
2430
        # A string that is not recognized as an integer
 
2431
        self.assertConvertInvalid(opt, u'forty-two')
 
2432
        # A list of strings is never recognized as an integer
 
2433
        self.assertConvertInvalid(opt, [u'a', u'list'])
 
2434
 
 
2435
    def test_convert_valid(self):
 
2436
        opt = self.get_option()
 
2437
        self.assertConverted(16, opt, u'16')
 
2438
 
 
2439
 
 
2440
class TestOptionWithSIUnitConverter(tests.TestCase, TestOptionConverterMixin):
 
2441
 
 
2442
    def get_option(self):
 
2443
        return config.Option('foo', help='An integer in SI units.',
 
2444
                             from_unicode=config.int_SI_from_store)
 
2445
 
 
2446
    def test_convert_invalid(self):
 
2447
        opt = self.get_option()
 
2448
        self.assertConvertInvalid(opt, u'not-a-unit')
 
2449
        self.assertConvertInvalid(opt, u'Gb') # Forgot the int
 
2450
        self.assertConvertInvalid(opt, u'1b') # Forgot the unit
 
2451
        self.assertConvertInvalid(opt, u'1GG')
 
2452
        self.assertConvertInvalid(opt, u'1Mbb')
 
2453
        self.assertConvertInvalid(opt, u'1MM')
 
2454
 
 
2455
    def test_convert_valid(self):
 
2456
        opt = self.get_option()
 
2457
        self.assertConverted(int(5e3), opt, u'5kb')
 
2458
        self.assertConverted(int(5e6), opt, u'5M')
 
2459
        self.assertConverted(int(5e6), opt, u'5MB')
 
2460
        self.assertConverted(int(5e9), opt, u'5g')
 
2461
        self.assertConverted(int(5e9), opt, u'5gB')
 
2462
        self.assertConverted(100, opt, u'100')
 
2463
 
 
2464
 
 
2465
class TestListOption(tests.TestCase, TestOptionConverterMixin):
 
2466
 
 
2467
    def get_option(self):
 
2468
        return config.ListOption('foo', help='A list.')
 
2469
 
 
2470
    def test_convert_invalid(self):
 
2471
        opt = self.get_option()
 
2472
        # We don't even try to convert a list into a list, we only expect
 
2473
        # strings
 
2474
        self.assertConvertInvalid(opt, [1])
 
2475
        # No string is invalid as all forms can be converted to a list
 
2476
 
 
2477
    def test_convert_valid(self):
 
2478
        opt = self.get_option()
 
2479
        # An empty string is an empty list
 
2480
        self.assertConverted([], opt, '') # Using a bare str() just in case
 
2481
        self.assertConverted([], opt, u'')
 
2482
        # A boolean
 
2483
        self.assertConverted([u'True'], opt, u'True')
 
2484
        # An integer
 
2485
        self.assertConverted([u'42'], opt, u'42')
 
2486
        # A single string
 
2487
        self.assertConverted([u'bar'], opt, u'bar')
 
2488
 
2270
2489
 
2271
2490
class TestOptionRegistry(tests.TestCase):
2272
2491
 
2347
2566
 
2348
2567
class TestMutableSection(tests.TestCase):
2349
2568
 
2350
 
    # FIXME: Parametrize so that all sections (including os.environ and the
2351
 
    # ones produced by Stores) run these tests -- vila 2011-04-01
 
2569
    scenarios = [('mutable',
 
2570
                  {'get_section':
 
2571
                       lambda opts: config.MutableSection('myID', opts)},),
 
2572
        ]
2352
2573
 
2353
2574
    def test_set(self):
2354
2575
        a_dict = dict(foo='bar')
2355
 
        section = config.MutableSection('myID', a_dict)
 
2576
        section = self.get_section(a_dict)
2356
2577
        section.set('foo', 'new_value')
2357
2578
        self.assertEquals('new_value', section.get('foo'))
2358
2579
        # The change appears in the shared section
2363
2584
 
2364
2585
    def test_set_preserve_original_once(self):
2365
2586
        a_dict = dict(foo='bar')
2366
 
        section = config.MutableSection('myID', a_dict)
 
2587
        section = self.get_section(a_dict)
2367
2588
        section.set('foo', 'first_value')
2368
2589
        section.set('foo', 'second_value')
2369
2590
        # We keep track of the original value
2372
2593
 
2373
2594
    def test_remove(self):
2374
2595
        a_dict = dict(foo='bar')
2375
 
        section = config.MutableSection('myID', a_dict)
 
2596
        section = self.get_section(a_dict)
2376
2597
        section.remove('foo')
2377
2598
        # We get None for unknown options via the default value
2378
2599
        self.assertEquals(None, section.get('foo'))
2385
2606
 
2386
2607
    def test_remove_new_option(self):
2387
2608
        a_dict = dict()
2388
 
        section = config.MutableSection('myID', a_dict)
 
2609
        section = self.get_section(a_dict)
2389
2610
        section.set('foo', 'bar')
2390
2611
        section.remove('foo')
2391
2612
        self.assertFalse('foo' in section.options)
2395
2616
        self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
2396
2617
 
2397
2618
 
 
2619
class TestCommandLineStore(tests.TestCase):
 
2620
 
 
2621
    def setUp(self):
 
2622
        super(TestCommandLineStore, self).setUp()
 
2623
        self.store = config.CommandLineStore()
 
2624
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
 
2625
 
 
2626
    def get_section(self):
 
2627
        """Get the unique section for the command line overrides."""
 
2628
        sections = list(self.store.get_sections())
 
2629
        self.assertLength(1, sections)
 
2630
        store, section = sections[0]
 
2631
        self.assertEquals(self.store, store)
 
2632
        return section
 
2633
 
 
2634
    def test_no_override(self):
 
2635
        self.store._from_cmdline([])
 
2636
        section = self.get_section()
 
2637
        self.assertLength(0, list(section.iter_option_names()))
 
2638
 
 
2639
    def test_simple_override(self):
 
2640
        self.store._from_cmdline(['a=b'])
 
2641
        section = self.get_section()
 
2642
        self.assertEqual('b', section.get('a'))
 
2643
 
 
2644
    def test_list_override(self):
 
2645
        opt = config.ListOption('l')
 
2646
        config.option_registry.register(opt)
 
2647
        self.store._from_cmdline(['l=1,2,3'])
 
2648
        val = self.get_section().get('l')
 
2649
        self.assertEqual('1,2,3', val)
 
2650
        # Reminder: lists should be registered as such explicitely, otherwise
 
2651
        # the conversion needs to be done afterwards.
 
2652
        self.assertEqual(['1', '2', '3'],
 
2653
                         opt.convert_from_unicode(self.store, val))
 
2654
 
 
2655
    def test_multiple_overrides(self):
 
2656
        self.store._from_cmdline(['a=b', 'x=y'])
 
2657
        section = self.get_section()
 
2658
        self.assertEquals('b', section.get('a'))
 
2659
        self.assertEquals('y', section.get('x'))
 
2660
 
 
2661
    def test_wrong_syntax(self):
 
2662
        self.assertRaises(errors.BzrCommandError,
 
2663
                          self.store._from_cmdline, ['a=b', 'c'])
 
2664
 
 
2665
class TestStoreMinimalAPI(tests.TestCaseWithTransport):
 
2666
 
 
2667
    scenarios = [(key, {'get_store': builder}) for key, builder
 
2668
                 in config.test_store_builder_registry.iteritems()] + [
 
2669
        ('cmdline', {'get_store': lambda test: config.CommandLineStore()})]
 
2670
 
 
2671
    def test_id(self):
 
2672
        store = self.get_store(self)
 
2673
        if type(store) == config.TransportIniFileStore:
 
2674
            raise tests.TestNotApplicable(
 
2675
                "%s is not a concrete Store implementation"
 
2676
                " so it doesn't need an id" % (store.__class__.__name__,))
 
2677
        self.assertIsNot(None, store.id)
 
2678
 
 
2679
 
2398
2680
class TestStore(tests.TestCaseWithTransport):
2399
2681
 
2400
 
    def assertSectionContent(self, expected, section):
 
2682
    def assertSectionContent(self, expected, (store, section)):
2401
2683
        """Assert that some options have the proper values in a section."""
2402
2684
        expected_name, expected_options = expected
2403
2685
        self.assertEquals(expected_name, section.id)
2411
2693
    scenarios = [(key, {'get_store': builder}) for key, builder
2412
2694
                 in config.test_store_builder_registry.iteritems()]
2413
2695
 
2414
 
    def setUp(self):
2415
 
        super(TestReadonlyStore, self).setUp()
2416
 
 
2417
2696
    def test_building_delays_load(self):
2418
2697
        store = self.get_store(self)
2419
2698
        self.assertEquals(False, store.is_loaded())
2445
2724
        self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2446
2725
 
2447
2726
 
 
2727
class TestStoreQuoting(TestStore):
 
2728
 
 
2729
    scenarios = [(key, {'get_store': builder}) for key, builder
 
2730
                 in config.test_store_builder_registry.iteritems()]
 
2731
 
 
2732
    def setUp(self):
 
2733
        super(TestStoreQuoting, self).setUp()
 
2734
        self.store = self.get_store(self)
 
2735
        # We need a loaded store but any content will do
 
2736
        self.store._load_from_string('')
 
2737
 
 
2738
    def assertIdempotent(self, s):
 
2739
        """Assert that quoting an unquoted string is a no-op and vice-versa.
 
2740
 
 
2741
        What matters here is that option values, as they appear in a store, can
 
2742
        be safely round-tripped out of the store and back.
 
2743
 
 
2744
        :param s: A string, quoted if required.
 
2745
        """
 
2746
        self.assertEquals(s, self.store.quote(self.store.unquote(s)))
 
2747
        self.assertEquals(s, self.store.unquote(self.store.quote(s)))
 
2748
 
 
2749
    def test_empty_string(self):
 
2750
        if isinstance(self.store, config.IniFileStore):
 
2751
            # configobj._quote doesn't handle empty values
 
2752
            self.assertRaises(AssertionError,
 
2753
                              self.assertIdempotent, '')
 
2754
        else:
 
2755
            self.assertIdempotent('')
 
2756
        # But quoted empty strings are ok
 
2757
        self.assertIdempotent('""')
 
2758
 
 
2759
    def test_embedded_spaces(self):
 
2760
        self.assertIdempotent('" a b c "')
 
2761
 
 
2762
    def test_embedded_commas(self):
 
2763
        self.assertIdempotent('" a , b c "')
 
2764
 
 
2765
    def test_simple_comma(self):
 
2766
        if isinstance(self.store, config.IniFileStore):
 
2767
            # configobj requires that lists are special-cased
 
2768
           self.assertRaises(AssertionError,
 
2769
                             self.assertIdempotent, ',')
 
2770
        else:
 
2771
            self.assertIdempotent(',')
 
2772
        # When a single comma is required, quoting is also required
 
2773
        self.assertIdempotent('","')
 
2774
 
 
2775
    def test_list(self):
 
2776
        if isinstance(self.store, config.IniFileStore):
 
2777
            # configobj requires that lists are special-cased
 
2778
            self.assertRaises(AssertionError,
 
2779
                              self.assertIdempotent, 'a,b')
 
2780
        else:
 
2781
            self.assertIdempotent('a,b')
 
2782
 
 
2783
 
 
2784
class TestDictFromStore(tests.TestCase):
 
2785
 
 
2786
    def test_unquote_not_string(self):
 
2787
        conf = config.MemoryStack('x=2\n[a_section]\na=1\n')
 
2788
        value = conf.get('a_section')
 
2789
        # Urgh, despite 'conf' asking for the no-name section, we get the
 
2790
        # content of another section as a dict o_O
 
2791
        self.assertEquals({'a': '1'}, value)
 
2792
        unquoted = conf.store.unquote(value)
 
2793
        # Which cannot be unquoted but shouldn't crash either (the use cases
 
2794
        # are getting the value or displaying it. In the later case, '%s' will
 
2795
        # do).
 
2796
        self.assertEquals({'a': '1'}, unquoted)
 
2797
        self.assertEquals("{u'a': u'1'}", '%s' % (unquoted,))
 
2798
 
 
2799
 
2448
2800
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2449
 
    """Simulate loading a config store without content of various encodings.
 
2801
    """Simulate loading a config store with content of various encodings.
2450
2802
 
2451
2803
    All files produced by bzr are in utf8 content.
2452
2804
 
2465
2817
        utf8_content = unicode_content.encode('utf8')
2466
2818
        # Store the raw content in the config file
2467
2819
        t.put_bytes('foo.conf', utf8_content)
2468
 
        store = config.IniFileStore(t, 'foo.conf')
 
2820
        store = config.TransportIniFileStore(t, 'foo.conf')
2469
2821
        store.load()
2470
2822
        stack = config.Stack([store.get_sections], store)
2471
2823
        self.assertEquals(unicode_user, stack.get('user'))
2474
2826
        """Ensure we display a proper error on non-ascii, non utf-8 content."""
2475
2827
        t = self.get_transport()
2476
2828
        t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2477
 
        store = config.IniFileStore(t, 'foo.conf')
 
2829
        store = config.TransportIniFileStore(t, 'foo.conf')
2478
2830
        self.assertRaises(errors.ConfigContentError, store.load)
2479
2831
 
2480
2832
    def test_load_erroneous_content(self):
2481
2833
        """Ensure we display a proper error on content that can't be parsed."""
2482
2834
        t = self.get_transport()
2483
2835
        t.put_bytes('foo.conf', '[open_section\n')
2484
 
        store = config.IniFileStore(t, 'foo.conf')
 
2836
        store = config.TransportIniFileStore(t, 'foo.conf')
2485
2837
        self.assertRaises(errors.ParseConfigError, store.load)
2486
2838
 
 
2839
    def test_load_permission_denied(self):
 
2840
        """Ensure we get warned when trying to load an inaccessible file."""
 
2841
        warnings = []
 
2842
        def warning(*args):
 
2843
            warnings.append(args[0] % args[1:])
 
2844
        self.overrideAttr(trace, 'warning', warning)
 
2845
 
 
2846
        t = self.get_transport()
 
2847
 
 
2848
        def get_bytes(relpath):
 
2849
            raise errors.PermissionDenied(relpath, "")
 
2850
        t.get_bytes = get_bytes
 
2851
        store = config.TransportIniFileStore(t, 'foo.conf')
 
2852
        self.assertRaises(errors.PermissionDenied, store.load)
 
2853
        self.assertEquals(
 
2854
            warnings,
 
2855
            [u'Permission denied while trying to load configuration store %s.'
 
2856
             % store.external_url()])
 
2857
 
2487
2858
 
2488
2859
class TestIniConfigContent(tests.TestCaseWithTransport):
2489
 
    """Simulate loading a IniBasedConfig without content of various encodings.
 
2860
    """Simulate loading a IniBasedConfig with content of various encodings.
2490
2861
 
2491
2862
    All files produced by bzr are in utf8 content.
2492
2863
 
2636
3007
        self.assertLength(1, calls)
2637
3008
        self.assertEquals((store,), calls[0])
2638
3009
 
2639
 
 
2640
 
class TestIniFileStore(TestStore):
 
3010
    def test_set_mark_dirty(self):
 
3011
        stack = config.MemoryStack('')
 
3012
        self.assertLength(0, stack.store.dirty_sections)
 
3013
        stack.set('foo', 'baz')
 
3014
        self.assertLength(1, stack.store.dirty_sections)
 
3015
        self.assertTrue(stack.store._need_saving())
 
3016
 
 
3017
    def test_remove_mark_dirty(self):
 
3018
        stack = config.MemoryStack('foo=bar')
 
3019
        self.assertLength(0, stack.store.dirty_sections)
 
3020
        stack.remove('foo')
 
3021
        self.assertLength(1, stack.store.dirty_sections)
 
3022
        self.assertTrue(stack.store._need_saving())
 
3023
 
 
3024
 
 
3025
class TestStoreSaveChanges(tests.TestCaseWithTransport):
 
3026
    """Tests that config changes are kept in memory and saved on-demand."""
 
3027
 
 
3028
    def setUp(self):
 
3029
        super(TestStoreSaveChanges, self).setUp()
 
3030
        self.transport = self.get_transport()
 
3031
        # Most of the tests involve two stores pointing to the same persistent
 
3032
        # storage to observe the effects of concurrent changes
 
3033
        self.st1 = config.TransportIniFileStore(self.transport, 'foo.conf')
 
3034
        self.st2 = config.TransportIniFileStore(self.transport, 'foo.conf')
 
3035
        self.warnings = []
 
3036
        def warning(*args):
 
3037
            self.warnings.append(args[0] % args[1:])
 
3038
        self.overrideAttr(trace, 'warning', warning)
 
3039
 
 
3040
    def has_store(self, store):
 
3041
        store_basename = urlutils.relative_url(self.transport.external_url(),
 
3042
                                               store.external_url())
 
3043
        return self.transport.has(store_basename)
 
3044
 
 
3045
    def get_stack(self, store):
 
3046
        # Any stack will do as long as it uses the right store, just a single
 
3047
        # no-name section is enough
 
3048
        return config.Stack([store.get_sections], store)
 
3049
 
 
3050
    def test_no_changes_no_save(self):
 
3051
        s = self.get_stack(self.st1)
 
3052
        s.store.save_changes()
 
3053
        self.assertEquals(False, self.has_store(self.st1))
 
3054
 
 
3055
    def test_unrelated_concurrent_update(self):
 
3056
        s1 = self.get_stack(self.st1)
 
3057
        s2 = self.get_stack(self.st2)
 
3058
        s1.set('foo', 'bar')
 
3059
        s2.set('baz', 'quux')
 
3060
        s1.store.save()
 
3061
        # Changes don't propagate magically
 
3062
        self.assertEquals(None, s1.get('baz'))
 
3063
        s2.store.save_changes()
 
3064
        self.assertEquals('quux', s2.get('baz'))
 
3065
        # Changes are acquired when saving
 
3066
        self.assertEquals('bar', s2.get('foo'))
 
3067
        # Since there is no overlap, no warnings are emitted
 
3068
        self.assertLength(0, self.warnings)
 
3069
 
 
3070
    def test_concurrent_update_modified(self):
 
3071
        s1 = self.get_stack(self.st1)
 
3072
        s2 = self.get_stack(self.st2)
 
3073
        s1.set('foo', 'bar')
 
3074
        s2.set('foo', 'baz')
 
3075
        s1.store.save()
 
3076
        # Last speaker wins
 
3077
        s2.store.save_changes()
 
3078
        self.assertEquals('baz', s2.get('foo'))
 
3079
        # But the user get a warning
 
3080
        self.assertLength(1, self.warnings)
 
3081
        warning = self.warnings[0]
 
3082
        self.assertStartsWith(warning, 'Option foo in section None')
 
3083
        self.assertEndsWith(warning, 'was changed from <CREATED> to bar.'
 
3084
                            ' The baz value will be saved.')
 
3085
 
 
3086
    def test_concurrent_deletion(self):
 
3087
        self.st1._load_from_string('foo=bar')
 
3088
        self.st1.save()
 
3089
        s1 = self.get_stack(self.st1)
 
3090
        s2 = self.get_stack(self.st2)
 
3091
        s1.remove('foo')
 
3092
        s2.remove('foo')
 
3093
        s1.store.save_changes()
 
3094
        # No warning yet
 
3095
        self.assertLength(0, self.warnings)
 
3096
        s2.store.save_changes()
 
3097
        # Now we get one
 
3098
        self.assertLength(1, self.warnings)
 
3099
        warning = self.warnings[0]
 
3100
        self.assertStartsWith(warning, 'Option foo in section None')
 
3101
        self.assertEndsWith(warning, 'was changed from bar to <CREATED>.'
 
3102
                            ' The <DELETED> value will be saved.')
 
3103
 
 
3104
 
 
3105
class TestQuotingIniFileStore(tests.TestCaseWithTransport):
 
3106
 
 
3107
    def get_store(self):
 
3108
        return config.TransportIniFileStore(self.get_transport(), 'foo.conf')
 
3109
 
 
3110
    def test_get_quoted_string(self):
 
3111
        store = self.get_store()
 
3112
        store._load_from_string('foo= " abc "')
 
3113
        stack = config.Stack([store.get_sections])
 
3114
        self.assertEquals(' abc ', stack.get('foo'))
 
3115
 
 
3116
    def test_set_quoted_string(self):
 
3117
        store = self.get_store()
 
3118
        stack = config.Stack([store.get_sections], store)
 
3119
        stack.set('foo', ' a b c ')
 
3120
        store.save()
 
3121
        self.assertFileEqual('foo = " a b c "' + os.linesep, 'foo.conf')
 
3122
 
 
3123
 
 
3124
class TestTransportIniFileStore(TestStore):
2641
3125
 
2642
3126
    def test_loading_unknown_file_fails(self):
2643
 
        store = config.IniFileStore(self.get_transport(), 'I-do-not-exist')
 
3127
        store = config.TransportIniFileStore(self.get_transport(),
 
3128
            'I-do-not-exist')
2644
3129
        self.assertRaises(errors.NoSuchFile, store.load)
2645
3130
 
2646
3131
    def test_invalid_content(self):
2647
 
        store = config.IniFileStore(self.get_transport(), 'foo.conf', )
 
3132
        store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
2648
3133
        self.assertEquals(False, store.is_loaded())
2649
3134
        exc = self.assertRaises(
2650
3135
            errors.ParseConfigError, store._load_from_string,
2658
3143
        # option names share the same name space...)
2659
3144
        # FIXME: This should be fixed by forbidding dicts as values ?
2660
3145
        # -- vila 2011-04-05
2661
 
        store = config.IniFileStore(self.get_transport(), 'foo.conf', )
 
3146
        store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
2662
3147
        store._load_from_string('''
2663
3148
foo=bar
2664
3149
l=1,2
2674
3159
        sections = list(store.get_sections())
2675
3160
        self.assertLength(4, sections)
2676
3161
        # The default section has no name.
2677
 
        # List values are provided as lists
2678
 
        self.assertSectionContent((None, {'foo': 'bar', 'l': ['1', '2']}),
 
3162
        # List values are provided as strings and need to be explicitly
 
3163
        # converted by specifying from_unicode=list_from_store at option
 
3164
        # registration
 
3165
        self.assertSectionContent((None, {'foo': 'bar', 'l': u'1,2'}),
2679
3166
                                  sections[0])
2680
3167
        self.assertSectionContent(
2681
3168
            ('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
2711
3198
 
2712
3199
    def setUp(self):
2713
3200
        super(TestConcurrentStoreUpdates, self).setUp()
2714
 
        self._content = 'one=1\ntwo=2\n'
2715
3201
        self.stack = self.get_stack(self)
2716
3202
        if not isinstance(self.stack, config._CompatibleStack):
2717
3203
            raise tests.TestNotApplicable(
2718
3204
                '%s is not meant to be compatible with the old config design'
2719
3205
                % (self.stack,))
2720
 
        self.stack.store._load_from_string(self._content)
 
3206
        self.stack.set('one', '1')
 
3207
        self.stack.set('two', '2')
2721
3208
        # Flush the store
2722
3209
        self.stack.store.save()
2723
3210
 
2827
3314
    # FIXME: It may be worth looking into removing the lock dir when it's not
2828
3315
    # needed anymore and look at possible fallouts for concurrent lockers. This
2829
3316
    # will matter if/when we use config files outside of bazaar directories
2830
 
    # (.bazaar or .bzr) -- vila 20110-04-11
 
3317
    # (.bazaar or .bzr) -- vila 20110-04-111
2831
3318
 
2832
3319
 
2833
3320
class TestSectionMatcher(TestStore):
2834
3321
 
2835
 
    scenarios = [('location', {'matcher': config.LocationMatcher})]
 
3322
    scenarios = [('location', {'matcher': config.LocationMatcher}),
 
3323
                 ('id', {'matcher': config.NameMatcher}),]
2836
3324
 
2837
 
    def get_store(self, file_name):
2838
 
        return config.IniFileStore(self.get_readonly_transport(), file_name)
 
3325
    def setUp(self):
 
3326
        super(TestSectionMatcher, self).setUp()
 
3327
        # Any simple store is good enough
 
3328
        self.get_store = config.test_store_builder_registry.get('configobj')
2839
3329
 
2840
3330
    def test_no_matches_for_empty_stores(self):
2841
 
        store = self.get_store('foo.conf')
 
3331
        store = self.get_store(self)
2842
3332
        store._load_from_string('')
2843
3333
        matcher = self.matcher(store, '/bar')
2844
3334
        self.assertEquals([], list(matcher.get_sections()))
2845
3335
 
2846
3336
    def test_build_doesnt_load_store(self):
2847
 
        store = self.get_store('foo.conf')
 
3337
        store = self.get_store(self)
2848
3338
        matcher = self.matcher(store, '/bar')
2849
3339
        self.assertFalse(store.is_loaded())
2850
3340
 
2853
3343
 
2854
3344
    def get_section(self, options, extra_path):
2855
3345
        section = config.Section('foo', options)
2856
 
        # We don't care about the length so we use '0'
2857
 
        return config.LocationSection(section, 0, extra_path)
 
3346
        return config.LocationSection(section, extra_path)
2858
3347
 
2859
3348
    def test_simple_option(self):
2860
3349
        section = self.get_section({'foo': 'bar'}, '')
2874
3363
 
2875
3364
class TestLocationMatcher(TestStore):
2876
3365
 
2877
 
    def get_store(self, file_name):
2878
 
        return config.IniFileStore(self.get_readonly_transport(), file_name)
 
3366
    def setUp(self):
 
3367
        super(TestLocationMatcher, self).setUp()
 
3368
        # Any simple store is good enough
 
3369
        self.get_store = config.test_store_builder_registry.get('configobj')
2879
3370
 
2880
3371
    def test_unrelated_section_excluded(self):
2881
 
        store = self.get_store('foo.conf')
 
3372
        store = self.get_store(self)
2882
3373
        store._load_from_string('''
2883
3374
[/foo]
2884
3375
section=/foo
2893
3384
''')
2894
3385
        self.assertEquals(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
2895
3386
                           '/quux/quux'],
2896
 
                          [section.id for section in store.get_sections()])
 
3387
                          [section.id for _, section in store.get_sections()])
2897
3388
        matcher = config.LocationMatcher(store, '/foo/bar/quux')
2898
 
        sections = list(matcher.get_sections())
2899
 
        self.assertEquals([3, 2],
2900
 
                          [section.length for section in sections])
 
3389
        sections = [section for _, section in matcher.get_sections()]
2901
3390
        self.assertEquals(['/foo/bar', '/foo'],
2902
3391
                          [section.id for section in sections])
2903
3392
        self.assertEquals(['quux', 'bar/quux'],
2904
3393
                          [section.extra_path for section in sections])
2905
3394
 
2906
3395
    def test_more_specific_sections_first(self):
2907
 
        store = self.get_store('foo.conf')
 
3396
        store = self.get_store(self)
2908
3397
        store._load_from_string('''
2909
3398
[/foo]
2910
3399
section=/foo
2912
3401
section=/foo/bar
2913
3402
''')
2914
3403
        self.assertEquals(['/foo', '/foo/bar'],
2915
 
                          [section.id for section in store.get_sections()])
 
3404
                          [section.id for _, section in store.get_sections()])
2916
3405
        matcher = config.LocationMatcher(store, '/foo/bar/baz')
2917
 
        sections = list(matcher.get_sections())
2918
 
        self.assertEquals([3, 2],
2919
 
                          [section.length for section in sections])
 
3406
        sections = [section for _, section in matcher.get_sections()]
2920
3407
        self.assertEquals(['/foo/bar', '/foo'],
2921
3408
                          [section.id for section in sections])
2922
3409
        self.assertEquals(['baz', 'bar/baz'],
2925
3412
    def test_appendpath_in_no_name_section(self):
2926
3413
        # It's a bit weird to allow appendpath in a no-name section, but
2927
3414
        # someone may found a use for it
2928
 
        store = self.get_store('foo.conf')
 
3415
        store = self.get_store(self)
2929
3416
        store._load_from_string('''
2930
3417
foo=bar
2931
3418
foo:policy = appendpath
2933
3420
        matcher = config.LocationMatcher(store, 'dir/subdir')
2934
3421
        sections = list(matcher.get_sections())
2935
3422
        self.assertLength(1, sections)
2936
 
        self.assertEquals('bar/dir/subdir', sections[0].get('foo'))
 
3423
        self.assertEquals('bar/dir/subdir', sections[0][1].get('foo'))
2937
3424
 
2938
3425
    def test_file_urls_are_normalized(self):
2939
 
        store = self.get_store('foo.conf')
 
3426
        store = self.get_store(self)
2940
3427
        if sys.platform == 'win32':
2941
3428
            expected_url = 'file:///C:/dir/subdir'
2942
3429
            expected_location = 'C:/dir/subdir'
2947
3434
        self.assertEquals(expected_location, matcher.location)
2948
3435
 
2949
3436
 
2950
 
class TestStackGet(tests.TestCase):
2951
 
 
2952
 
    # FIXME: This should be parametrized for all known Stack or dedicated
2953
 
    # paramerized tests created to avoid bloating -- vila 2011-03-31
2954
 
 
2955
 
    def overrideOptionRegistry(self):
 
3437
class TestStartingPathMatcher(TestStore):
 
3438
 
 
3439
    def setUp(self):
 
3440
        super(TestStartingPathMatcher, self).setUp()
 
3441
        # Any simple store is good enough
 
3442
        self.store = config.IniFileStore()
 
3443
 
 
3444
    def assertSectionIDs(self, expected, location, content):
 
3445
        self.store._load_from_string(content)
 
3446
        matcher = config.StartingPathMatcher(self.store, location)
 
3447
        sections = list(matcher.get_sections())
 
3448
        self.assertLength(len(expected), sections)
 
3449
        self.assertEqual(expected, [section.id for _, section in sections])
 
3450
        return sections
 
3451
 
 
3452
    def test_empty(self):
 
3453
        self.assertSectionIDs([], self.get_url(), '')
 
3454
 
 
3455
    def test_url_vs_local_paths(self):
 
3456
        # The matcher location is an url and the section names are local paths
 
3457
        sections = self.assertSectionIDs(['/foo/bar', '/foo'],
 
3458
                                         'file:///foo/bar/baz', '''\
 
3459
[/foo]
 
3460
[/foo/bar]
 
3461
''')
 
3462
 
 
3463
    def test_local_path_vs_url(self):
 
3464
        # The matcher location is a local path and the section names are urls
 
3465
        sections = self.assertSectionIDs(['file:///foo/bar', 'file:///foo'],
 
3466
                                         '/foo/bar/baz', '''\
 
3467
[file:///foo]
 
3468
[file:///foo/bar]
 
3469
''')
 
3470
 
 
3471
 
 
3472
    def test_no_name_section_included_when_present(self):
 
3473
        # Note that other tests will cover the case where the no-name section
 
3474
        # is empty and as such, not included.
 
3475
        sections = self.assertSectionIDs(['/foo/bar', '/foo', None],
 
3476
                                         '/foo/bar/baz', '''\
 
3477
option = defined so the no-name section exists
 
3478
[/foo]
 
3479
[/foo/bar]
 
3480
''')
 
3481
        self.assertEquals(['baz', 'bar/baz', '/foo/bar/baz'],
 
3482
                          [s.locals['relpath'] for _, s in sections])
 
3483
 
 
3484
    def test_order_reversed(self):
 
3485
        self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
 
3486
[/foo]
 
3487
[/foo/bar]
 
3488
''')
 
3489
 
 
3490
    def test_unrelated_section_excluded(self):
 
3491
        self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
 
3492
[/foo]
 
3493
[/foo/qux]
 
3494
[/foo/bar]
 
3495
''')
 
3496
 
 
3497
    def test_glob_included(self):
 
3498
        sections = self.assertSectionIDs(['/foo/*/baz', '/foo/b*', '/foo'],
 
3499
                                         '/foo/bar/baz', '''\
 
3500
[/foo]
 
3501
[/foo/qux]
 
3502
[/foo/b*]
 
3503
[/foo/*/baz]
 
3504
''')
 
3505
        # Note that 'baz' as a relpath for /foo/b* is not fully correct, but
 
3506
        # nothing really is... as far using {relpath} to append it to something
 
3507
        # else, this seems good enough though.
 
3508
        self.assertEquals(['', 'baz', 'bar/baz'],
 
3509
                          [s.locals['relpath'] for _, s in sections])
 
3510
 
 
3511
    def test_respect_order(self):
 
3512
        self.assertSectionIDs(['/foo', '/foo/b*', '/foo/*/baz'],
 
3513
                              '/foo/bar/baz', '''\
 
3514
[/foo/*/baz]
 
3515
[/foo/qux]
 
3516
[/foo/b*]
 
3517
[/foo]
 
3518
''')
 
3519
 
 
3520
 
 
3521
class TestNameMatcher(TestStore):
 
3522
 
 
3523
    def setUp(self):
 
3524
        super(TestNameMatcher, self).setUp()
 
3525
        self.matcher = config.NameMatcher
 
3526
        # Any simple store is good enough
 
3527
        self.get_store = config.test_store_builder_registry.get('configobj')
 
3528
 
 
3529
    def get_matching_sections(self, name):
 
3530
        store = self.get_store(self)
 
3531
        store._load_from_string('''
 
3532
[foo]
 
3533
option=foo
 
3534
[foo/baz]
 
3535
option=foo/baz
 
3536
[bar]
 
3537
option=bar
 
3538
''')
 
3539
        matcher = self.matcher(store, name)
 
3540
        return list(matcher.get_sections())
 
3541
 
 
3542
    def test_matching(self):
 
3543
        sections = self.get_matching_sections('foo')
 
3544
        self.assertLength(1, sections)
 
3545
        self.assertSectionContent(('foo', {'option': 'foo'}), sections[0])
 
3546
 
 
3547
    def test_not_matching(self):
 
3548
        sections = self.get_matching_sections('baz')
 
3549
        self.assertLength(0, sections)
 
3550
 
 
3551
 
 
3552
class TestBaseStackGet(tests.TestCase):
 
3553
 
 
3554
    def setUp(self):
 
3555
        super(TestBaseStackGet, self).setUp()
2956
3556
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2957
3557
 
2958
 
    def test_single_config_get(self):
2959
 
        conf = dict(foo='bar')
2960
 
        conf_stack = config.Stack([conf])
2961
 
        self.assertEquals('bar', conf_stack.get('foo'))
 
3558
    def test_get_first_definition(self):
 
3559
        store1 = config.IniFileStore()
 
3560
        store1._load_from_string('foo=bar')
 
3561
        store2 = config.IniFileStore()
 
3562
        store2._load_from_string('foo=baz')
 
3563
        conf = config.Stack([store1.get_sections, store2.get_sections])
 
3564
        self.assertEquals('bar', conf.get('foo'))
2962
3565
 
2963
3566
    def test_get_with_registered_default_value(self):
2964
 
        conf_stack = config.Stack([dict()])
2965
 
        opt = config.Option('foo', default='bar')
2966
 
        self.overrideOptionRegistry()
2967
 
        config.option_registry.register('foo', opt)
 
3567
        config.option_registry.register(config.Option('foo', default='bar'))
 
3568
        conf_stack = config.Stack([])
2968
3569
        self.assertEquals('bar', conf_stack.get('foo'))
2969
3570
 
2970
3571
    def test_get_without_registered_default_value(self):
2971
 
        conf_stack = config.Stack([dict()])
2972
 
        opt = config.Option('foo')
2973
 
        self.overrideOptionRegistry()
2974
 
        config.option_registry.register('foo', opt)
 
3572
        config.option_registry.register(config.Option('foo'))
 
3573
        conf_stack = config.Stack([])
2975
3574
        self.assertEquals(None, conf_stack.get('foo'))
2976
3575
 
2977
3576
    def test_get_without_default_value_for_not_registered(self):
2978
 
        conf_stack = config.Stack([dict()])
2979
 
        opt = config.Option('foo')
2980
 
        self.overrideOptionRegistry()
 
3577
        conf_stack = config.Stack([])
2981
3578
        self.assertEquals(None, conf_stack.get('foo'))
2982
3579
 
2983
 
    def test_get_first_definition(self):
2984
 
        conf1 = dict(foo='bar')
2985
 
        conf2 = dict(foo='baz')
2986
 
        conf_stack = config.Stack([conf1, conf2])
2987
 
        self.assertEquals('bar', conf_stack.get('foo'))
2988
 
 
2989
 
    def test_get_embedded_definition(self):
2990
 
        conf1 = dict(yy='12')
2991
 
        conf2 = config.Stack([dict(xx='42'), dict(foo='baz')])
2992
 
        conf_stack = config.Stack([conf1, conf2])
2993
 
        self.assertEquals('baz', conf_stack.get('foo'))
2994
 
 
2995
3580
    def test_get_for_empty_section_callable(self):
2996
3581
        conf_stack = config.Stack([lambda : []])
2997
3582
        self.assertEquals(None, conf_stack.get('foo'))
2998
3583
 
2999
3584
    def test_get_for_broken_callable(self):
3000
3585
        # Trying to use and invalid callable raises an exception on first use
3001
 
        conf_stack = config.Stack([lambda : object()])
 
3586
        conf_stack = config.Stack([object])
3002
3587
        self.assertRaises(TypeError, conf_stack.get, 'foo')
3003
3588
 
3004
3589
 
 
3590
class TestStackWithSimpleStore(tests.TestCase):
 
3591
 
 
3592
    def setUp(self):
 
3593
        super(TestStackWithSimpleStore, self).setUp()
 
3594
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
 
3595
        self.registry = config.option_registry
 
3596
 
 
3597
    def get_conf(self, content=None):
 
3598
        return config.MemoryStack(content)
 
3599
 
 
3600
    def test_override_value_from_env(self):
 
3601
        self.registry.register(
 
3602
            config.Option('foo', default='bar', override_from_env=['FOO']))
 
3603
        self.overrideEnv('FOO', 'quux')
 
3604
        # Env variable provides a default taking over the option one
 
3605
        conf = self.get_conf('foo=store')
 
3606
        self.assertEquals('quux', conf.get('foo'))
 
3607
 
 
3608
    def test_first_override_value_from_env_wins(self):
 
3609
        self.registry.register(
 
3610
            config.Option('foo', default='bar',
 
3611
                          override_from_env=['NO_VALUE', 'FOO', 'BAZ']))
 
3612
        self.overrideEnv('FOO', 'foo')
 
3613
        self.overrideEnv('BAZ', 'baz')
 
3614
        # The first env var set wins
 
3615
        conf = self.get_conf('foo=store')
 
3616
        self.assertEquals('foo', conf.get('foo'))
 
3617
 
 
3618
 
 
3619
class TestMemoryStack(tests.TestCase):
 
3620
 
 
3621
    def test_get(self):
 
3622
        conf = config.MemoryStack('foo=bar')
 
3623
        self.assertEquals('bar', conf.get('foo'))
 
3624
 
 
3625
    def test_set(self):
 
3626
        conf = config.MemoryStack('foo=bar')
 
3627
        conf.set('foo', 'baz')
 
3628
        self.assertEquals('baz', conf.get('foo'))
 
3629
 
 
3630
    def test_no_content(self):
 
3631
        conf = config.MemoryStack()
 
3632
        # No content means no loading
 
3633
        self.assertFalse(conf.store.is_loaded())
 
3634
        self.assertRaises(NotImplementedError, conf.get, 'foo')
 
3635
        # But a content can still be provided
 
3636
        conf.store._load_from_string('foo=bar')
 
3637
        self.assertEquals('bar', conf.get('foo'))
 
3638
 
 
3639
 
3005
3640
class TestStackWithTransport(tests.TestCaseWithTransport):
3006
3641
 
3007
3642
    scenarios = [(key, {'get_stack': builder}) for key, builder
3025
3660
        self.assertEquals(None, self.conf.get('foo'))
3026
3661
 
3027
3662
    def test_get_hook(self):
3028
 
        self.conf.store._load_from_string('foo=bar')
 
3663
        self.conf.set('foo', 'bar')
3029
3664
        calls = []
3030
3665
        def hook(*args):
3031
3666
            calls.append(args)
3037
3672
        self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
3038
3673
 
3039
3674
 
3040
 
class TestStackGetWithConverter(TestStackGet):
 
3675
class TestStackGetWithConverter(tests.TestCase):
3041
3676
 
3042
3677
    def setUp(self):
3043
3678
        super(TestStackGetWithConverter, self).setUp()
3044
3679
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3045
3680
        self.registry = config.option_registry
3046
3681
 
3047
 
    def register_bool_option(self, name, default, invalid=None):
3048
 
        b = config.Option(name, default=default, help='A boolean.',
3049
 
                          from_unicode=config.bool_from_store,
3050
 
                          invalid=invalid)
 
3682
    def get_conf(self, content=None):
 
3683
        return config.MemoryStack(content)
 
3684
 
 
3685
    def register_bool_option(self, name, default=None, default_from_env=None):
 
3686
        b = config.Option(name, help='A boolean.',
 
3687
                          default=default, default_from_env=default_from_env,
 
3688
                          from_unicode=config.bool_from_store)
3051
3689
        self.registry.register(b)
3052
3690
 
3053
 
    def test_get_with_bool_not_defined_default_true(self):
3054
 
        self.register_bool_option('foo', True)
3055
 
        self.assertEquals(True, self.conf.get('foo'))
3056
 
 
3057
 
    def test_get_with_bool_not_defined_default_false(self):
3058
 
        self.register_bool_option('foo', False)
3059
 
        self.assertEquals(False, self.conf.get('foo'))
3060
 
 
3061
 
    def test_get_with_bool_converter_not_default(self):
3062
 
        self.register_bool_option('foo', False)
3063
 
        self.conf.store._load_from_string('foo=yes')
3064
 
        self.assertEquals(True, self.conf.get('foo'))
3065
 
 
3066
 
    def test_get_with_bool_converter_invalid_string(self):
3067
 
        self.register_bool_option('foo', False)
3068
 
        self.conf.store._load_from_string('foo=not-a-boolean')
3069
 
        self.assertEquals(False, self.conf.get('foo'))
3070
 
 
3071
 
    def test_get_with_bool_converter_invalid_list(self):
3072
 
        self.register_bool_option('foo', False)
3073
 
        self.conf.store._load_from_string('foo=not,a,boolean')
3074
 
        self.assertEquals(False, self.conf.get('foo'))
3075
 
 
3076
 
    def test_get_invalid_warns(self):
3077
 
        self.register_bool_option('foo', False, invalid='warning')
3078
 
        self.conf.store._load_from_string('foo=not-a-boolean')
3079
 
        warnings = []
3080
 
        def warning(*args):
3081
 
            warnings.append(args[0] % args[1:])
3082
 
        self.overrideAttr(trace, 'warning', warning)
3083
 
        self.assertEquals(False, self.conf.get('foo'))
3084
 
        self.assertLength(1, warnings)
3085
 
        self.assertEquals('Value "not-a-boolean" is not valid for "foo"',
3086
 
                          warnings[0])
3087
 
 
3088
 
    def test_get_invalid_errors(self):
3089
 
        self.register_bool_option('foo', False, invalid='error')
3090
 
        self.conf.store._load_from_string('foo=not-a-boolean')
3091
 
        self.assertRaises(errors.ConfigOptionValueError, self.conf.get, 'foo')
3092
 
 
3093
 
    def register_integer_option(self, name, default):
3094
 
        i = config.Option(name, default=default, help='An integer.',
 
3691
    def test_get_default_bool_None(self):
 
3692
        self.register_bool_option('foo')
 
3693
        conf = self.get_conf('')
 
3694
        self.assertEquals(None, conf.get('foo'))
 
3695
 
 
3696
    def test_get_default_bool_True(self):
 
3697
        self.register_bool_option('foo', u'True')
 
3698
        conf = self.get_conf('')
 
3699
        self.assertEquals(True, conf.get('foo'))
 
3700
 
 
3701
    def test_get_default_bool_False(self):
 
3702
        self.register_bool_option('foo', False)
 
3703
        conf = self.get_conf('')
 
3704
        self.assertEquals(False, conf.get('foo'))
 
3705
 
 
3706
    def test_get_default_bool_False_as_string(self):
 
3707
        self.register_bool_option('foo', u'False')
 
3708
        conf = self.get_conf('')
 
3709
        self.assertEquals(False, conf.get('foo'))
 
3710
 
 
3711
    def test_get_default_bool_from_env_converted(self):
 
3712
        self.register_bool_option('foo', u'True', default_from_env=['FOO'])
 
3713
        self.overrideEnv('FOO', 'False')
 
3714
        conf = self.get_conf('')
 
3715
        self.assertEquals(False, conf.get('foo'))
 
3716
 
 
3717
    def test_get_default_bool_when_conversion_fails(self):
 
3718
        self.register_bool_option('foo', default='True')
 
3719
        conf = self.get_conf('foo=invalid boolean')
 
3720
        self.assertEquals(True, conf.get('foo'))
 
3721
 
 
3722
    def register_integer_option(self, name,
 
3723
                                default=None, default_from_env=None):
 
3724
        i = config.Option(name, help='An integer.',
 
3725
                          default=default, default_from_env=default_from_env,
3095
3726
                          from_unicode=config.int_from_store)
3096
3727
        self.registry.register(i)
3097
3728
 
3098
 
    def test_get_with_integer_not_defined_returns_default(self):
3099
 
        self.register_integer_option('foo', 42)
3100
 
        self.assertEquals(42, self.conf.get('foo'))
3101
 
 
3102
 
    def test_get_with_integer_converter_not_default(self):
3103
 
        self.register_integer_option('foo', 42)
3104
 
        self.conf.store._load_from_string('foo=16')
3105
 
        self.assertEquals(16, self.conf.get('foo'))
3106
 
 
3107
 
    def test_get_with_integer_converter_invalid_string(self):
3108
 
        # We don't set a default value
3109
 
        self.register_integer_option('foo', None)
3110
 
        self.conf.store._load_from_string('foo=forty-two')
3111
 
        # No default value, so we should get None
3112
 
        self.assertEquals(None, self.conf.get('foo'))
3113
 
 
3114
 
    def test_get_with_integer_converter_invalid_list(self):
3115
 
        # We don't set a default value
3116
 
        self.register_integer_option('foo', None)
3117
 
        self.conf.store._load_from_string('foo=a,list')
3118
 
        # No default value, so we should get None
3119
 
        self.assertEquals(None, self.conf.get('foo'))
3120
 
 
3121
 
    def register_list_option(self, name, default):
3122
 
        l = config.Option(name, default=default, help='A list.',
3123
 
                          from_unicode=config.list_from_store)
 
3729
    def test_get_default_integer_None(self):
 
3730
        self.register_integer_option('foo')
 
3731
        conf = self.get_conf('')
 
3732
        self.assertEquals(None, conf.get('foo'))
 
3733
 
 
3734
    def test_get_default_integer(self):
 
3735
        self.register_integer_option('foo', 42)
 
3736
        conf = self.get_conf('')
 
3737
        self.assertEquals(42, conf.get('foo'))
 
3738
 
 
3739
    def test_get_default_integer_as_string(self):
 
3740
        self.register_integer_option('foo', u'42')
 
3741
        conf = self.get_conf('')
 
3742
        self.assertEquals(42, conf.get('foo'))
 
3743
 
 
3744
    def test_get_default_integer_from_env(self):
 
3745
        self.register_integer_option('foo', default_from_env=['FOO'])
 
3746
        self.overrideEnv('FOO', '18')
 
3747
        conf = self.get_conf('')
 
3748
        self.assertEquals(18, conf.get('foo'))
 
3749
 
 
3750
    def test_get_default_integer_when_conversion_fails(self):
 
3751
        self.register_integer_option('foo', default='12')
 
3752
        conf = self.get_conf('foo=invalid integer')
 
3753
        self.assertEquals(12, conf.get('foo'))
 
3754
 
 
3755
    def register_list_option(self, name, default=None, default_from_env=None):
 
3756
        l = config.ListOption(name, help='A list.', default=default,
 
3757
                              default_from_env=default_from_env)
3124
3758
        self.registry.register(l)
3125
3759
 
3126
 
    def test_get_with_list_not_defined_returns_default(self):
3127
 
        self.register_list_option('foo', [])
3128
 
        self.assertEquals([], self.conf.get('foo'))
3129
 
 
3130
 
    def test_get_with_list_converter_nothing(self):
3131
 
        self.register_list_option('foo', [1])
3132
 
        self.conf.store._load_from_string('foo=')
3133
 
        self.assertEquals([], self.conf.get('foo'))
 
3760
    def test_get_default_list_None(self):
 
3761
        self.register_list_option('foo')
 
3762
        conf = self.get_conf('')
 
3763
        self.assertEquals(None, conf.get('foo'))
 
3764
 
 
3765
    def test_get_default_list_empty(self):
 
3766
        self.register_list_option('foo', '')
 
3767
        conf = self.get_conf('')
 
3768
        self.assertEquals([], conf.get('foo'))
 
3769
 
 
3770
    def test_get_default_list_from_env(self):
 
3771
        self.register_list_option('foo', default_from_env=['FOO'])
 
3772
        self.overrideEnv('FOO', '')
 
3773
        conf = self.get_conf('')
 
3774
        self.assertEquals([], conf.get('foo'))
3134
3775
 
3135
3776
    def test_get_with_list_converter_no_item(self):
3136
 
        self.register_list_option('foo', [1])
3137
 
        self.conf.store._load_from_string('foo=,')
3138
 
        self.assertEquals([], self.conf.get('foo'))
3139
 
 
3140
 
    def test_get_with_list_converter_one_boolean(self):
3141
 
        self.register_list_option('foo', [1])
3142
 
        self.conf.store._load_from_string('foo=True')
3143
 
        # We get a list of one string
3144
 
        self.assertEquals(['True'], self.conf.get('foo'))
3145
 
 
3146
 
    def test_get_with_list_converter_one_integer(self):
3147
 
        self.register_list_option('foo', [1])
3148
 
        self.conf.store._load_from_string('foo=2')
3149
 
        # We get a list of one string
3150
 
        self.assertEquals(['2'], self.conf.get('foo'))
3151
 
 
3152
 
    def test_get_with_list_converter_one_string(self):
3153
 
        self.register_list_option('foo', ['foo'])
3154
 
        self.conf.store._load_from_string('foo=bar')
3155
 
        # We get a list of one string
3156
 
        self.assertEquals(['bar'], self.conf.get('foo'))
 
3777
        self.register_list_option('foo', None)
 
3778
        conf = self.get_conf('foo=,')
 
3779
        self.assertEquals([], conf.get('foo'))
3157
3780
 
3158
3781
    def test_get_with_list_converter_many_items(self):
3159
 
        self.register_list_option('foo', [1])
3160
 
        self.conf.store._load_from_string('foo=m,o,r,e')
3161
 
        self.assertEquals(['m', 'o', 'r', 'e'], self.conf.get('foo'))
 
3782
        self.register_list_option('foo', None)
 
3783
        conf = self.get_conf('foo=m,o,r,e')
 
3784
        self.assertEquals(['m', 'o', 'r', 'e'], conf.get('foo'))
 
3785
 
 
3786
    def test_get_with_list_converter_embedded_spaces_many_items(self):
 
3787
        self.register_list_option('foo', None)
 
3788
        conf = self.get_conf('foo=" bar", "baz "')
 
3789
        self.assertEquals([' bar', 'baz '], conf.get('foo'))
 
3790
 
 
3791
    def test_get_with_list_converter_stripped_spaces_many_items(self):
 
3792
        self.register_list_option('foo', None)
 
3793
        conf = self.get_conf('foo= bar ,  baz ')
 
3794
        self.assertEquals(['bar', 'baz'], conf.get('foo'))
 
3795
 
 
3796
 
 
3797
class TestIterOptionRefs(tests.TestCase):
 
3798
    """iter_option_refs is a bit unusual, document some cases."""
 
3799
 
 
3800
    def assertRefs(self, expected, string):
 
3801
        self.assertEquals(expected, list(config.iter_option_refs(string)))
 
3802
 
 
3803
    def test_empty(self):
 
3804
        self.assertRefs([(False, '')], '')
 
3805
 
 
3806
    def test_no_refs(self):
 
3807
        self.assertRefs([(False, 'foo bar')], 'foo bar')
 
3808
 
 
3809
    def test_single_ref(self):
 
3810
        self.assertRefs([(False, ''), (True, '{foo}'), (False, '')], '{foo}')
 
3811
 
 
3812
    def test_broken_ref(self):
 
3813
        self.assertRefs([(False, '{foo')], '{foo')
 
3814
 
 
3815
    def test_embedded_ref(self):
 
3816
        self.assertRefs([(False, '{'), (True, '{foo}'), (False, '}')],
 
3817
                        '{{foo}}')
 
3818
 
 
3819
    def test_two_refs(self):
 
3820
        self.assertRefs([(False, ''), (True, '{foo}'),
 
3821
                         (False, ''), (True, '{bar}'),
 
3822
                         (False, ''),],
 
3823
                        '{foo}{bar}')
 
3824
 
 
3825
    def test_newline_in_refs_are_not_matched(self):
 
3826
        self.assertRefs([(False, '{\nxx}{xx\n}{{\n}}')], '{\nxx}{xx\n}{{\n}}')
 
3827
 
 
3828
 
 
3829
class TestStackExpandOptions(tests.TestCaseWithTransport):
 
3830
 
 
3831
    def setUp(self):
 
3832
        super(TestStackExpandOptions, self).setUp()
 
3833
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
 
3834
        self.registry = config.option_registry
 
3835
        self.conf = build_branch_stack(self)
 
3836
 
 
3837
    def assertExpansion(self, expected, string, env=None):
 
3838
        self.assertEquals(expected, self.conf.expand_options(string, env))
 
3839
 
 
3840
    def test_no_expansion(self):
 
3841
        self.assertExpansion('foo', 'foo')
 
3842
 
 
3843
    def test_expand_default_value(self):
 
3844
        self.conf.store._load_from_string('bar=baz')
 
3845
        self.registry.register(config.Option('foo', default=u'{bar}'))
 
3846
        self.assertEquals('baz', self.conf.get('foo', expand=True))
 
3847
 
 
3848
    def test_expand_default_from_env(self):
 
3849
        self.conf.store._load_from_string('bar=baz')
 
3850
        self.registry.register(config.Option('foo', default_from_env=['FOO']))
 
3851
        self.overrideEnv('FOO', '{bar}')
 
3852
        self.assertEquals('baz', self.conf.get('foo', expand=True))
 
3853
 
 
3854
    def test_expand_default_on_failed_conversion(self):
 
3855
        self.conf.store._load_from_string('baz=bogus\nbar=42\nfoo={baz}')
 
3856
        self.registry.register(
 
3857
            config.Option('foo', default=u'{bar}',
 
3858
                          from_unicode=config.int_from_store))
 
3859
        self.assertEquals(42, self.conf.get('foo', expand=True))
 
3860
 
 
3861
    def test_env_adding_options(self):
 
3862
        self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
 
3863
 
 
3864
    def test_env_overriding_options(self):
 
3865
        self.conf.store._load_from_string('foo=baz')
 
3866
        self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
 
3867
 
 
3868
    def test_simple_ref(self):
 
3869
        self.conf.store._load_from_string('foo=xxx')
 
3870
        self.assertExpansion('xxx', '{foo}')
 
3871
 
 
3872
    def test_unknown_ref(self):
 
3873
        self.assertRaises(errors.ExpandingUnknownOption,
 
3874
                          self.conf.expand_options, '{foo}')
 
3875
 
 
3876
    def test_indirect_ref(self):
 
3877
        self.conf.store._load_from_string('''
 
3878
foo=xxx
 
3879
bar={foo}
 
3880
''')
 
3881
        self.assertExpansion('xxx', '{bar}')
 
3882
 
 
3883
    def test_embedded_ref(self):
 
3884
        self.conf.store._load_from_string('''
 
3885
foo=xxx
 
3886
bar=foo
 
3887
''')
 
3888
        self.assertExpansion('xxx', '{{bar}}')
 
3889
 
 
3890
    def test_simple_loop(self):
 
3891
        self.conf.store._load_from_string('foo={foo}')
 
3892
        self.assertRaises(errors.OptionExpansionLoop,
 
3893
                          self.conf.expand_options, '{foo}')
 
3894
 
 
3895
    def test_indirect_loop(self):
 
3896
        self.conf.store._load_from_string('''
 
3897
foo={bar}
 
3898
bar={baz}
 
3899
baz={foo}''')
 
3900
        e = self.assertRaises(errors.OptionExpansionLoop,
 
3901
                              self.conf.expand_options, '{foo}')
 
3902
        self.assertEquals('foo->bar->baz', e.refs)
 
3903
        self.assertEquals('{foo}', e.string)
 
3904
 
 
3905
    def test_list(self):
 
3906
        self.conf.store._load_from_string('''
 
3907
foo=start
 
3908
bar=middle
 
3909
baz=end
 
3910
list={foo},{bar},{baz}
 
3911
''')
 
3912
        self.registry.register(
 
3913
            config.ListOption('list'))
 
3914
        self.assertEquals(['start', 'middle', 'end'],
 
3915
                           self.conf.get('list', expand=True))
 
3916
 
 
3917
    def test_cascading_list(self):
 
3918
        self.conf.store._load_from_string('''
 
3919
foo=start,{bar}
 
3920
bar=middle,{baz}
 
3921
baz=end
 
3922
list={foo}
 
3923
''')
 
3924
        self.registry.register(
 
3925
            config.ListOption('list'))
 
3926
        self.assertEquals(['start', 'middle', 'end'],
 
3927
                           self.conf.get('list', expand=True))
 
3928
 
 
3929
    def test_pathologically_hidden_list(self):
 
3930
        self.conf.store._load_from_string('''
 
3931
foo=bin
 
3932
bar=go
 
3933
start={foo
 
3934
middle=},{
 
3935
end=bar}
 
3936
hidden={start}{middle}{end}
 
3937
''')
 
3938
        # What matters is what the registration says, the conversion happens
 
3939
        # only after all expansions have been performed
 
3940
        self.registry.register(config.ListOption('hidden'))
 
3941
        self.assertEquals(['bin', 'go'],
 
3942
                          self.conf.get('hidden', expand=True))
 
3943
 
 
3944
 
 
3945
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
 
3946
 
 
3947
    def setUp(self):
 
3948
        super(TestStackCrossSectionsExpand, self).setUp()
 
3949
 
 
3950
    def get_config(self, location, string):
 
3951
        if string is None:
 
3952
            string = ''
 
3953
        # Since we don't save the config we won't strictly require to inherit
 
3954
        # from TestCaseInTempDir, but an error occurs so quickly...
 
3955
        c = config.LocationStack(location)
 
3956
        c.store._load_from_string(string)
 
3957
        return c
 
3958
 
 
3959
    def test_dont_cross_unrelated_section(self):
 
3960
        c = self.get_config('/another/branch/path','''
 
3961
[/one/branch/path]
 
3962
foo = hello
 
3963
bar = {foo}/2
 
3964
 
 
3965
[/another/branch/path]
 
3966
bar = {foo}/2
 
3967
''')
 
3968
        self.assertRaises(errors.ExpandingUnknownOption,
 
3969
                          c.get, 'bar', expand=True)
 
3970
 
 
3971
    def test_cross_related_sections(self):
 
3972
        c = self.get_config('/project/branch/path','''
 
3973
[/project]
 
3974
foo = qu
 
3975
 
 
3976
[/project/branch/path]
 
3977
bar = {foo}ux
 
3978
''')
 
3979
        self.assertEquals('quux', c.get('bar', expand=True))
 
3980
 
 
3981
 
 
3982
class TestStackCrossStoresExpand(tests.TestCaseWithTransport):
 
3983
 
 
3984
    def test_cross_global_locations(self):
 
3985
        l_store = config.LocationStore()
 
3986
        l_store._load_from_string('''
 
3987
[/branch]
 
3988
lfoo = loc-foo
 
3989
lbar = {gbar}
 
3990
''')
 
3991
        l_store.save()
 
3992
        g_store = config.GlobalStore()
 
3993
        g_store._load_from_string('''
 
3994
[DEFAULT]
 
3995
gfoo = {lfoo}
 
3996
gbar = glob-bar
 
3997
''')
 
3998
        g_store.save()
 
3999
        stack = config.LocationStack('/branch')
 
4000
        self.assertEquals('glob-bar', stack.get('lbar', expand=True))
 
4001
        self.assertEquals('loc-foo', stack.get('gfoo', expand=True))
 
4002
 
 
4003
 
 
4004
class TestStackExpandSectionLocals(tests.TestCaseWithTransport):
 
4005
 
 
4006
    def test_expand_locals_empty(self):
 
4007
        l_store = config.LocationStore()
 
4008
        l_store._load_from_string('''
 
4009
[/home/user/project]
 
4010
base = {basename}
 
4011
rel = {relpath}
 
4012
''')
 
4013
        l_store.save()
 
4014
        stack = config.LocationStack('/home/user/project/')
 
4015
        self.assertEquals('', stack.get('base', expand=True))
 
4016
        self.assertEquals('', stack.get('rel', expand=True))
 
4017
 
 
4018
    def test_expand_basename_locally(self):
 
4019
        l_store = config.LocationStore()
 
4020
        l_store._load_from_string('''
 
4021
[/home/user/project]
 
4022
bfoo = {basename}
 
4023
''')
 
4024
        l_store.save()
 
4025
        stack = config.LocationStack('/home/user/project/branch')
 
4026
        self.assertEquals('branch', stack.get('bfoo', expand=True))
 
4027
 
 
4028
    def test_expand_basename_locally_longer_path(self):
 
4029
        l_store = config.LocationStore()
 
4030
        l_store._load_from_string('''
 
4031
[/home/user]
 
4032
bfoo = {basename}
 
4033
''')
 
4034
        l_store.save()
 
4035
        stack = config.LocationStack('/home/user/project/dir/branch')
 
4036
        self.assertEquals('branch', stack.get('bfoo', expand=True))
 
4037
 
 
4038
    def test_expand_relpath_locally(self):
 
4039
        l_store = config.LocationStore()
 
4040
        l_store._load_from_string('''
 
4041
[/home/user/project]
 
4042
lfoo = loc-foo/{relpath}
 
4043
''')
 
4044
        l_store.save()
 
4045
        stack = config.LocationStack('/home/user/project/branch')
 
4046
        self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
 
4047
 
 
4048
    def test_expand_relpath_unknonw_in_global(self):
 
4049
        g_store = config.GlobalStore()
 
4050
        g_store._load_from_string('''
 
4051
[DEFAULT]
 
4052
gfoo = {relpath}
 
4053
''')
 
4054
        g_store.save()
 
4055
        stack = config.LocationStack('/home/user/project/branch')
 
4056
        self.assertRaises(errors.ExpandingUnknownOption,
 
4057
                          stack.get, 'gfoo', expand=True)
 
4058
 
 
4059
    def test_expand_local_option_locally(self):
 
4060
        l_store = config.LocationStore()
 
4061
        l_store._load_from_string('''
 
4062
[/home/user/project]
 
4063
lfoo = loc-foo/{relpath}
 
4064
lbar = {gbar}
 
4065
''')
 
4066
        l_store.save()
 
4067
        g_store = config.GlobalStore()
 
4068
        g_store._load_from_string('''
 
4069
[DEFAULT]
 
4070
gfoo = {lfoo}
 
4071
gbar = glob-bar
 
4072
''')
 
4073
        g_store.save()
 
4074
        stack = config.LocationStack('/home/user/project/branch')
 
4075
        self.assertEquals('glob-bar', stack.get('lbar', expand=True))
 
4076
        self.assertEquals('loc-foo/branch', stack.get('gfoo', expand=True))
 
4077
 
 
4078
    def test_locals_dont_leak(self):
 
4079
        """Make sure we chose the right local in presence of several sections.
 
4080
        """
 
4081
        l_store = config.LocationStore()
 
4082
        l_store._load_from_string('''
 
4083
[/home/user]
 
4084
lfoo = loc-foo/{relpath}
 
4085
[/home/user/project]
 
4086
lfoo = loc-foo/{relpath}
 
4087
''')
 
4088
        l_store.save()
 
4089
        stack = config.LocationStack('/home/user/project/branch')
 
4090
        self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
 
4091
        stack = config.LocationStack('/home/user/bar/baz')
 
4092
        self.assertEquals('loc-foo/bar/baz', stack.get('lfoo', expand=True))
 
4093
 
3162
4094
 
3163
4095
 
3164
4096
class TestStackSet(TestStackWithTransport):
3165
4097
 
3166
4098
    def test_simple_set(self):
3167
4099
        conf = self.get_stack(self)
3168
 
        conf.store._load_from_string('foo=bar')
3169
 
        self.assertEquals('bar', conf.get('foo'))
 
4100
        self.assertEquals(None, conf.get('foo'))
3170
4101
        conf.set('foo', 'baz')
3171
4102
        # Did we get it back ?
3172
4103
        self.assertEquals('baz', conf.get('foo'))
3192
4123
 
3193
4124
    def test_remove_existing(self):
3194
4125
        conf = self.get_stack(self)
3195
 
        conf.store._load_from_string('foo=bar')
 
4126
        conf.set('foo', 'bar')
3196
4127
        self.assertEquals('bar', conf.get('foo'))
3197
4128
        conf.remove('foo')
3198
4129
        # Did we get it back ?
3209
4140
        config.ConfigHooks.install_named_hook('remove', hook, None)
3210
4141
        self.assertLength(0, calls)
3211
4142
        conf = self.get_stack(self)
3212
 
        conf.store._load_from_string('foo=bar')
 
4143
        conf.set('foo', 'bar')
3213
4144
        conf.remove('foo')
3214
4145
        self.assertLength(1, calls)
3215
4146
        self.assertEquals((conf, 'foo'), calls[0])
3877
4808
 
3878
4809
    def test_auto_user_id(self):
3879
4810
        """Automatic inference of user name.
3880
 
        
 
4811
 
3881
4812
        This is a bit hard to test in an isolated way, because it depends on
3882
4813
        system functions that go direct to /etc or perhaps somewhere else.
3883
4814
        But it's reasonable to say that on Unix, with an /etc/mailname, we ought
3893
4824
        else:
3894
4825
            self.assertEquals((None, None), (realname, address))
3895
4826
 
 
4827
 
 
4828
class EmailOptionTests(tests.TestCase):
 
4829
 
 
4830
    def test_default_email_uses_BZR_EMAIL(self):
 
4831
        conf = config.MemoryStack('email=jelmer@debian.org')
 
4832
        # BZR_EMAIL takes precedence over EMAIL
 
4833
        self.overrideEnv('BZR_EMAIL', 'jelmer@samba.org')
 
4834
        self.overrideEnv('EMAIL', 'jelmer@apache.org')
 
4835
        self.assertEquals('jelmer@samba.org', conf.get('email'))
 
4836
 
 
4837
    def test_default_email_uses_EMAIL(self):
 
4838
        conf = config.MemoryStack('')
 
4839
        self.overrideEnv('BZR_EMAIL', None)
 
4840
        self.overrideEnv('EMAIL', 'jelmer@apache.org')
 
4841
        self.assertEquals('jelmer@apache.org', conf.get('email'))
 
4842
 
 
4843
    def test_BZR_EMAIL_overrides(self):
 
4844
        conf = config.MemoryStack('email=jelmer@debian.org')
 
4845
        self.overrideEnv('BZR_EMAIL', 'jelmer@apache.org')
 
4846
        self.assertEquals('jelmer@apache.org', conf.get('email'))
 
4847
        self.overrideEnv('BZR_EMAIL', None)
 
4848
        self.overrideEnv('EMAIL', 'jelmer@samba.org')
 
4849
        self.assertEquals('jelmer@debian.org', conf.get('email'))