~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_config.py

  • Committer: Martin Packman
  • Date: 2012-01-05 09:50:04 UTC
  • mfrom: (6424 +trunk)
  • mto: This revision was merged to the branch mainline in revision 6426.
  • Revision ID: martin.packman@canonical.com-20120105095004-mia9xb7y0efmto0v
Merge bzr.dev to resolve conflicts in bzrlib.builtins

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()
839
844
        self.assertEquals(['{foo', '}', '{', 'bar}'],
840
845
                          conf.get_user_option('hidden', expand=True))
841
846
 
 
847
 
842
848
class TestLocationConfigOptionExpansion(tests.TestCaseInTempDir):
843
849
 
844
850
    def get_config(self, location, string=None):
1065
1071
si_g = 5g,
1066
1072
si_gb = 5gB,
1067
1073
""")
1068
 
        get_si = conf.get_user_option_as_int_from_SI
 
1074
        def get_si(s, default=None):
 
1075
            return self.applyDeprecated(
 
1076
                deprecated_in((2, 5, 0)),
 
1077
                conf.get_user_option_as_int_from_SI, s, default)
1069
1078
        self.assertEqual(100, get_si('plain'))
1070
1079
        self.assertEqual(5000, get_si('si_k'))
1071
1080
        self.assertEqual(5000, get_si('si_kb'))
1076
1085
        self.assertEqual(None, get_si('non-exist'))
1077
1086
        self.assertEqual(42, get_si('non-exist-with-default',  42))
1078
1087
 
 
1088
 
1079
1089
class TestSupressWarning(TestIniConfig):
1080
1090
 
1081
1091
    def make_warnings_config(self, s):
1237
1247
    def test_signatures_always(self):
1238
1248
        my_config = config.GlobalConfig.from_string(sample_always_signatures)
1239
1249
        self.assertEqual(config.CHECK_NEVER,
1240
 
                         my_config.signature_checking())
 
1250
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1251
                             my_config.signature_checking))
1241
1252
        self.assertEqual(config.SIGN_ALWAYS,
1242
 
                         my_config.signing_policy())
1243
 
        self.assertEqual(True, my_config.signature_needed())
 
1253
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1254
                             my_config.signing_policy))
 
1255
        self.assertEqual(True,
 
1256
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1257
                my_config.signature_needed))
1244
1258
 
1245
1259
    def test_signatures_if_possible(self):
1246
1260
        my_config = config.GlobalConfig.from_string(sample_maybe_signatures)
1247
1261
        self.assertEqual(config.CHECK_NEVER,
1248
 
                         my_config.signature_checking())
 
1262
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1263
                             my_config.signature_checking))
1249
1264
        self.assertEqual(config.SIGN_WHEN_REQUIRED,
1250
 
                         my_config.signing_policy())
1251
 
        self.assertEqual(False, my_config.signature_needed())
 
1265
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1266
                             my_config.signing_policy))
 
1267
        self.assertEqual(False, self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1268
            my_config.signature_needed))
1252
1269
 
1253
1270
    def test_signatures_ignore(self):
1254
1271
        my_config = config.GlobalConfig.from_string(sample_ignore_signatures)
1255
1272
        self.assertEqual(config.CHECK_ALWAYS,
1256
 
                         my_config.signature_checking())
 
1273
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1274
                             my_config.signature_checking))
1257
1275
        self.assertEqual(config.SIGN_NEVER,
1258
 
                         my_config.signing_policy())
1259
 
        self.assertEqual(False, my_config.signature_needed())
 
1276
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1277
                             my_config.signing_policy))
 
1278
        self.assertEqual(False, self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1279
            my_config.signature_needed))
1260
1280
 
1261
1281
    def _get_sample_config(self):
1262
1282
        my_config = config.GlobalConfig.from_string(sample_config_text)
1264
1284
 
1265
1285
    def test_gpg_signing_command(self):
1266
1286
        my_config = self._get_sample_config()
1267
 
        self.assertEqual("gnome-gpg", my_config.gpg_signing_command())
1268
 
        self.assertEqual(False, my_config.signature_needed())
 
1287
        self.assertEqual("gnome-gpg",
 
1288
            self.applyDeprecated(
 
1289
                deprecated_in((2, 5, 0)), my_config.gpg_signing_command))
 
1290
        self.assertEqual(False, self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1291
            my_config.signature_needed))
1269
1292
 
1270
1293
    def test_gpg_signing_key(self):
1271
1294
        my_config = self._get_sample_config()
1272
 
        self.assertEqual("DD4D5088", my_config.gpg_signing_key())
 
1295
        self.assertEqual("DD4D5088",
 
1296
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1297
                my_config.gpg_signing_key))
1273
1298
 
1274
1299
    def _get_empty_config(self):
1275
1300
        my_config = config.GlobalConfig()
1277
1302
 
1278
1303
    def test_gpg_signing_command_unset(self):
1279
1304
        my_config = self._get_empty_config()
1280
 
        self.assertEqual("gpg", my_config.gpg_signing_command())
 
1305
        self.assertEqual("gpg",
 
1306
            self.applyDeprecated(
 
1307
                deprecated_in((2, 5, 0)), my_config.gpg_signing_command))
1281
1308
 
1282
1309
    def test_get_user_option_default(self):
1283
1310
        my_config = self._get_empty_config()
1290
1317
 
1291
1318
    def test_post_commit_default(self):
1292
1319
        my_config = self._get_sample_config()
1293
 
        self.assertEqual(None, my_config.post_commit())
 
1320
        self.assertEqual(None,
 
1321
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1322
                                              my_config.post_commit))
1294
1323
 
1295
1324
    def test_configured_logformat(self):
1296
1325
        my_config = self._get_sample_config()
1297
 
        self.assertEqual("short", my_config.log_format())
 
1326
        self.assertEqual("short",
 
1327
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1328
                                              my_config.log_format))
1298
1329
 
1299
1330
    def test_configured_acceptable_keys(self):
1300
1331
        my_config = self._get_sample_config()
1301
 
        self.assertEqual("amy", my_config.acceptable_keys())
 
1332
        self.assertEqual("amy",
 
1333
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1334
                my_config.acceptable_keys))
1302
1335
 
1303
1336
    def test_configured_validate_signatures_in_log(self):
1304
1337
        my_config = self._get_sample_config()
1342
1375
        self.log(repr(tools))
1343
1376
        self.assertEqual(
1344
1377
            {u'funkytool' : u'funkytool "arg with spaces" {this_temp}',
1345
 
            u'sometool' : u'sometool {base} {this} {other} -o {result}'},
 
1378
            u'sometool' : u'sometool {base} {this} {other} -o {result}',
 
1379
            u'newtool' : u'"newtool with spaces" {this_temp}'},
1346
1380
            tools)
1347
1381
 
1348
1382
    def test_get_merge_tools_empty(self):
1539
1573
        self.get_branch_config('http://www.example.com',
1540
1574
                                 global_config=sample_ignore_signatures)
1541
1575
        self.assertEqual(config.CHECK_ALWAYS,
1542
 
                         self.my_config.signature_checking())
 
1576
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1577
                             self.my_config.signature_checking))
1543
1578
        self.assertEqual(config.SIGN_NEVER,
1544
 
                         self.my_config.signing_policy())
 
1579
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1580
                             self.my_config.signing_policy))
1545
1581
 
1546
1582
    def test_signatures_never(self):
1547
1583
        self.get_branch_config('/a/c')
1548
1584
        self.assertEqual(config.CHECK_NEVER,
1549
 
                         self.my_config.signature_checking())
 
1585
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1586
                             self.my_config.signature_checking))
1550
1587
 
1551
1588
    def test_signatures_when_available(self):
1552
1589
        self.get_branch_config('/a/', global_config=sample_ignore_signatures)
1553
1590
        self.assertEqual(config.CHECK_IF_POSSIBLE,
1554
 
                         self.my_config.signature_checking())
 
1591
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1592
                             self.my_config.signature_checking))
1555
1593
 
1556
1594
    def test_signatures_always(self):
1557
1595
        self.get_branch_config('/b')
1558
1596
        self.assertEqual(config.CHECK_ALWAYS,
1559
 
                         self.my_config.signature_checking())
 
1597
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1598
                         self.my_config.signature_checking))
1560
1599
 
1561
1600
    def test_gpg_signing_command(self):
1562
1601
        self.get_branch_config('/b')
1563
 
        self.assertEqual("gnome-gpg", self.my_config.gpg_signing_command())
 
1602
        self.assertEqual("gnome-gpg",
 
1603
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1604
                self.my_config.gpg_signing_command))
1564
1605
 
1565
1606
    def test_gpg_signing_command_missing(self):
1566
1607
        self.get_branch_config('/a')
1567
 
        self.assertEqual("false", self.my_config.gpg_signing_command())
 
1608
        self.assertEqual("false",
 
1609
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1610
                self.my_config.gpg_signing_command))
1568
1611
 
1569
1612
    def test_gpg_signing_key(self):
1570
1613
        self.get_branch_config('/b')
1571
 
        self.assertEqual("DD4D5088", self.my_config.gpg_signing_key())
 
1614
        self.assertEqual("DD4D5088", self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1615
            self.my_config.gpg_signing_key))
1572
1616
 
1573
1617
    def test_gpg_signing_key_default(self):
1574
1618
        self.get_branch_config('/a')
1575
 
        self.assertEqual("erik@bagfors.nu", self.my_config.gpg_signing_key())
 
1619
        self.assertEqual("erik@bagfors.nu",
 
1620
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1621
                self.my_config.gpg_signing_key))
1576
1622
 
1577
1623
    def test_get_user_option_global(self):
1578
1624
        self.get_branch_config('/a')
1666
1712
    def test_post_commit_default(self):
1667
1713
        self.get_branch_config('/a/c')
1668
1714
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1669
 
                         self.my_config.post_commit())
 
1715
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1716
                                              self.my_config.post_commit))
1670
1717
 
1671
1718
    def get_branch_config(self, location, global_config=None,
1672
1719
                          location_config=None):
1762
1809
        return my_config
1763
1810
 
1764
1811
    def test_user_id(self):
1765
 
        branch = FakeBranch(user_id='Robert Collins <robertc@example.net>')
 
1812
        branch = FakeBranch()
1766
1813
        my_config = config.BranchConfig(branch)
1767
 
        self.assertEqual("Robert Collins <robertc@example.net>",
1768
 
                         my_config.username())
 
1814
        self.assertIsNot(None, my_config.username())
1769
1815
        my_config.branch.control_files.files['email'] = "John"
1770
1816
        my_config.set_user_option('email',
1771
1817
                                  "Robert Collins <robertc@example.org>")
1772
 
        self.assertEqual("John", my_config.username())
1773
 
        del my_config.branch.control_files.files['email']
1774
1818
        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())
 
1819
                        my_config.username())
1783
1820
 
1784
1821
    def test_BZR_EMAIL_OVERRIDES(self):
1785
1822
        self.overrideEnv('BZR_EMAIL', "Robert Collins <robertc@example.org>")
1791
1828
    def test_signatures_forced(self):
1792
1829
        my_config = self.get_branch_config(
1793
1830
            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())
 
1831
        self.assertEqual(config.CHECK_NEVER,
 
1832
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1833
                my_config.signature_checking))
 
1834
        self.assertEqual(config.SIGN_ALWAYS,
 
1835
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1836
                my_config.signing_policy))
 
1837
        self.assertTrue(self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1838
            my_config.signature_needed))
1797
1839
 
1798
1840
    def test_signatures_forced_branch(self):
1799
1841
        my_config = self.get_branch_config(
1800
1842
            global_config=sample_ignore_signatures,
1801
1843
            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())
 
1844
        self.assertEqual(config.CHECK_NEVER,
 
1845
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1846
                my_config.signature_checking))
 
1847
        self.assertEqual(config.SIGN_ALWAYS,
 
1848
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1849
                my_config.signing_policy))
 
1850
        self.assertTrue(self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1851
            my_config.signature_needed))
1805
1852
 
1806
1853
    def test_gpg_signing_command(self):
1807
1854
        my_config = self.get_branch_config(
1808
1855
            global_config=sample_config_text,
1809
1856
            # branch data cannot set gpg_signing_command
1810
1857
            branch_data_config="gpg_signing_command=pgp")
1811
 
        self.assertEqual('gnome-gpg', my_config.gpg_signing_command())
 
1858
        self.assertEqual('gnome-gpg',
 
1859
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1860
                my_config.gpg_signing_command))
1812
1861
 
1813
1862
    def test_get_user_option_global(self):
1814
1863
        my_config = self.get_branch_config(global_config=sample_config_text)
1821
1870
                                      location_config=sample_branches_text)
1822
1871
        self.assertEqual(my_config.branch.base, '/a/c')
1823
1872
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1824
 
                         my_config.post_commit())
 
1873
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1874
                                              my_config.post_commit))
1825
1875
        my_config.set_user_option('post_commit', 'rmtree_root')
1826
1876
        # post-commit is ignored when present in branch data
1827
1877
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1828
 
                         my_config.post_commit())
 
1878
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1879
                                              my_config.post_commit))
1829
1880
        my_config.set_user_option('post_commit', 'rmtree_root',
1830
1881
                                  store=config.STORE_LOCATION)
1831
 
        self.assertEqual('rmtree_root', my_config.post_commit())
 
1882
        self.assertEqual('rmtree_root',
 
1883
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1884
                                              my_config.post_commit))
1832
1885
 
1833
1886
    def test_config_precedence(self):
1834
1887
        # FIXME: eager test, luckily no persitent config file makes it fail
1968
2021
        conf = config.TransportConfig(t, 'foo.conf')
1969
2022
        self.assertRaises(errors.ParseConfigError, conf._get_configobj)
1970
2023
 
 
2024
    def test_load_permission_denied(self):
 
2025
        """Ensure we get an empty config file if the file is inaccessible."""
 
2026
        warnings = []
 
2027
        def warning(*args):
 
2028
            warnings.append(args[0] % args[1:])
 
2029
        self.overrideAttr(trace, 'warning', warning)
 
2030
 
 
2031
        class DenyingTransport(object):
 
2032
 
 
2033
            def __init__(self, base):
 
2034
                self.base = base
 
2035
 
 
2036
            def get_bytes(self, relpath):
 
2037
                raise errors.PermissionDenied(relpath, "")
 
2038
 
 
2039
        cfg = config.TransportConfig(
 
2040
            DenyingTransport("nonexisting://"), 'control.conf')
 
2041
        self.assertIs(None, cfg.get_option('non-existant', 'SECTION'))
 
2042
        self.assertEquals(
 
2043
            warnings,
 
2044
            [u'Permission denied while trying to open configuration file '
 
2045
             u'nonexisting:///control.conf.'])
 
2046
 
1971
2047
    def test_get_value(self):
1972
2048
        """Test that retreiving a value from a section is possible"""
1973
2049
        bzrdir_config = config.TransportConfig(self.get_transport('.'),
2253
2329
        opt = config.Option('foo', default='bar')
2254
2330
        self.assertEquals('bar', opt.get_default())
2255
2331
 
 
2332
    def test_callable_default_value(self):
 
2333
        def bar_as_unicode():
 
2334
            return u'bar'
 
2335
        opt = config.Option('foo', default=bar_as_unicode)
 
2336
        self.assertEquals('bar', opt.get_default())
 
2337
 
2256
2338
    def test_default_value_from_env(self):
2257
2339
        opt = config.Option('foo', default='bar', default_from_env=['FOO'])
2258
2340
        self.overrideEnv('FOO', 'quux')
2274
2356
        self.assertRaises(AssertionError, config.Option, 'foo',
2275
2357
                          default=object())
2276
2358
 
 
2359
    def test_not_supported_callable_default_value_not_unicode(self):
 
2360
        def bar_not_unicode():
 
2361
            return 'bar'
 
2362
        opt = config.Option('foo', default=bar_not_unicode)
 
2363
        self.assertRaises(AssertionError, opt.get_default)
 
2364
 
2277
2365
 
2278
2366
class TestOptionConverterMixin(object):
2279
2367
 
2280
2368
    def assertConverted(self, expected, opt, value):
2281
 
        self.assertEquals(expected, opt.convert_from_unicode(value))
 
2369
        self.assertEquals(expected, opt.convert_from_unicode(None, value))
2282
2370
 
2283
2371
    def assertWarns(self, opt, value):
2284
2372
        warnings = []
2285
2373
        def warning(*args):
2286
2374
            warnings.append(args[0] % args[1:])
2287
2375
        self.overrideAttr(trace, 'warning', warning)
2288
 
        self.assertEquals(None, opt.convert_from_unicode(value))
 
2376
        self.assertEquals(None, opt.convert_from_unicode(None, value))
2289
2377
        self.assertLength(1, warnings)
2290
2378
        self.assertEquals(
2291
2379
            'Value "%s" is not valid for "%s"' % (value, opt.name),
2293
2381
 
2294
2382
    def assertErrors(self, opt, value):
2295
2383
        self.assertRaises(errors.ConfigOptionValueError,
2296
 
                          opt.convert_from_unicode, value)
 
2384
                          opt.convert_from_unicode, None, value)
2297
2385
 
2298
2386
    def assertConvertInvalid(self, opt, invalid_value):
2299
2387
        opt.invalid = None
2300
 
        self.assertEquals(None, opt.convert_from_unicode(invalid_value))
 
2388
        self.assertEquals(None, opt.convert_from_unicode(None, invalid_value))
2301
2389
        opt.invalid = 'warning'
2302
2390
        self.assertWarns(opt, invalid_value)
2303
2391
        opt.invalid = 'error'
2341
2429
        opt = self.get_option()
2342
2430
        self.assertConverted(16, opt, u'16')
2343
2431
 
2344
 
class TestOptionWithListConverter(tests.TestCase, TestOptionConverterMixin):
2345
 
 
2346
 
    def get_option(self):
2347
 
        return config.Option('foo', help='A list.',
2348
 
                             from_unicode=config.list_from_store)
2349
 
 
2350
 
    def test_convert_invalid(self):
 
2432
 
 
2433
class TestOptionWithSIUnitConverter(tests.TestCase, TestOptionConverterMixin):
 
2434
 
 
2435
    def get_option(self):
 
2436
        return config.Option('foo', help='An integer in SI units.',
 
2437
                             from_unicode=config.int_SI_from_store)
 
2438
 
 
2439
    def test_convert_invalid(self):
 
2440
        opt = self.get_option()
 
2441
        self.assertConvertInvalid(opt, u'not-a-unit')
 
2442
        self.assertConvertInvalid(opt, u'Gb') # Forgot the int
 
2443
        self.assertConvertInvalid(opt, u'1b') # Forgot the unit
 
2444
        self.assertConvertInvalid(opt, u'1GG')
 
2445
        self.assertConvertInvalid(opt, u'1Mbb')
 
2446
        self.assertConvertInvalid(opt, u'1MM')
 
2447
 
 
2448
    def test_convert_valid(self):
 
2449
        opt = self.get_option()
 
2450
        self.assertConverted(int(5e3), opt, u'5kb')
 
2451
        self.assertConverted(int(5e6), opt, u'5M')
 
2452
        self.assertConverted(int(5e6), opt, u'5MB')
 
2453
        self.assertConverted(int(5e9), opt, u'5g')
 
2454
        self.assertConverted(int(5e9), opt, u'5gB')
 
2455
        self.assertConverted(100, opt, u'100')
 
2456
 
 
2457
 
 
2458
class TestListOption(tests.TestCase, TestOptionConverterMixin):
 
2459
 
 
2460
    def get_option(self):
 
2461
        return config.ListOption('foo', help='A list.')
 
2462
 
 
2463
    def test_convert_invalid(self):
 
2464
        opt = self.get_option()
 
2465
        # We don't even try to convert a list into a list, we only expect
 
2466
        # strings
 
2467
        self.assertConvertInvalid(opt, [1])
2351
2468
        # No string is invalid as all forms can be converted to a list
2352
 
        pass
2353
2469
 
2354
2470
    def test_convert_valid(self):
2355
2471
        opt = self.get_option()
2362
2478
        self.assertConverted([u'42'], opt, u'42')
2363
2479
        # A single string
2364
2480
        self.assertConverted([u'bar'], opt, u'bar')
2365
 
        # A list remains a list (configObj will turn a string containing commas
2366
 
        # into a list, but that's not what we're testing here)
2367
 
        self.assertConverted([u'foo', u'1', u'True'],
2368
 
                             opt, [u'foo', u'1', u'True'])
2369
2481
 
2370
2482
 
2371
2483
class TestOptionRegistry(tests.TestCase):
2447
2559
 
2448
2560
class TestMutableSection(tests.TestCase):
2449
2561
 
2450
 
    # FIXME: Parametrize so that all sections (including os.environ and the
2451
 
    # ones produced by Stores) run these tests -- vila 2011-04-01
 
2562
    scenarios = [('mutable',
 
2563
                  {'get_section':
 
2564
                       lambda opts: config.MutableSection('myID', opts)},),
 
2565
        ]
2452
2566
 
2453
2567
    def test_set(self):
2454
2568
        a_dict = dict(foo='bar')
2455
 
        section = config.MutableSection('myID', a_dict)
 
2569
        section = self.get_section(a_dict)
2456
2570
        section.set('foo', 'new_value')
2457
2571
        self.assertEquals('new_value', section.get('foo'))
2458
2572
        # The change appears in the shared section
2463
2577
 
2464
2578
    def test_set_preserve_original_once(self):
2465
2579
        a_dict = dict(foo='bar')
2466
 
        section = config.MutableSection('myID', a_dict)
 
2580
        section = self.get_section(a_dict)
2467
2581
        section.set('foo', 'first_value')
2468
2582
        section.set('foo', 'second_value')
2469
2583
        # We keep track of the original value
2472
2586
 
2473
2587
    def test_remove(self):
2474
2588
        a_dict = dict(foo='bar')
2475
 
        section = config.MutableSection('myID', a_dict)
 
2589
        section = self.get_section(a_dict)
2476
2590
        section.remove('foo')
2477
2591
        # We get None for unknown options via the default value
2478
2592
        self.assertEquals(None, section.get('foo'))
2485
2599
 
2486
2600
    def test_remove_new_option(self):
2487
2601
        a_dict = dict()
2488
 
        section = config.MutableSection('myID', a_dict)
 
2602
        section = self.get_section(a_dict)
2489
2603
        section.set('foo', 'bar')
2490
2604
        section.remove('foo')
2491
2605
        self.assertFalse('foo' in section.options)
2495
2609
        self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
2496
2610
 
2497
2611
 
 
2612
class TestCommandLineStore(tests.TestCase):
 
2613
 
 
2614
    def setUp(self):
 
2615
        super(TestCommandLineStore, self).setUp()
 
2616
        self.store = config.CommandLineStore()
 
2617
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
 
2618
 
 
2619
    def get_section(self):
 
2620
        """Get the unique section for the command line overrides."""
 
2621
        sections = list(self.store.get_sections())
 
2622
        self.assertLength(1, sections)
 
2623
        store, section = sections[0]
 
2624
        self.assertEquals(self.store, store)
 
2625
        return section
 
2626
 
 
2627
    def test_no_override(self):
 
2628
        self.store._from_cmdline([])
 
2629
        section = self.get_section()
 
2630
        self.assertLength(0, list(section.iter_option_names()))
 
2631
 
 
2632
    def test_simple_override(self):
 
2633
        self.store._from_cmdline(['a=b'])
 
2634
        section = self.get_section()
 
2635
        self.assertEqual('b', section.get('a'))
 
2636
 
 
2637
    def test_list_override(self):
 
2638
        opt = config.ListOption('l')
 
2639
        config.option_registry.register(opt)
 
2640
        self.store._from_cmdline(['l=1,2,3'])
 
2641
        val = self.get_section().get('l')
 
2642
        self.assertEqual('1,2,3', val)
 
2643
        # Reminder: lists should be registered as such explicitely, otherwise
 
2644
        # the conversion needs to be done afterwards.
 
2645
        self.assertEqual(['1', '2', '3'],
 
2646
                         opt.convert_from_unicode(self.store, val))
 
2647
 
 
2648
    def test_multiple_overrides(self):
 
2649
        self.store._from_cmdline(['a=b', 'x=y'])
 
2650
        section = self.get_section()
 
2651
        self.assertEquals('b', section.get('a'))
 
2652
        self.assertEquals('y', section.get('x'))
 
2653
 
 
2654
    def test_wrong_syntax(self):
 
2655
        self.assertRaises(errors.BzrCommandError,
 
2656
                          self.store._from_cmdline, ['a=b', 'c'])
 
2657
 
 
2658
class TestStoreMinimalAPI(tests.TestCaseWithTransport):
 
2659
 
 
2660
    scenarios = [(key, {'get_store': builder}) for key, builder
 
2661
                 in config.test_store_builder_registry.iteritems()] + [
 
2662
        ('cmdline', {'get_store': lambda test: config.CommandLineStore()})]
 
2663
 
 
2664
    def test_id(self):
 
2665
        store = self.get_store(self)
 
2666
        if type(store) == config.TransportIniFileStore:
 
2667
            raise tests.TestNotApplicable(
 
2668
                "%s is not a concrete Store implementation"
 
2669
                " so it doesn't need an id" % (store.__class__.__name__,))
 
2670
        self.assertIsNot(None, store.id)
 
2671
 
 
2672
 
2498
2673
class TestStore(tests.TestCaseWithTransport):
2499
2674
 
2500
 
    def assertSectionContent(self, expected, section):
 
2675
    def assertSectionContent(self, expected, (store, section)):
2501
2676
        """Assert that some options have the proper values in a section."""
2502
2677
        expected_name, expected_options = expected
2503
2678
        self.assertEquals(expected_name, section.id)
2511
2686
    scenarios = [(key, {'get_store': builder}) for key, builder
2512
2687
                 in config.test_store_builder_registry.iteritems()]
2513
2688
 
2514
 
    def setUp(self):
2515
 
        super(TestReadonlyStore, self).setUp()
2516
 
 
2517
2689
    def test_building_delays_load(self):
2518
2690
        store = self.get_store(self)
2519
2691
        self.assertEquals(False, store.is_loaded())
2545
2717
        self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2546
2718
 
2547
2719
 
 
2720
class TestStoreQuoting(TestStore):
 
2721
 
 
2722
    scenarios = [(key, {'get_store': builder}) for key, builder
 
2723
                 in config.test_store_builder_registry.iteritems()]
 
2724
 
 
2725
    def setUp(self):
 
2726
        super(TestStoreQuoting, self).setUp()
 
2727
        self.store = self.get_store(self)
 
2728
        # We need a loaded store but any content will do
 
2729
        self.store._load_from_string('')
 
2730
 
 
2731
    def assertIdempotent(self, s):
 
2732
        """Assert that quoting an unquoted string is a no-op and vice-versa.
 
2733
 
 
2734
        What matters here is that option values, as they appear in a store, can
 
2735
        be safely round-tripped out of the store and back.
 
2736
 
 
2737
        :param s: A string, quoted if required.
 
2738
        """
 
2739
        self.assertEquals(s, self.store.quote(self.store.unquote(s)))
 
2740
        self.assertEquals(s, self.store.unquote(self.store.quote(s)))
 
2741
 
 
2742
    def test_empty_string(self):
 
2743
        if isinstance(self.store, config.IniFileStore):
 
2744
            # configobj._quote doesn't handle empty values
 
2745
            self.assertRaises(AssertionError,
 
2746
                              self.assertIdempotent, '')
 
2747
        else:
 
2748
            self.assertIdempotent('')
 
2749
        # But quoted empty strings are ok
 
2750
        self.assertIdempotent('""')
 
2751
 
 
2752
    def test_embedded_spaces(self):
 
2753
        self.assertIdempotent('" a b c "')
 
2754
 
 
2755
    def test_embedded_commas(self):
 
2756
        self.assertIdempotent('" a , b c "')
 
2757
 
 
2758
    def test_simple_comma(self):
 
2759
        if isinstance(self.store, config.IniFileStore):
 
2760
            # configobj requires that lists are special-cased
 
2761
           self.assertRaises(AssertionError,
 
2762
                             self.assertIdempotent, ',')
 
2763
        else:
 
2764
            self.assertIdempotent(',')
 
2765
        # When a single comma is required, quoting is also required
 
2766
        self.assertIdempotent('","')
 
2767
 
 
2768
    def test_list(self):
 
2769
        if isinstance(self.store, config.IniFileStore):
 
2770
            # configobj requires that lists are special-cased
 
2771
            self.assertRaises(AssertionError,
 
2772
                              self.assertIdempotent, 'a,b')
 
2773
        else:
 
2774
            self.assertIdempotent('a,b')
 
2775
 
 
2776
 
 
2777
class TestDictFromStore(tests.TestCase):
 
2778
 
 
2779
    def test_unquote_not_string(self):
 
2780
        conf = config.MemoryStack('x=2\n[a_section]\na=1\n')
 
2781
        value = conf.get('a_section')
 
2782
        # Urgh, despite 'conf' asking for the no-name section, we get the
 
2783
        # content of another section as a dict o_O
 
2784
        self.assertEquals({'a': '1'}, value)
 
2785
        unquoted = conf.store.unquote(value)
 
2786
        # Which cannot be unquoted but shouldn't crash either (the use cases
 
2787
        # are getting the value or displaying it. In the later case, '%s' will
 
2788
        # do).
 
2789
        self.assertEquals({'a': '1'}, unquoted)
 
2790
        self.assertEquals("{u'a': u'1'}", '%s' % (unquoted,))
 
2791
 
 
2792
 
2548
2793
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2549
 
    """Simulate loading a config store without content of various encodings.
 
2794
    """Simulate loading a config store with content of various encodings.
2550
2795
 
2551
2796
    All files produced by bzr are in utf8 content.
2552
2797
 
2565
2810
        utf8_content = unicode_content.encode('utf8')
2566
2811
        # Store the raw content in the config file
2567
2812
        t.put_bytes('foo.conf', utf8_content)
2568
 
        store = config.IniFileStore(t, 'foo.conf')
 
2813
        store = config.TransportIniFileStore(t, 'foo.conf')
2569
2814
        store.load()
2570
2815
        stack = config.Stack([store.get_sections], store)
2571
2816
        self.assertEquals(unicode_user, stack.get('user'))
2574
2819
        """Ensure we display a proper error on non-ascii, non utf-8 content."""
2575
2820
        t = self.get_transport()
2576
2821
        t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2577
 
        store = config.IniFileStore(t, 'foo.conf')
 
2822
        store = config.TransportIniFileStore(t, 'foo.conf')
2578
2823
        self.assertRaises(errors.ConfigContentError, store.load)
2579
2824
 
2580
2825
    def test_load_erroneous_content(self):
2581
2826
        """Ensure we display a proper error on content that can't be parsed."""
2582
2827
        t = self.get_transport()
2583
2828
        t.put_bytes('foo.conf', '[open_section\n')
2584
 
        store = config.IniFileStore(t, 'foo.conf')
 
2829
        store = config.TransportIniFileStore(t, 'foo.conf')
2585
2830
        self.assertRaises(errors.ParseConfigError, store.load)
2586
2831
 
 
2832
    def test_load_permission_denied(self):
 
2833
        """Ensure we get warned when trying to load an inaccessible file."""
 
2834
        warnings = []
 
2835
        def warning(*args):
 
2836
            warnings.append(args[0] % args[1:])
 
2837
        self.overrideAttr(trace, 'warning', warning)
 
2838
 
 
2839
        t = self.get_transport()
 
2840
 
 
2841
        def get_bytes(relpath):
 
2842
            raise errors.PermissionDenied(relpath, "")
 
2843
        t.get_bytes = get_bytes
 
2844
        store = config.TransportIniFileStore(t, 'foo.conf')
 
2845
        self.assertRaises(errors.PermissionDenied, store.load)
 
2846
        self.assertEquals(
 
2847
            warnings,
 
2848
            [u'Permission denied while trying to load configuration store %s.'
 
2849
             % store.external_url()])
 
2850
 
2587
2851
 
2588
2852
class TestIniConfigContent(tests.TestCaseWithTransport):
2589
 
    """Simulate loading a IniBasedConfig without content of various encodings.
 
2853
    """Simulate loading a IniBasedConfig with content of various encodings.
2590
2854
 
2591
2855
    All files produced by bzr are in utf8 content.
2592
2856
 
2737
3001
        self.assertEquals((store,), calls[0])
2738
3002
 
2739
3003
 
2740
 
class TestIniFileStore(TestStore):
 
3004
class TestQuotingIniFileStore(tests.TestCaseWithTransport):
 
3005
 
 
3006
    def get_store(self):
 
3007
        return config.TransportIniFileStore(self.get_transport(), 'foo.conf')
 
3008
 
 
3009
    def test_get_quoted_string(self):
 
3010
        store = self.get_store()
 
3011
        store._load_from_string('foo= " abc "')
 
3012
        stack = config.Stack([store.get_sections])
 
3013
        self.assertEquals(' abc ', stack.get('foo'))
 
3014
 
 
3015
    def test_set_quoted_string(self):
 
3016
        store = self.get_store()
 
3017
        stack = config.Stack([store.get_sections], store)
 
3018
        stack.set('foo', ' a b c ')
 
3019
        store.save()
 
3020
        self.assertFileEqual('foo = " a b c "' + os.linesep, 'foo.conf')
 
3021
 
 
3022
 
 
3023
class TestTransportIniFileStore(TestStore):
2741
3024
 
2742
3025
    def test_loading_unknown_file_fails(self):
2743
 
        store = config.IniFileStore(self.get_transport(), 'I-do-not-exist')
 
3026
        store = config.TransportIniFileStore(self.get_transport(),
 
3027
            'I-do-not-exist')
2744
3028
        self.assertRaises(errors.NoSuchFile, store.load)
2745
3029
 
2746
3030
    def test_invalid_content(self):
2747
 
        store = config.IniFileStore(self.get_transport(), 'foo.conf', )
 
3031
        store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
2748
3032
        self.assertEquals(False, store.is_loaded())
2749
3033
        exc = self.assertRaises(
2750
3034
            errors.ParseConfigError, store._load_from_string,
2758
3042
        # option names share the same name space...)
2759
3043
        # FIXME: This should be fixed by forbidding dicts as values ?
2760
3044
        # -- vila 2011-04-05
2761
 
        store = config.IniFileStore(self.get_transport(), 'foo.conf', )
 
3045
        store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
2762
3046
        store._load_from_string('''
2763
3047
foo=bar
2764
3048
l=1,2
2774
3058
        sections = list(store.get_sections())
2775
3059
        self.assertLength(4, sections)
2776
3060
        # The default section has no name.
2777
 
        # List values are provided as lists
2778
 
        self.assertSectionContent((None, {'foo': 'bar', 'l': ['1', '2']}),
 
3061
        # List values are provided as strings and need to be explicitly
 
3062
        # converted by specifying from_unicode=list_from_store at option
 
3063
        # registration
 
3064
        self.assertSectionContent((None, {'foo': 'bar', 'l': u'1,2'}),
2779
3065
                                  sections[0])
2780
3066
        self.assertSectionContent(
2781
3067
            ('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
2811
3097
 
2812
3098
    def setUp(self):
2813
3099
        super(TestConcurrentStoreUpdates, self).setUp()
2814
 
        self._content = 'one=1\ntwo=2\n'
2815
3100
        self.stack = self.get_stack(self)
2816
3101
        if not isinstance(self.stack, config._CompatibleStack):
2817
3102
            raise tests.TestNotApplicable(
2818
3103
                '%s is not meant to be compatible with the old config design'
2819
3104
                % (self.stack,))
2820
 
        self.stack.store._load_from_string(self._content)
 
3105
        self.stack.set('one', '1')
 
3106
        self.stack.set('two', '2')
2821
3107
        # Flush the store
2822
3108
        self.stack.store.save()
2823
3109
 
2927
3213
    # FIXME: It may be worth looking into removing the lock dir when it's not
2928
3214
    # needed anymore and look at possible fallouts for concurrent lockers. This
2929
3215
    # will matter if/when we use config files outside of bazaar directories
2930
 
    # (.bazaar or .bzr) -- vila 20110-04-11
 
3216
    # (.bazaar or .bzr) -- vila 20110-04-111
2931
3217
 
2932
3218
 
2933
3219
class TestSectionMatcher(TestStore):
2934
3220
 
2935
 
    scenarios = [('location', {'matcher': config.LocationMatcher})]
 
3221
    scenarios = [('location', {'matcher': config.LocationMatcher}),
 
3222
                 ('id', {'matcher': config.NameMatcher}),]
2936
3223
 
2937
 
    def get_store(self, file_name):
2938
 
        return config.IniFileStore(self.get_readonly_transport(), file_name)
 
3224
    def setUp(self):
 
3225
        super(TestSectionMatcher, self).setUp()
 
3226
        # Any simple store is good enough
 
3227
        self.get_store = config.test_store_builder_registry.get('configobj')
2939
3228
 
2940
3229
    def test_no_matches_for_empty_stores(self):
2941
 
        store = self.get_store('foo.conf')
 
3230
        store = self.get_store(self)
2942
3231
        store._load_from_string('')
2943
3232
        matcher = self.matcher(store, '/bar')
2944
3233
        self.assertEquals([], list(matcher.get_sections()))
2945
3234
 
2946
3235
    def test_build_doesnt_load_store(self):
2947
 
        store = self.get_store('foo.conf')
 
3236
        store = self.get_store(self)
2948
3237
        matcher = self.matcher(store, '/bar')
2949
3238
        self.assertFalse(store.is_loaded())
2950
3239
 
2974
3263
 
2975
3264
class TestLocationMatcher(TestStore):
2976
3265
 
2977
 
    def get_store(self, file_name):
2978
 
        return config.IniFileStore(self.get_readonly_transport(), file_name)
 
3266
    def setUp(self):
 
3267
        super(TestLocationMatcher, self).setUp()
 
3268
        # Any simple store is good enough
 
3269
        self.get_store = config.test_store_builder_registry.get('configobj')
2979
3270
 
2980
3271
    def test_unrelated_section_excluded(self):
2981
 
        store = self.get_store('foo.conf')
 
3272
        store = self.get_store(self)
2982
3273
        store._load_from_string('''
2983
3274
[/foo]
2984
3275
section=/foo
2993
3284
''')
2994
3285
        self.assertEquals(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
2995
3286
                           '/quux/quux'],
2996
 
                          [section.id for section in store.get_sections()])
 
3287
                          [section.id for _, section in store.get_sections()])
2997
3288
        matcher = config.LocationMatcher(store, '/foo/bar/quux')
2998
 
        sections = list(matcher.get_sections())
 
3289
        sections = [section for s, section in matcher.get_sections()]
2999
3290
        self.assertEquals([3, 2],
3000
3291
                          [section.length for section in sections])
3001
3292
        self.assertEquals(['/foo/bar', '/foo'],
3004
3295
                          [section.extra_path for section in sections])
3005
3296
 
3006
3297
    def test_more_specific_sections_first(self):
3007
 
        store = self.get_store('foo.conf')
 
3298
        store = self.get_store(self)
3008
3299
        store._load_from_string('''
3009
3300
[/foo]
3010
3301
section=/foo
3012
3303
section=/foo/bar
3013
3304
''')
3014
3305
        self.assertEquals(['/foo', '/foo/bar'],
3015
 
                          [section.id for section in store.get_sections()])
 
3306
                          [section.id for _, section in store.get_sections()])
3016
3307
        matcher = config.LocationMatcher(store, '/foo/bar/baz')
3017
 
        sections = list(matcher.get_sections())
 
3308
        sections = [section for s, section in matcher.get_sections()]
3018
3309
        self.assertEquals([3, 2],
3019
3310
                          [section.length for section in sections])
3020
3311
        self.assertEquals(['/foo/bar', '/foo'],
3025
3316
    def test_appendpath_in_no_name_section(self):
3026
3317
        # It's a bit weird to allow appendpath in a no-name section, but
3027
3318
        # someone may found a use for it
3028
 
        store = self.get_store('foo.conf')
 
3319
        store = self.get_store(self)
3029
3320
        store._load_from_string('''
3030
3321
foo=bar
3031
3322
foo:policy = appendpath
3033
3324
        matcher = config.LocationMatcher(store, 'dir/subdir')
3034
3325
        sections = list(matcher.get_sections())
3035
3326
        self.assertLength(1, sections)
3036
 
        self.assertEquals('bar/dir/subdir', sections[0].get('foo'))
 
3327
        self.assertEquals('bar/dir/subdir', sections[0][1].get('foo'))
3037
3328
 
3038
3329
    def test_file_urls_are_normalized(self):
3039
 
        store = self.get_store('foo.conf')
 
3330
        store = self.get_store(self)
3040
3331
        if sys.platform == 'win32':
3041
3332
            expected_url = 'file:///C:/dir/subdir'
3042
3333
            expected_location = 'C:/dir/subdir'
3047
3338
        self.assertEquals(expected_location, matcher.location)
3048
3339
 
3049
3340
 
3050
 
class TestStackGet(tests.TestCase):
3051
 
 
3052
 
    # FIXME: This should be parametrized for all known Stack or dedicated
3053
 
    # paramerized tests created to avoid bloating -- vila 2011-03-31
3054
 
 
3055
 
    def overrideOptionRegistry(self):
 
3341
class TestNameMatcher(TestStore):
 
3342
 
 
3343
    def setUp(self):
 
3344
        super(TestNameMatcher, self).setUp()
 
3345
        self.matcher = config.NameMatcher
 
3346
        # Any simple store is good enough
 
3347
        self.get_store = config.test_store_builder_registry.get('configobj')
 
3348
 
 
3349
    def get_matching_sections(self, name):
 
3350
        store = self.get_store(self)
 
3351
        store._load_from_string('''
 
3352
[foo]
 
3353
option=foo
 
3354
[foo/baz]
 
3355
option=foo/baz
 
3356
[bar]
 
3357
option=bar
 
3358
''')
 
3359
        matcher = self.matcher(store, name)
 
3360
        return list(matcher.get_sections())
 
3361
 
 
3362
    def test_matching(self):
 
3363
        sections = self.get_matching_sections('foo')
 
3364
        self.assertLength(1, sections)
 
3365
        self.assertSectionContent(('foo', {'option': 'foo'}), sections[0])
 
3366
 
 
3367
    def test_not_matching(self):
 
3368
        sections = self.get_matching_sections('baz')
 
3369
        self.assertLength(0, sections)
 
3370
 
 
3371
 
 
3372
class TestBaseStackGet(tests.TestCase):
 
3373
 
 
3374
    def setUp(self):
 
3375
        super(TestBaseStackGet, self).setUp()
3056
3376
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3057
3377
 
3058
 
    def test_single_config_get(self):
3059
 
        conf = dict(foo='bar')
3060
 
        conf_stack = config.Stack([conf])
3061
 
        self.assertEquals('bar', conf_stack.get('foo'))
 
3378
    def test_get_first_definition(self):
 
3379
        store1 = config.IniFileStore()
 
3380
        store1._load_from_string('foo=bar')
 
3381
        store2 = config.IniFileStore()
 
3382
        store2._load_from_string('foo=baz')
 
3383
        conf = config.Stack([store1.get_sections, store2.get_sections])
 
3384
        self.assertEquals('bar', conf.get('foo'))
3062
3385
 
3063
3386
    def test_get_with_registered_default_value(self):
3064
 
        conf_stack = config.Stack([dict()])
3065
 
        opt = config.Option('foo', default='bar')
3066
 
        self.overrideOptionRegistry()
3067
 
        config.option_registry.register('foo', opt)
 
3387
        config.option_registry.register(config.Option('foo', default='bar'))
 
3388
        conf_stack = config.Stack([])
3068
3389
        self.assertEquals('bar', conf_stack.get('foo'))
3069
3390
 
3070
3391
    def test_get_without_registered_default_value(self):
3071
 
        conf_stack = config.Stack([dict()])
3072
 
        opt = config.Option('foo')
3073
 
        self.overrideOptionRegistry()
3074
 
        config.option_registry.register('foo', opt)
 
3392
        config.option_registry.register(config.Option('foo'))
 
3393
        conf_stack = config.Stack([])
3075
3394
        self.assertEquals(None, conf_stack.get('foo'))
3076
3395
 
3077
3396
    def test_get_without_default_value_for_not_registered(self):
3078
 
        conf_stack = config.Stack([dict()])
3079
 
        opt = config.Option('foo')
3080
 
        self.overrideOptionRegistry()
 
3397
        conf_stack = config.Stack([])
3081
3398
        self.assertEquals(None, conf_stack.get('foo'))
3082
3399
 
3083
 
    def test_get_first_definition(self):
3084
 
        conf1 = dict(foo='bar')
3085
 
        conf2 = dict(foo='baz')
3086
 
        conf_stack = config.Stack([conf1, conf2])
3087
 
        self.assertEquals('bar', conf_stack.get('foo'))
3088
 
 
3089
 
    def test_get_embedded_definition(self):
3090
 
        conf1 = dict(yy='12')
3091
 
        conf2 = config.Stack([dict(xx='42'), dict(foo='baz')])
3092
 
        conf_stack = config.Stack([conf1, conf2])
3093
 
        self.assertEquals('baz', conf_stack.get('foo'))
3094
 
 
3095
3400
    def test_get_for_empty_section_callable(self):
3096
3401
        conf_stack = config.Stack([lambda : []])
3097
3402
        self.assertEquals(None, conf_stack.get('foo'))
3098
3403
 
3099
3404
    def test_get_for_broken_callable(self):
3100
3405
        # Trying to use and invalid callable raises an exception on first use
3101
 
        conf_stack = config.Stack([lambda : object()])
 
3406
        conf_stack = config.Stack([object])
3102
3407
        self.assertRaises(TypeError, conf_stack.get, 'foo')
3103
3408
 
3104
3409
 
 
3410
class TestStackWithSimpleStore(tests.TestCase):
 
3411
 
 
3412
    def setUp(self):
 
3413
        super(TestStackWithSimpleStore, self).setUp()
 
3414
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
 
3415
        self.registry = config.option_registry
 
3416
 
 
3417
    def get_conf(self, content=None):
 
3418
        return config.MemoryStack(content)
 
3419
 
 
3420
    def test_override_value_from_env(self):
 
3421
        self.registry.register(
 
3422
            config.Option('foo', default='bar', override_from_env=['FOO']))
 
3423
        self.overrideEnv('FOO', 'quux')
 
3424
        # Env variable provides a default taking over the option one
 
3425
        conf = self.get_conf('foo=store')
 
3426
        self.assertEquals('quux', conf.get('foo'))
 
3427
 
 
3428
    def test_first_override_value_from_env_wins(self):
 
3429
        self.registry.register(
 
3430
            config.Option('foo', default='bar',
 
3431
                          override_from_env=['NO_VALUE', 'FOO', 'BAZ']))
 
3432
        self.overrideEnv('FOO', 'foo')
 
3433
        self.overrideEnv('BAZ', 'baz')
 
3434
        # The first env var set wins
 
3435
        conf = self.get_conf('foo=store')
 
3436
        self.assertEquals('foo', conf.get('foo'))
 
3437
 
 
3438
 
 
3439
class TestMemoryStack(tests.TestCase):
 
3440
 
 
3441
    def test_get(self):
 
3442
        conf = config.MemoryStack('foo=bar')
 
3443
        self.assertEquals('bar', conf.get('foo'))
 
3444
 
 
3445
    def test_set(self):
 
3446
        conf = config.MemoryStack('foo=bar')
 
3447
        conf.set('foo', 'baz')
 
3448
        self.assertEquals('baz', conf.get('foo'))
 
3449
 
 
3450
    def test_no_content(self):
 
3451
        conf = config.MemoryStack()
 
3452
        # No content means no loading
 
3453
        self.assertFalse(conf.store.is_loaded())
 
3454
        self.assertRaises(NotImplementedError, conf.get, 'foo')
 
3455
        # But a content can still be provided
 
3456
        conf.store._load_from_string('foo=bar')
 
3457
        self.assertEquals('bar', conf.get('foo'))
 
3458
 
 
3459
 
3105
3460
class TestStackWithTransport(tests.TestCaseWithTransport):
3106
3461
 
3107
3462
    scenarios = [(key, {'get_stack': builder}) for key, builder
3125
3480
        self.assertEquals(None, self.conf.get('foo'))
3126
3481
 
3127
3482
    def test_get_hook(self):
3128
 
        self.conf.store._load_from_string('foo=bar')
 
3483
        self.conf.set('foo', 'bar')
3129
3484
        calls = []
3130
3485
        def hook(*args):
3131
3486
            calls.append(args)
3137
3492
        self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
3138
3493
 
3139
3494
 
3140
 
class TestStackGetWithConverter(TestStackGet):
 
3495
class TestStackGetWithConverter(tests.TestCase):
3141
3496
 
3142
3497
    def setUp(self):
3143
3498
        super(TestStackGetWithConverter, self).setUp()
3144
3499
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3145
3500
        self.registry = config.option_registry
3146
3501
 
 
3502
    def get_conf(self, content=None):
 
3503
        return config.MemoryStack(content)
 
3504
 
3147
3505
    def register_bool_option(self, name, default=None, default_from_env=None):
3148
3506
        b = config.Option(name, help='A boolean.',
3149
3507
                          default=default, default_from_env=default_from_env,
3152
3510
 
3153
3511
    def test_get_default_bool_None(self):
3154
3512
        self.register_bool_option('foo')
3155
 
        self.assertEquals(None, self.conf.get('foo'))
 
3513
        conf = self.get_conf('')
 
3514
        self.assertEquals(None, conf.get('foo'))
3156
3515
 
3157
3516
    def test_get_default_bool_True(self):
3158
3517
        self.register_bool_option('foo', u'True')
3159
 
        self.assertEquals(True, self.conf.get('foo'))
 
3518
        conf = self.get_conf('')
 
3519
        self.assertEquals(True, conf.get('foo'))
3160
3520
 
3161
3521
    def test_get_default_bool_False(self):
3162
3522
        self.register_bool_option('foo', False)
3163
 
        self.assertEquals(False, self.conf.get('foo'))
 
3523
        conf = self.get_conf('')
 
3524
        self.assertEquals(False, conf.get('foo'))
3164
3525
 
3165
3526
    def test_get_default_bool_False_as_string(self):
3166
3527
        self.register_bool_option('foo', u'False')
3167
 
        self.assertEquals(False, self.conf.get('foo'))
 
3528
        conf = self.get_conf('')
 
3529
        self.assertEquals(False, conf.get('foo'))
3168
3530
 
3169
3531
    def test_get_default_bool_from_env_converted(self):
3170
3532
        self.register_bool_option('foo', u'True', default_from_env=['FOO'])
3171
3533
        self.overrideEnv('FOO', 'False')
3172
 
        self.assertEquals(False, self.conf.get('foo'))
 
3534
        conf = self.get_conf('')
 
3535
        self.assertEquals(False, conf.get('foo'))
3173
3536
 
3174
3537
    def test_get_default_bool_when_conversion_fails(self):
3175
3538
        self.register_bool_option('foo', default='True')
3176
 
        self.conf.store._load_from_string('foo=invalid boolean')
3177
 
        self.assertEquals(True, self.conf.get('foo'))
 
3539
        conf = self.get_conf('foo=invalid boolean')
 
3540
        self.assertEquals(True, conf.get('foo'))
3178
3541
 
3179
3542
    def register_integer_option(self, name,
3180
3543
                                default=None, default_from_env=None):
3185
3548
 
3186
3549
    def test_get_default_integer_None(self):
3187
3550
        self.register_integer_option('foo')
3188
 
        self.assertEquals(None, self.conf.get('foo'))
 
3551
        conf = self.get_conf('')
 
3552
        self.assertEquals(None, conf.get('foo'))
3189
3553
 
3190
3554
    def test_get_default_integer(self):
3191
3555
        self.register_integer_option('foo', 42)
3192
 
        self.assertEquals(42, self.conf.get('foo'))
 
3556
        conf = self.get_conf('')
 
3557
        self.assertEquals(42, conf.get('foo'))
3193
3558
 
3194
3559
    def test_get_default_integer_as_string(self):
3195
3560
        self.register_integer_option('foo', u'42')
3196
 
        self.assertEquals(42, self.conf.get('foo'))
 
3561
        conf = self.get_conf('')
 
3562
        self.assertEquals(42, conf.get('foo'))
3197
3563
 
3198
3564
    def test_get_default_integer_from_env(self):
3199
3565
        self.register_integer_option('foo', default_from_env=['FOO'])
3200
3566
        self.overrideEnv('FOO', '18')
3201
 
        self.assertEquals(18, self.conf.get('foo'))
 
3567
        conf = self.get_conf('')
 
3568
        self.assertEquals(18, conf.get('foo'))
3202
3569
 
3203
3570
    def test_get_default_integer_when_conversion_fails(self):
3204
3571
        self.register_integer_option('foo', default='12')
3205
 
        self.conf.store._load_from_string('foo=invalid integer')
3206
 
        self.assertEquals(12, self.conf.get('foo'))
 
3572
        conf = self.get_conf('foo=invalid integer')
 
3573
        self.assertEquals(12, conf.get('foo'))
3207
3574
 
3208
3575
    def register_list_option(self, name, default=None, default_from_env=None):
3209
 
        l = config.Option(name, help='A list.',
3210
 
                          default=default, default_from_env=default_from_env,
3211
 
                          from_unicode=config.list_from_store)
 
3576
        l = config.ListOption(name, help='A list.', default=default,
 
3577
                              default_from_env=default_from_env)
3212
3578
        self.registry.register(l)
3213
3579
 
3214
3580
    def test_get_default_list_None(self):
3215
3581
        self.register_list_option('foo')
3216
 
        self.assertEquals(None, self.conf.get('foo'))
 
3582
        conf = self.get_conf('')
 
3583
        self.assertEquals(None, conf.get('foo'))
3217
3584
 
3218
3585
    def test_get_default_list_empty(self):
3219
3586
        self.register_list_option('foo', '')
3220
 
        self.assertEquals([], self.conf.get('foo'))
 
3587
        conf = self.get_conf('')
 
3588
        self.assertEquals([], conf.get('foo'))
3221
3589
 
3222
3590
    def test_get_default_list_from_env(self):
3223
3591
        self.register_list_option('foo', default_from_env=['FOO'])
3224
3592
        self.overrideEnv('FOO', '')
3225
 
        self.assertEquals([], self.conf.get('foo'))
 
3593
        conf = self.get_conf('')
 
3594
        self.assertEquals([], conf.get('foo'))
3226
3595
 
3227
3596
    def test_get_with_list_converter_no_item(self):
3228
3597
        self.register_list_option('foo', None)
3229
 
        self.conf.store._load_from_string('foo=,')
3230
 
        self.assertEquals([], self.conf.get('foo'))
 
3598
        conf = self.get_conf('foo=,')
 
3599
        self.assertEquals([], conf.get('foo'))
3231
3600
 
3232
3601
    def test_get_with_list_converter_many_items(self):
3233
3602
        self.register_list_option('foo', None)
3234
 
        self.conf.store._load_from_string('foo=m,o,r,e')
3235
 
        self.assertEquals(['m', 'o', 'r', 'e'], self.conf.get('foo'))
 
3603
        conf = self.get_conf('foo=m,o,r,e')
 
3604
        self.assertEquals(['m', 'o', 'r', 'e'], conf.get('foo'))
 
3605
 
 
3606
    def test_get_with_list_converter_embedded_spaces_many_items(self):
 
3607
        self.register_list_option('foo', None)
 
3608
        conf = self.get_conf('foo=" bar", "baz "')
 
3609
        self.assertEquals([' bar', 'baz '], conf.get('foo'))
 
3610
 
 
3611
    def test_get_with_list_converter_stripped_spaces_many_items(self):
 
3612
        self.register_list_option('foo', None)
 
3613
        conf = self.get_conf('foo= bar ,  baz ')
 
3614
        self.assertEquals(['bar', 'baz'], conf.get('foo'))
 
3615
 
 
3616
 
 
3617
class TestIterOptionRefs(tests.TestCase):
 
3618
    """iter_option_refs is a bit unusual, document some cases."""
 
3619
 
 
3620
    def assertRefs(self, expected, string):
 
3621
        self.assertEquals(expected, list(config.iter_option_refs(string)))
 
3622
 
 
3623
    def test_empty(self):
 
3624
        self.assertRefs([(False, '')], '')
 
3625
 
 
3626
    def test_no_refs(self):
 
3627
        self.assertRefs([(False, 'foo bar')], 'foo bar')
 
3628
 
 
3629
    def test_single_ref(self):
 
3630
        self.assertRefs([(False, ''), (True, '{foo}'), (False, '')], '{foo}')
 
3631
 
 
3632
    def test_broken_ref(self):
 
3633
        self.assertRefs([(False, '{foo')], '{foo')
 
3634
 
 
3635
    def test_embedded_ref(self):
 
3636
        self.assertRefs([(False, '{'), (True, '{foo}'), (False, '}')],
 
3637
                        '{{foo}}')
 
3638
 
 
3639
    def test_two_refs(self):
 
3640
        self.assertRefs([(False, ''), (True, '{foo}'),
 
3641
                         (False, ''), (True, '{bar}'),
 
3642
                         (False, ''),],
 
3643
                        '{foo}{bar}')
 
3644
 
 
3645
    def test_newline_in_refs_are_not_matched(self):
 
3646
        self.assertRefs([(False, '{\nxx}{xx\n}{{\n}}')], '{\nxx}{xx\n}{{\n}}')
 
3647
 
 
3648
 
 
3649
class TestStackExpandOptions(tests.TestCaseWithTransport):
 
3650
 
 
3651
    def setUp(self):
 
3652
        super(TestStackExpandOptions, self).setUp()
 
3653
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
 
3654
        self.registry = config.option_registry
 
3655
        self.conf = build_branch_stack(self)
 
3656
 
 
3657
    def assertExpansion(self, expected, string, env=None):
 
3658
        self.assertEquals(expected, self.conf.expand_options(string, env))
 
3659
 
 
3660
    def test_no_expansion(self):
 
3661
        self.assertExpansion('foo', 'foo')
 
3662
 
 
3663
    def test_expand_default_value(self):
 
3664
        self.conf.store._load_from_string('bar=baz')
 
3665
        self.registry.register(config.Option('foo', default=u'{bar}'))
 
3666
        self.assertEquals('baz', self.conf.get('foo', expand=True))
 
3667
 
 
3668
    def test_expand_default_from_env(self):
 
3669
        self.conf.store._load_from_string('bar=baz')
 
3670
        self.registry.register(config.Option('foo', default_from_env=['FOO']))
 
3671
        self.overrideEnv('FOO', '{bar}')
 
3672
        self.assertEquals('baz', self.conf.get('foo', expand=True))
 
3673
 
 
3674
    def test_expand_default_on_failed_conversion(self):
 
3675
        self.conf.store._load_from_string('baz=bogus\nbar=42\nfoo={baz}')
 
3676
        self.registry.register(
 
3677
            config.Option('foo', default=u'{bar}',
 
3678
                          from_unicode=config.int_from_store))
 
3679
        self.assertEquals(42, self.conf.get('foo', expand=True))
 
3680
 
 
3681
    def test_env_adding_options(self):
 
3682
        self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
 
3683
 
 
3684
    def test_env_overriding_options(self):
 
3685
        self.conf.store._load_from_string('foo=baz')
 
3686
        self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
 
3687
 
 
3688
    def test_simple_ref(self):
 
3689
        self.conf.store._load_from_string('foo=xxx')
 
3690
        self.assertExpansion('xxx', '{foo}')
 
3691
 
 
3692
    def test_unknown_ref(self):
 
3693
        self.assertRaises(errors.ExpandingUnknownOption,
 
3694
                          self.conf.expand_options, '{foo}')
 
3695
 
 
3696
    def test_indirect_ref(self):
 
3697
        self.conf.store._load_from_string('''
 
3698
foo=xxx
 
3699
bar={foo}
 
3700
''')
 
3701
        self.assertExpansion('xxx', '{bar}')
 
3702
 
 
3703
    def test_embedded_ref(self):
 
3704
        self.conf.store._load_from_string('''
 
3705
foo=xxx
 
3706
bar=foo
 
3707
''')
 
3708
        self.assertExpansion('xxx', '{{bar}}')
 
3709
 
 
3710
    def test_simple_loop(self):
 
3711
        self.conf.store._load_from_string('foo={foo}')
 
3712
        self.assertRaises(errors.OptionExpansionLoop,
 
3713
                          self.conf.expand_options, '{foo}')
 
3714
 
 
3715
    def test_indirect_loop(self):
 
3716
        self.conf.store._load_from_string('''
 
3717
foo={bar}
 
3718
bar={baz}
 
3719
baz={foo}''')
 
3720
        e = self.assertRaises(errors.OptionExpansionLoop,
 
3721
                              self.conf.expand_options, '{foo}')
 
3722
        self.assertEquals('foo->bar->baz', e.refs)
 
3723
        self.assertEquals('{foo}', e.string)
 
3724
 
 
3725
    def test_list(self):
 
3726
        self.conf.store._load_from_string('''
 
3727
foo=start
 
3728
bar=middle
 
3729
baz=end
 
3730
list={foo},{bar},{baz}
 
3731
''')
 
3732
        self.registry.register(
 
3733
            config.ListOption('list'))
 
3734
        self.assertEquals(['start', 'middle', 'end'],
 
3735
                           self.conf.get('list', expand=True))
 
3736
 
 
3737
    def test_cascading_list(self):
 
3738
        self.conf.store._load_from_string('''
 
3739
foo=start,{bar}
 
3740
bar=middle,{baz}
 
3741
baz=end
 
3742
list={foo}
 
3743
''')
 
3744
        self.registry.register(
 
3745
            config.ListOption('list'))
 
3746
        self.assertEquals(['start', 'middle', 'end'],
 
3747
                           self.conf.get('list', expand=True))
 
3748
 
 
3749
    def test_pathologically_hidden_list(self):
 
3750
        self.conf.store._load_from_string('''
 
3751
foo=bin
 
3752
bar=go
 
3753
start={foo
 
3754
middle=},{
 
3755
end=bar}
 
3756
hidden={start}{middle}{end}
 
3757
''')
 
3758
        # What matters is what the registration says, the conversion happens
 
3759
        # only after all expansions have been performed
 
3760
        self.registry.register(config.ListOption('hidden'))
 
3761
        self.assertEquals(['bin', 'go'],
 
3762
                          self.conf.get('hidden', expand=True))
 
3763
 
 
3764
 
 
3765
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
 
3766
 
 
3767
    def setUp(self):
 
3768
        super(TestStackCrossSectionsExpand, self).setUp()
 
3769
 
 
3770
    def get_config(self, location, string):
 
3771
        if string is None:
 
3772
            string = ''
 
3773
        # Since we don't save the config we won't strictly require to inherit
 
3774
        # from TestCaseInTempDir, but an error occurs so quickly...
 
3775
        c = config.LocationStack(location)
 
3776
        c.store._load_from_string(string)
 
3777
        return c
 
3778
 
 
3779
    def test_dont_cross_unrelated_section(self):
 
3780
        c = self.get_config('/another/branch/path','''
 
3781
[/one/branch/path]
 
3782
foo = hello
 
3783
bar = {foo}/2
 
3784
 
 
3785
[/another/branch/path]
 
3786
bar = {foo}/2
 
3787
''')
 
3788
        self.assertRaises(errors.ExpandingUnknownOption,
 
3789
                          c.get, 'bar', expand=True)
 
3790
 
 
3791
    def test_cross_related_sections(self):
 
3792
        c = self.get_config('/project/branch/path','''
 
3793
[/project]
 
3794
foo = qu
 
3795
 
 
3796
[/project/branch/path]
 
3797
bar = {foo}ux
 
3798
''')
 
3799
        self.assertEquals('quux', c.get('bar', expand=True))
 
3800
 
 
3801
 
 
3802
class TestStackCrossStoresExpand(tests.TestCaseWithTransport):
 
3803
 
 
3804
    def test_cross_global_locations(self):
 
3805
        l_store = config.LocationStore()
 
3806
        l_store._load_from_string('''
 
3807
[/branch]
 
3808
lfoo = loc-foo
 
3809
lbar = {gbar}
 
3810
''')
 
3811
        l_store.save()
 
3812
        g_store = config.GlobalStore()
 
3813
        g_store._load_from_string('''
 
3814
[DEFAULT]
 
3815
gfoo = {lfoo}
 
3816
gbar = glob-bar
 
3817
''')
 
3818
        g_store.save()
 
3819
        stack = config.LocationStack('/branch')
 
3820
        self.assertEquals('glob-bar', stack.get('lbar', expand=True))
 
3821
        self.assertEquals('loc-foo', stack.get('gfoo', expand=True))
 
3822
 
 
3823
 
 
3824
class TestStackExpandSectionLocals(tests.TestCaseWithTransport):
 
3825
 
 
3826
    def test_expand_locals_empty(self):
 
3827
        l_store = config.LocationStore()
 
3828
        l_store._load_from_string('''
 
3829
[/home/user/project]
 
3830
base = {basename}
 
3831
rel = {relpath}
 
3832
''')
 
3833
        l_store.save()
 
3834
        stack = config.LocationStack('/home/user/project/')
 
3835
        self.assertEquals('', stack.get('base', expand=True))
 
3836
        self.assertEquals('', stack.get('rel', expand=True))
 
3837
 
 
3838
    def test_expand_basename_locally(self):
 
3839
        l_store = config.LocationStore()
 
3840
        l_store._load_from_string('''
 
3841
[/home/user/project]
 
3842
bfoo = {basename}
 
3843
''')
 
3844
        l_store.save()
 
3845
        stack = config.LocationStack('/home/user/project/branch')
 
3846
        self.assertEquals('branch', stack.get('bfoo', expand=True))
 
3847
 
 
3848
    def test_expand_basename_locally_longer_path(self):
 
3849
        l_store = config.LocationStore()
 
3850
        l_store._load_from_string('''
 
3851
[/home/user]
 
3852
bfoo = {basename}
 
3853
''')
 
3854
        l_store.save()
 
3855
        stack = config.LocationStack('/home/user/project/dir/branch')
 
3856
        self.assertEquals('branch', stack.get('bfoo', expand=True))
 
3857
 
 
3858
    def test_expand_relpath_locally(self):
 
3859
        l_store = config.LocationStore()
 
3860
        l_store._load_from_string('''
 
3861
[/home/user/project]
 
3862
lfoo = loc-foo/{relpath}
 
3863
''')
 
3864
        l_store.save()
 
3865
        stack = config.LocationStack('/home/user/project/branch')
 
3866
        self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
 
3867
 
 
3868
    def test_expand_relpath_unknonw_in_global(self):
 
3869
        g_store = config.GlobalStore()
 
3870
        g_store._load_from_string('''
 
3871
[DEFAULT]
 
3872
gfoo = {relpath}
 
3873
''')
 
3874
        g_store.save()
 
3875
        stack = config.LocationStack('/home/user/project/branch')
 
3876
        self.assertRaises(errors.ExpandingUnknownOption,
 
3877
                          stack.get, 'gfoo', expand=True)
 
3878
 
 
3879
    def test_expand_local_option_locally(self):
 
3880
        l_store = config.LocationStore()
 
3881
        l_store._load_from_string('''
 
3882
[/home/user/project]
 
3883
lfoo = loc-foo/{relpath}
 
3884
lbar = {gbar}
 
3885
''')
 
3886
        l_store.save()
 
3887
        g_store = config.GlobalStore()
 
3888
        g_store._load_from_string('''
 
3889
[DEFAULT]
 
3890
gfoo = {lfoo}
 
3891
gbar = glob-bar
 
3892
''')
 
3893
        g_store.save()
 
3894
        stack = config.LocationStack('/home/user/project/branch')
 
3895
        self.assertEquals('glob-bar', stack.get('lbar', expand=True))
 
3896
        self.assertEquals('loc-foo/branch', stack.get('gfoo', expand=True))
 
3897
 
 
3898
    def test_locals_dont_leak(self):
 
3899
        """Make sure we chose the right local in presence of several sections.
 
3900
        """
 
3901
        l_store = config.LocationStore()
 
3902
        l_store._load_from_string('''
 
3903
[/home/user]
 
3904
lfoo = loc-foo/{relpath}
 
3905
[/home/user/project]
 
3906
lfoo = loc-foo/{relpath}
 
3907
''')
 
3908
        l_store.save()
 
3909
        stack = config.LocationStack('/home/user/project/branch')
 
3910
        self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
 
3911
        stack = config.LocationStack('/home/user/bar/baz')
 
3912
        self.assertEquals('loc-foo/bar/baz', stack.get('lfoo', expand=True))
 
3913
 
3236
3914
 
3237
3915
 
3238
3916
class TestStackSet(TestStackWithTransport):
3239
3917
 
3240
3918
    def test_simple_set(self):
3241
3919
        conf = self.get_stack(self)
3242
 
        conf.store._load_from_string('foo=bar')
3243
 
        self.assertEquals('bar', conf.get('foo'))
 
3920
        self.assertEquals(None, conf.get('foo'))
3244
3921
        conf.set('foo', 'baz')
3245
3922
        # Did we get it back ?
3246
3923
        self.assertEquals('baz', conf.get('foo'))
3266
3943
 
3267
3944
    def test_remove_existing(self):
3268
3945
        conf = self.get_stack(self)
3269
 
        conf.store._load_from_string('foo=bar')
 
3946
        conf.set('foo', 'bar')
3270
3947
        self.assertEquals('bar', conf.get('foo'))
3271
3948
        conf.remove('foo')
3272
3949
        # Did we get it back ?
3283
3960
        config.ConfigHooks.install_named_hook('remove', hook, None)
3284
3961
        self.assertLength(0, calls)
3285
3962
        conf = self.get_stack(self)
3286
 
        conf.store._load_from_string('foo=bar')
 
3963
        conf.set('foo', 'bar')
3287
3964
        conf.remove('foo')
3288
3965
        self.assertLength(1, calls)
3289
3966
        self.assertEquals((conf, 'foo'), calls[0])
3951
4628
 
3952
4629
    def test_auto_user_id(self):
3953
4630
        """Automatic inference of user name.
3954
 
        
 
4631
 
3955
4632
        This is a bit hard to test in an isolated way, because it depends on
3956
4633
        system functions that go direct to /etc or perhaps somewhere else.
3957
4634
        But it's reasonable to say that on Unix, with an /etc/mailname, we ought
3967
4644
        else:
3968
4645
            self.assertEquals((None, None), (realname, address))
3969
4646
 
 
4647
 
 
4648
class EmailOptionTests(tests.TestCase):
 
4649
 
 
4650
    def test_default_email_uses_BZR_EMAIL(self):
 
4651
        conf = config.MemoryStack('email=jelmer@debian.org')
 
4652
        # BZR_EMAIL takes precedence over EMAIL
 
4653
        self.overrideEnv('BZR_EMAIL', 'jelmer@samba.org')
 
4654
        self.overrideEnv('EMAIL', 'jelmer@apache.org')
 
4655
        self.assertEquals('jelmer@samba.org', conf.get('email'))
 
4656
 
 
4657
    def test_default_email_uses_EMAIL(self):
 
4658
        conf = config.MemoryStack('')
 
4659
        self.overrideEnv('BZR_EMAIL', None)
 
4660
        self.overrideEnv('EMAIL', 'jelmer@apache.org')
 
4661
        self.assertEquals('jelmer@apache.org', conf.get('email'))
 
4662
 
 
4663
    def test_BZR_EMAIL_overrides(self):
 
4664
        conf = config.MemoryStack('email=jelmer@debian.org')
 
4665
        self.overrideEnv('BZR_EMAIL', 'jelmer@apache.org')
 
4666
        self.assertEquals('jelmer@apache.org', conf.get('email'))
 
4667
        self.overrideEnv('BZR_EMAIL', None)
 
4668
        self.overrideEnv('EMAIL', 'jelmer@samba.org')
 
4669
        self.assertEquals('jelmer@debian.org', conf.get('email'))