~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_config.py

  • Committer: Patch Queue Manager
  • Date: 2012-03-12 13:38:09 UTC
  • mfrom: (6478.2.1 path2id-list)
  • Revision ID: pqm@pqm.ubuntu.com-20120312133809-q6xr6ujz40gpucun
(jelmer) Reintroduce support for passing lists to Tree.path2id(). (Jelmer
 Vernooij)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
 
1
# Copyright (C) 2005-2012 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
27
27
#import bzrlib specific imports here
28
28
from bzrlib import (
29
29
    branch,
30
 
    bzrdir,
31
30
    config,
 
31
    controldir,
32
32
    diff,
33
33
    errors,
34
34
    osutils,
35
35
    mail_client,
36
 
    mergetools,
37
36
    ui,
38
37
    urlutils,
39
 
    registry,
 
38
    registry as _mod_registry,
40
39
    remote,
41
40
    tests,
42
41
    trace,
43
 
    transport,
44
42
    )
45
43
from bzrlib.symbol_versioning import (
46
44
    deprecated_in,
47
 
    deprecated_method,
48
45
    )
49
46
from bzrlib.transport import remote as transport_remote
50
47
from bzrlib.tests import (
71
68
 
72
69
# Register helpers to build stores
73
70
config.test_store_builder_registry.register(
74
 
    'configobj', lambda test: config.IniFileStore(test.get_transport(),
75
 
                                                  'configobj.conf'))
 
71
    'configobj', lambda test: config.TransportIniFileStore(
 
72
        test.get_transport(), 'configobj.conf'))
76
73
config.test_store_builder_registry.register(
77
74
    'bazaar', lambda test: config.GlobalStore())
78
75
config.test_store_builder_registry.register(
116
113
config.test_store_builder_registry.register('branch', build_branch_store)
117
114
 
118
115
 
 
116
def build_control_store(test):
 
117
    build_backing_branch(test, 'branch')
 
118
    b = controldir.ControlDir.open('branch')
 
119
    return config.ControlStore(b)
 
120
config.test_store_builder_registry.register('control', build_control_store)
 
121
 
 
122
 
119
123
def build_remote_branch_store(test):
120
124
    # There is only one permutation (but we won't be able to handle more with
121
125
    # this design anyway)
141
145
config.test_stack_builder_registry.register('branch', build_branch_stack)
142
146
 
143
147
 
144
 
def build_remote_branch_stack(test):
145
 
    # There is only one permutation (but we won't be able to handle more with
146
 
    # this design anyway)
147
 
    (transport_class,
148
 
     server_class) = transport_remote.get_test_permutations()[0]
149
 
    build_backing_branch(test, 'branch', transport_class, server_class)
150
 
    b = branch.Branch.open(test.get_url('branch'))
151
 
    return config.BranchStack(b)
152
 
config.test_stack_builder_registry.register('remote_branch',
153
 
                                            build_remote_branch_stack)
 
148
def build_branch_only_stack(test):
 
149
    # There is only one permutation (but we won't be able to handle more with
 
150
    # this design anyway)
 
151
    (transport_class,
 
152
     server_class) = transport_remote.get_test_permutations()[0]
 
153
    build_backing_branch(test, 'branch', transport_class, server_class)
 
154
    b = branch.Branch.open(test.get_url('branch'))
 
155
    return config.BranchOnlyStack(b)
 
156
config.test_stack_builder_registry.register('branch_only',
 
157
                                            build_branch_only_stack)
 
158
 
 
159
def build_remote_control_stack(test):
 
160
    # There is only one permutation (but we won't be able to handle more with
 
161
    # this design anyway)
 
162
    (transport_class,
 
163
     server_class) = transport_remote.get_test_permutations()[0]
 
164
    # We need only a bzrdir for this, not a full branch, but it's not worth
 
165
    # creating a dedicated helper to create only the bzrdir
 
166
    build_backing_branch(test, 'branch', transport_class, server_class)
 
167
    b = branch.Branch.open(test.get_url('branch'))
 
168
    return config.RemoteControlStack(b.bzrdir)
 
169
config.test_stack_builder_registry.register('remote_control',
 
170
                                            build_remote_control_stack)
154
171
 
155
172
 
156
173
sample_long_alias="log -r-15..-1 --line"
160
177
editor=vim
161
178
change_editor=vimdiff -of @new_path @old_path
162
179
gpg_signing_command=gnome-gpg
 
180
gpg_signing_key=DD4D5088
163
181
log_format=short
164
182
validate_signatures_in_log=true
165
183
acceptable_keys=amy
166
184
user_global_option=something
167
185
bzr.mergetool.sometool=sometool {base} {this} {other} -o {result}
168
186
bzr.mergetool.funkytool=funkytool "arg with spaces" {this_temp}
 
187
bzr.mergetool.newtool='"newtool with spaces" {this_temp}'
169
188
bzr.default_mergetool=sometool
170
189
[ALIASES]
171
190
h=help
214
233
[/a/]
215
234
check_signatures=check-available
216
235
gpg_signing_command=false
 
236
gpg_signing_key=default
217
237
user_local_option=local
218
238
# test trailing / matching
219
239
[/a/*]
309
329
 
310
330
class FakeBranch(object):
311
331
 
312
 
    def __init__(self, base=None, user_id=None):
 
332
    def __init__(self, base=None):
313
333
        if base is None:
314
334
            self.base = "http://example.com/branches/demo"
315
335
        else:
316
336
            self.base = base
317
337
        self._transport = self.control_files = \
318
 
            FakeControlFilesAndTransport(user_id=user_id)
 
338
            FakeControlFilesAndTransport()
319
339
 
320
340
    def _get_config(self):
321
341
        return config.TransportConfig(self._transport, 'branch.conf')
329
349
 
330
350
class FakeControlFilesAndTransport(object):
331
351
 
332
 
    def __init__(self, user_id=None):
 
352
    def __init__(self):
333
353
        self.files = {}
334
 
        if user_id:
335
 
            self.files['email'] = user_id
336
354
        self._transport = self
337
355
 
338
 
    def get_utf8(self, filename):
339
 
        # from LockableFiles
340
 
        raise AssertionError("get_utf8 should no longer be used")
341
 
 
342
356
    def get(self, filename):
343
357
        # from Transport
344
358
        try:
481
495
 
482
496
    def test_signatures_default(self):
483
497
        my_config = config.Config()
484
 
        self.assertFalse(my_config.signature_needed())
 
498
        self.assertFalse(
 
499
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
500
                my_config.signature_needed))
485
501
        self.assertEqual(config.CHECK_IF_POSSIBLE,
486
 
                         my_config.signature_checking())
 
502
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
503
                my_config.signature_checking))
487
504
        self.assertEqual(config.SIGN_WHEN_REQUIRED,
488
 
                         my_config.signing_policy())
 
505
                self.applyDeprecated(deprecated_in((2, 5, 0)),
 
506
                    my_config.signing_policy))
489
507
 
490
508
    def test_signatures_template_method(self):
491
509
        my_config = InstrumentedConfig()
492
 
        self.assertEqual(config.CHECK_NEVER, my_config.signature_checking())
 
510
        self.assertEqual(config.CHECK_NEVER,
 
511
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
512
                my_config.signature_checking))
493
513
        self.assertEqual(['_get_signature_checking'], my_config._calls)
494
514
 
495
515
    def test_signatures_template_method_none(self):
496
516
        my_config = InstrumentedConfig()
497
517
        my_config._signatures = None
498
518
        self.assertEqual(config.CHECK_IF_POSSIBLE,
499
 
                         my_config.signature_checking())
 
519
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
520
                             my_config.signature_checking))
500
521
        self.assertEqual(['_get_signature_checking'], my_config._calls)
501
522
 
502
523
    def test_gpg_signing_command_default(self):
503
524
        my_config = config.Config()
504
 
        self.assertEqual('gpg', my_config.gpg_signing_command())
 
525
        self.assertEqual('gpg',
 
526
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
527
                my_config.gpg_signing_command))
505
528
 
506
529
    def test_get_user_option_default(self):
507
530
        my_config = config.Config()
509
532
 
510
533
    def test_post_commit_default(self):
511
534
        my_config = config.Config()
512
 
        self.assertEqual(None, my_config.post_commit())
 
535
        self.assertEqual(None, self.applyDeprecated(deprecated_in((2, 5, 0)),
 
536
                                                    my_config.post_commit))
 
537
 
513
538
 
514
539
    def test_log_format_default(self):
515
540
        my_config = config.Config()
516
 
        self.assertEqual('long', my_config.log_format())
 
541
        self.assertEqual('long',
 
542
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
543
                                              my_config.log_format))
517
544
 
518
545
    def test_acceptable_keys_default(self):
519
546
        my_config = config.Config()
520
 
        self.assertEqual(None, my_config.acceptable_keys())
 
547
        self.assertEqual(None, self.applyDeprecated(deprecated_in((2, 5, 0)),
 
548
            my_config.acceptable_keys))
521
549
 
522
550
    def test_validate_signatures_in_log_default(self):
523
551
        my_config = config.Config()
549
577
    def test_config_dir(self):
550
578
        self.assertEqual(config.config_dir(), self.bzr_home)
551
579
 
 
580
    def test_config_dir_is_unicode(self):
 
581
        self.assertIsInstance(config.config_dir(), unicode)
 
582
 
552
583
    def test_config_filename(self):
553
584
        self.assertEqual(config.config_filename(),
554
585
                         self.bzr_home + '/bazaar.conf')
658
689
        self.assertFileEqual(content, 'test.conf')
659
690
 
660
691
 
661
 
class TestIniConfigOptionExpansionDefaultValue(tests.TestCaseInTempDir):
662
 
    """What is the default value of expand for config options.
663
 
 
664
 
    This is an opt-in beta feature used to evaluate whether or not option
665
 
    references can appear in dangerous place raising exceptions, disapearing
666
 
    (and as such corrupting data) or if it's safe to activate the option by
667
 
    default.
668
 
 
669
 
    Note that these tests relies on config._expand_default_value being already
670
 
    overwritten in the parent class setUp.
671
 
    """
672
 
 
673
 
    def setUp(self):
674
 
        super(TestIniConfigOptionExpansionDefaultValue, self).setUp()
675
 
        self.config = None
676
 
        self.warnings = []
677
 
        def warning(*args):
678
 
            self.warnings.append(args[0] % args[1:])
679
 
        self.overrideAttr(trace, 'warning', warning)
680
 
 
681
 
    def get_config(self, expand):
682
 
        c = config.GlobalConfig.from_string('bzr.config.expand=%s' % (expand,),
683
 
                                            save=True)
684
 
        return c
685
 
 
686
 
    def assertExpandIs(self, expected):
687
 
        actual = config._get_expand_default_value()
688
 
        #self.config.get_user_option_as_bool('bzr.config.expand')
689
 
        self.assertEquals(expected, actual)
690
 
 
691
 
    def test_default_is_None(self):
692
 
        self.assertEquals(None, config._expand_default_value)
693
 
 
694
 
    def test_default_is_False_even_if_None(self):
695
 
        self.config = self.get_config(None)
696
 
        self.assertExpandIs(False)
697
 
 
698
 
    def test_default_is_False_even_if_invalid(self):
699
 
        self.config = self.get_config('<your choice>')
700
 
        self.assertExpandIs(False)
701
 
        # ...
702
 
        # Huh ? My choice is False ? Thanks, always happy to hear that :D
703
 
        # Wait, you've been warned !
704
 
        self.assertLength(1, self.warnings)
705
 
        self.assertEquals(
706
 
            'Value "<your choice>" is not a boolean for "bzr.config.expand"',
707
 
            self.warnings[0])
708
 
 
709
 
    def test_default_is_True(self):
710
 
        self.config = self.get_config(True)
711
 
        self.assertExpandIs(True)
712
 
 
713
 
    def test_default_is_False(self):
714
 
        self.config = self.get_config(False)
715
 
        self.assertExpandIs(False)
716
 
 
717
 
 
718
692
class TestIniConfigOptionExpansion(tests.TestCase):
719
693
    """Test option expansion from the IniConfig level.
720
694
 
817
791
        self.assertEquals(['{foo', '}', '{', 'bar}'],
818
792
                          conf.get_user_option('hidden', expand=True))
819
793
 
 
794
 
820
795
class TestLocationConfigOptionExpansion(tests.TestCaseInTempDir):
821
796
 
822
797
    def get_config(self, location, string=None):
1033
1008
        # automatically cast to list
1034
1009
        self.assertEqual(['x'], get_list('one_item'))
1035
1010
 
 
1011
    def test_get_user_option_as_int_from_SI(self):
 
1012
        conf, parser = self.make_config_parser("""
 
1013
plain = 100
 
1014
si_k = 5k,
 
1015
si_kb = 5kb,
 
1016
si_m = 5M,
 
1017
si_mb = 5MB,
 
1018
si_g = 5g,
 
1019
si_gb = 5gB,
 
1020
""")
 
1021
        def get_si(s, default=None):
 
1022
            return self.applyDeprecated(
 
1023
                deprecated_in((2, 5, 0)),
 
1024
                conf.get_user_option_as_int_from_SI, s, default)
 
1025
        self.assertEqual(100, get_si('plain'))
 
1026
        self.assertEqual(5000, get_si('si_k'))
 
1027
        self.assertEqual(5000, get_si('si_kb'))
 
1028
        self.assertEqual(5000000, get_si('si_m'))
 
1029
        self.assertEqual(5000000, get_si('si_mb'))
 
1030
        self.assertEqual(5000000000, get_si('si_g'))
 
1031
        self.assertEqual(5000000000, get_si('si_gb'))
 
1032
        self.assertEqual(None, get_si('non-exist'))
 
1033
        self.assertEqual(42, get_si('non-exist-with-default',  42))
 
1034
 
1036
1035
 
1037
1036
class TestSupressWarning(TestIniConfig):
1038
1037
 
1086
1085
 
1087
1086
    def test_get_config(self):
1088
1087
        """The Branch.get_config method works properly"""
1089
 
        b = bzrdir.BzrDir.create_standalone_workingtree('.').branch
 
1088
        b = controldir.ControlDir.create_standalone_workingtree('.').branch
1090
1089
        my_config = b.get_config()
1091
1090
        self.assertIs(my_config.get_user_option('wacky'), None)
1092
1091
        my_config.set_user_option('wacky', 'unlikely')
1141
1140
        b = self.make_branch('!repo')
1142
1141
        self.assertEqual('!repo', b.get_config().get_nickname())
1143
1142
 
 
1143
    def test_autonick_uses_branch_name(self):
 
1144
        b = self.make_branch('foo', name='bar')
 
1145
        self.assertEqual('bar', b.get_config().get_nickname())
 
1146
 
1144
1147
    def test_warn_if_masked(self):
1145
1148
        warnings = []
1146
1149
        def warning(*args):
1195
1198
    def test_signatures_always(self):
1196
1199
        my_config = config.GlobalConfig.from_string(sample_always_signatures)
1197
1200
        self.assertEqual(config.CHECK_NEVER,
1198
 
                         my_config.signature_checking())
 
1201
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1202
                             my_config.signature_checking))
1199
1203
        self.assertEqual(config.SIGN_ALWAYS,
1200
 
                         my_config.signing_policy())
1201
 
        self.assertEqual(True, my_config.signature_needed())
 
1204
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1205
                             my_config.signing_policy))
 
1206
        self.assertEqual(True,
 
1207
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1208
                my_config.signature_needed))
1202
1209
 
1203
1210
    def test_signatures_if_possible(self):
1204
1211
        my_config = config.GlobalConfig.from_string(sample_maybe_signatures)
1205
1212
        self.assertEqual(config.CHECK_NEVER,
1206
 
                         my_config.signature_checking())
 
1213
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1214
                             my_config.signature_checking))
1207
1215
        self.assertEqual(config.SIGN_WHEN_REQUIRED,
1208
 
                         my_config.signing_policy())
1209
 
        self.assertEqual(False, my_config.signature_needed())
 
1216
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1217
                             my_config.signing_policy))
 
1218
        self.assertEqual(False, self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1219
            my_config.signature_needed))
1210
1220
 
1211
1221
    def test_signatures_ignore(self):
1212
1222
        my_config = config.GlobalConfig.from_string(sample_ignore_signatures)
1213
1223
        self.assertEqual(config.CHECK_ALWAYS,
1214
 
                         my_config.signature_checking())
 
1224
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1225
                             my_config.signature_checking))
1215
1226
        self.assertEqual(config.SIGN_NEVER,
1216
 
                         my_config.signing_policy())
1217
 
        self.assertEqual(False, my_config.signature_needed())
 
1227
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1228
                             my_config.signing_policy))
 
1229
        self.assertEqual(False, self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1230
            my_config.signature_needed))
1218
1231
 
1219
1232
    def _get_sample_config(self):
1220
1233
        my_config = config.GlobalConfig.from_string(sample_config_text)
1222
1235
 
1223
1236
    def test_gpg_signing_command(self):
1224
1237
        my_config = self._get_sample_config()
1225
 
        self.assertEqual("gnome-gpg", my_config.gpg_signing_command())
1226
 
        self.assertEqual(False, my_config.signature_needed())
 
1238
        self.assertEqual("gnome-gpg",
 
1239
            self.applyDeprecated(
 
1240
                deprecated_in((2, 5, 0)), my_config.gpg_signing_command))
 
1241
        self.assertEqual(False, self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1242
            my_config.signature_needed))
 
1243
 
 
1244
    def test_gpg_signing_key(self):
 
1245
        my_config = self._get_sample_config()
 
1246
        self.assertEqual("DD4D5088",
 
1247
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1248
                my_config.gpg_signing_key))
1227
1249
 
1228
1250
    def _get_empty_config(self):
1229
1251
        my_config = config.GlobalConfig()
1231
1253
 
1232
1254
    def test_gpg_signing_command_unset(self):
1233
1255
        my_config = self._get_empty_config()
1234
 
        self.assertEqual("gpg", my_config.gpg_signing_command())
 
1256
        self.assertEqual("gpg",
 
1257
            self.applyDeprecated(
 
1258
                deprecated_in((2, 5, 0)), my_config.gpg_signing_command))
1235
1259
 
1236
1260
    def test_get_user_option_default(self):
1237
1261
        my_config = self._get_empty_config()
1244
1268
 
1245
1269
    def test_post_commit_default(self):
1246
1270
        my_config = self._get_sample_config()
1247
 
        self.assertEqual(None, my_config.post_commit())
 
1271
        self.assertEqual(None,
 
1272
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1273
                                              my_config.post_commit))
1248
1274
 
1249
1275
    def test_configured_logformat(self):
1250
1276
        my_config = self._get_sample_config()
1251
 
        self.assertEqual("short", my_config.log_format())
 
1277
        self.assertEqual("short",
 
1278
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1279
                                              my_config.log_format))
1252
1280
 
1253
1281
    def test_configured_acceptable_keys(self):
1254
1282
        my_config = self._get_sample_config()
1255
 
        self.assertEqual("amy", my_config.acceptable_keys())
 
1283
        self.assertEqual("amy",
 
1284
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1285
                my_config.acceptable_keys))
1256
1286
 
1257
1287
    def test_configured_validate_signatures_in_log(self):
1258
1288
        my_config = self._get_sample_config()
1296
1326
        self.log(repr(tools))
1297
1327
        self.assertEqual(
1298
1328
            {u'funkytool' : u'funkytool "arg with spaces" {this_temp}',
1299
 
            u'sometool' : u'sometool {base} {this} {other} -o {result}'},
 
1329
            u'sometool' : u'sometool {base} {this} {other} -o {result}',
 
1330
            u'newtool' : u'"newtool with spaces" {this_temp}'},
1300
1331
            tools)
1301
1332
 
1302
1333
    def test_get_merge_tools_empty(self):
1493
1524
        self.get_branch_config('http://www.example.com',
1494
1525
                                 global_config=sample_ignore_signatures)
1495
1526
        self.assertEqual(config.CHECK_ALWAYS,
1496
 
                         self.my_config.signature_checking())
 
1527
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1528
                             self.my_config.signature_checking))
1497
1529
        self.assertEqual(config.SIGN_NEVER,
1498
 
                         self.my_config.signing_policy())
 
1530
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1531
                             self.my_config.signing_policy))
1499
1532
 
1500
1533
    def test_signatures_never(self):
1501
1534
        self.get_branch_config('/a/c')
1502
1535
        self.assertEqual(config.CHECK_NEVER,
1503
 
                         self.my_config.signature_checking())
 
1536
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1537
                             self.my_config.signature_checking))
1504
1538
 
1505
1539
    def test_signatures_when_available(self):
1506
1540
        self.get_branch_config('/a/', global_config=sample_ignore_signatures)
1507
1541
        self.assertEqual(config.CHECK_IF_POSSIBLE,
1508
 
                         self.my_config.signature_checking())
 
1542
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1543
                             self.my_config.signature_checking))
1509
1544
 
1510
1545
    def test_signatures_always(self):
1511
1546
        self.get_branch_config('/b')
1512
1547
        self.assertEqual(config.CHECK_ALWAYS,
1513
 
                         self.my_config.signature_checking())
 
1548
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1549
                         self.my_config.signature_checking))
1514
1550
 
1515
1551
    def test_gpg_signing_command(self):
1516
1552
        self.get_branch_config('/b')
1517
 
        self.assertEqual("gnome-gpg", self.my_config.gpg_signing_command())
 
1553
        self.assertEqual("gnome-gpg",
 
1554
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1555
                self.my_config.gpg_signing_command))
1518
1556
 
1519
1557
    def test_gpg_signing_command_missing(self):
1520
1558
        self.get_branch_config('/a')
1521
 
        self.assertEqual("false", self.my_config.gpg_signing_command())
 
1559
        self.assertEqual("false",
 
1560
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1561
                self.my_config.gpg_signing_command))
 
1562
 
 
1563
    def test_gpg_signing_key(self):
 
1564
        self.get_branch_config('/b')
 
1565
        self.assertEqual("DD4D5088", self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1566
            self.my_config.gpg_signing_key))
 
1567
 
 
1568
    def test_gpg_signing_key_default(self):
 
1569
        self.get_branch_config('/a')
 
1570
        self.assertEqual("erik@bagfors.nu",
 
1571
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1572
                self.my_config.gpg_signing_key))
1522
1573
 
1523
1574
    def test_get_user_option_global(self):
1524
1575
        self.get_branch_config('/a')
1612
1663
    def test_post_commit_default(self):
1613
1664
        self.get_branch_config('/a/c')
1614
1665
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1615
 
                         self.my_config.post_commit())
 
1666
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1667
                                              self.my_config.post_commit))
1616
1668
 
1617
1669
    def get_branch_config(self, location, global_config=None,
1618
1670
                          location_config=None):
1708
1760
        return my_config
1709
1761
 
1710
1762
    def test_user_id(self):
1711
 
        branch = FakeBranch(user_id='Robert Collins <robertc@example.net>')
 
1763
        branch = FakeBranch()
1712
1764
        my_config = config.BranchConfig(branch)
1713
 
        self.assertEqual("Robert Collins <robertc@example.net>",
1714
 
                         my_config.username())
 
1765
        self.assertIsNot(None, my_config.username())
1715
1766
        my_config.branch.control_files.files['email'] = "John"
1716
1767
        my_config.set_user_option('email',
1717
1768
                                  "Robert Collins <robertc@example.org>")
1718
 
        self.assertEqual("John", my_config.username())
1719
 
        del my_config.branch.control_files.files['email']
1720
1769
        self.assertEqual("Robert Collins <robertc@example.org>",
1721
 
                         my_config.username())
1722
 
 
1723
 
    def test_not_set_in_branch(self):
1724
 
        my_config = self.get_branch_config(global_config=sample_config_text)
1725
 
        self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
1726
 
                         my_config._get_user_id())
1727
 
        my_config.branch.control_files.files['email'] = "John"
1728
 
        self.assertEqual("John", my_config._get_user_id())
 
1770
                        my_config.username())
1729
1771
 
1730
1772
    def test_BZR_EMAIL_OVERRIDES(self):
1731
1773
        self.overrideEnv('BZR_EMAIL', "Robert Collins <robertc@example.org>")
1737
1779
    def test_signatures_forced(self):
1738
1780
        my_config = self.get_branch_config(
1739
1781
            global_config=sample_always_signatures)
1740
 
        self.assertEqual(config.CHECK_NEVER, my_config.signature_checking())
1741
 
        self.assertEqual(config.SIGN_ALWAYS, my_config.signing_policy())
1742
 
        self.assertTrue(my_config.signature_needed())
 
1782
        self.assertEqual(config.CHECK_NEVER,
 
1783
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1784
                my_config.signature_checking))
 
1785
        self.assertEqual(config.SIGN_ALWAYS,
 
1786
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1787
                my_config.signing_policy))
 
1788
        self.assertTrue(self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1789
            my_config.signature_needed))
1743
1790
 
1744
1791
    def test_signatures_forced_branch(self):
1745
1792
        my_config = self.get_branch_config(
1746
1793
            global_config=sample_ignore_signatures,
1747
1794
            branch_data_config=sample_always_signatures)
1748
 
        self.assertEqual(config.CHECK_NEVER, my_config.signature_checking())
1749
 
        self.assertEqual(config.SIGN_ALWAYS, my_config.signing_policy())
1750
 
        self.assertTrue(my_config.signature_needed())
 
1795
        self.assertEqual(config.CHECK_NEVER,
 
1796
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1797
                my_config.signature_checking))
 
1798
        self.assertEqual(config.SIGN_ALWAYS,
 
1799
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1800
                my_config.signing_policy))
 
1801
        self.assertTrue(self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1802
            my_config.signature_needed))
1751
1803
 
1752
1804
    def test_gpg_signing_command(self):
1753
1805
        my_config = self.get_branch_config(
1754
1806
            global_config=sample_config_text,
1755
1807
            # branch data cannot set gpg_signing_command
1756
1808
            branch_data_config="gpg_signing_command=pgp")
1757
 
        self.assertEqual('gnome-gpg', my_config.gpg_signing_command())
 
1809
        self.assertEqual('gnome-gpg',
 
1810
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1811
                my_config.gpg_signing_command))
1758
1812
 
1759
1813
    def test_get_user_option_global(self):
1760
1814
        my_config = self.get_branch_config(global_config=sample_config_text)
1767
1821
                                      location_config=sample_branches_text)
1768
1822
        self.assertEqual(my_config.branch.base, '/a/c')
1769
1823
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1770
 
                         my_config.post_commit())
 
1824
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1825
                                              my_config.post_commit))
1771
1826
        my_config.set_user_option('post_commit', 'rmtree_root')
1772
1827
        # post-commit is ignored when present in branch data
1773
1828
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1774
 
                         my_config.post_commit())
 
1829
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1830
                                              my_config.post_commit))
1775
1831
        my_config.set_user_option('post_commit', 'rmtree_root',
1776
1832
                                  store=config.STORE_LOCATION)
1777
 
        self.assertEqual('rmtree_root', my_config.post_commit())
 
1833
        self.assertEqual('rmtree_root',
 
1834
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1835
                                              my_config.post_commit))
1778
1836
 
1779
1837
    def test_config_precedence(self):
1780
1838
        # FIXME: eager test, luckily no persitent config file makes it fail
1796
1854
            location='http://example.com/specific')
1797
1855
        self.assertEqual(my_config.get_user_option('option'), 'exact')
1798
1856
 
1799
 
    def test_get_mail_client(self):
1800
 
        config = self.get_branch_config()
1801
 
        client = config.get_mail_client()
1802
 
        self.assertIsInstance(client, mail_client.DefaultMail)
1803
 
 
1804
 
        # Specific clients
1805
 
        config.set_user_option('mail_client', 'evolution')
1806
 
        client = config.get_mail_client()
1807
 
        self.assertIsInstance(client, mail_client.Evolution)
1808
 
 
1809
 
        config.set_user_option('mail_client', 'kmail')
1810
 
        client = config.get_mail_client()
1811
 
        self.assertIsInstance(client, mail_client.KMail)
1812
 
 
1813
 
        config.set_user_option('mail_client', 'mutt')
1814
 
        client = config.get_mail_client()
1815
 
        self.assertIsInstance(client, mail_client.Mutt)
1816
 
 
1817
 
        config.set_user_option('mail_client', 'thunderbird')
1818
 
        client = config.get_mail_client()
1819
 
        self.assertIsInstance(client, mail_client.Thunderbird)
1820
 
 
1821
 
        # Generic options
1822
 
        config.set_user_option('mail_client', 'default')
1823
 
        client = config.get_mail_client()
1824
 
        self.assertIsInstance(client, mail_client.DefaultMail)
1825
 
 
1826
 
        config.set_user_option('mail_client', 'editor')
1827
 
        client = config.get_mail_client()
1828
 
        self.assertIsInstance(client, mail_client.Editor)
1829
 
 
1830
 
        config.set_user_option('mail_client', 'mapi')
1831
 
        client = config.get_mail_client()
1832
 
        self.assertIsInstance(client, mail_client.MAPIClient)
1833
 
 
1834
 
        config.set_user_option('mail_client', 'xdg-email')
1835
 
        client = config.get_mail_client()
1836
 
        self.assertIsInstance(client, mail_client.XDGEmail)
1837
 
 
1838
 
        config.set_user_option('mail_client', 'firebird')
1839
 
        self.assertRaises(errors.UnknownMailClient, config.get_mail_client)
1840
 
 
1841
1857
 
1842
1858
class TestMailAddressExtraction(tests.TestCase):
1843
1859
 
1914
1930
        conf = config.TransportConfig(t, 'foo.conf')
1915
1931
        self.assertRaises(errors.ParseConfigError, conf._get_configobj)
1916
1932
 
 
1933
    def test_load_permission_denied(self):
 
1934
        """Ensure we get an empty config file if the file is inaccessible."""
 
1935
        warnings = []
 
1936
        def warning(*args):
 
1937
            warnings.append(args[0] % args[1:])
 
1938
        self.overrideAttr(trace, 'warning', warning)
 
1939
 
 
1940
        class DenyingTransport(object):
 
1941
 
 
1942
            def __init__(self, base):
 
1943
                self.base = base
 
1944
 
 
1945
            def get_bytes(self, relpath):
 
1946
                raise errors.PermissionDenied(relpath, "")
 
1947
 
 
1948
        cfg = config.TransportConfig(
 
1949
            DenyingTransport("nonexisting://"), 'control.conf')
 
1950
        self.assertIs(None, cfg.get_option('non-existant', 'SECTION'))
 
1951
        self.assertEquals(
 
1952
            warnings,
 
1953
            [u'Permission denied while trying to open configuration file '
 
1954
             u'nonexisting:///control.conf.'])
 
1955
 
1917
1956
    def test_get_value(self):
1918
1957
        """Test that retreiving a value from a section is possible"""
1919
1958
        bzrdir_config = config.TransportConfig(self.get_transport('.'),
2107
2146
        self.assertGetHook(remote_branch._get_config(), 'file', 'branch')
2108
2147
 
2109
2148
    def test_get_hook_remote_bzrdir(self):
2110
 
        remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
 
2149
        remote_bzrdir = controldir.ControlDir.open(self.get_url('tree'))
2111
2150
        conf = remote_bzrdir._get_config()
2112
2151
        conf.set_option('remotedir', 'file')
2113
2152
        self.assertGetHook(conf, 'file', 'remotedir')
2135
2174
    def test_set_hook_remote_bzrdir(self):
2136
2175
        remote_branch = branch.Branch.open(self.get_url('tree'))
2137
2176
        self.addCleanup(remote_branch.lock_write().unlock)
2138
 
        remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
 
2177
        remote_bzrdir = controldir.ControlDir.open(self.get_url('tree'))
2139
2178
        self.assertSetHook(remote_bzrdir._get_config(), 'file', 'remotedir')
2140
2179
 
2141
2180
    def assertLoadHook(self, expected_nb_calls, name, conf_class, *conf_args):
2158
2197
        self.assertLoadHook(1, 'file', remote.RemoteBranchConfig, remote_branch)
2159
2198
 
2160
2199
    def test_load_hook_remote_bzrdir(self):
2161
 
        remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
 
2200
        remote_bzrdir = controldir.ControlDir.open(self.get_url('tree'))
2162
2201
        # The config file doesn't exist, set an option to force its creation
2163
2202
        conf = remote_bzrdir._get_config()
2164
2203
        conf.set_option('remotedir', 'file')
2189
2228
    def test_save_hook_remote_bzrdir(self):
2190
2229
        remote_branch = branch.Branch.open(self.get_url('tree'))
2191
2230
        self.addCleanup(remote_branch.lock_write().unlock)
2192
 
        remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
 
2231
        remote_bzrdir = controldir.ControlDir.open(self.get_url('tree'))
2193
2232
        self.assertSaveHook(remote_bzrdir._get_config())
2194
2233
 
2195
2234
 
2199
2238
        opt = config.Option('foo', default='bar')
2200
2239
        self.assertEquals('bar', opt.get_default())
2201
2240
 
 
2241
    def test_callable_default_value(self):
 
2242
        def bar_as_unicode():
 
2243
            return u'bar'
 
2244
        opt = config.Option('foo', default=bar_as_unicode)
 
2245
        self.assertEquals('bar', opt.get_default())
 
2246
 
 
2247
    def test_default_value_from_env(self):
 
2248
        opt = config.Option('foo', default='bar', default_from_env=['FOO'])
 
2249
        self.overrideEnv('FOO', 'quux')
 
2250
        # Env variable provides a default taking over the option one
 
2251
        self.assertEquals('quux', opt.get_default())
 
2252
 
 
2253
    def test_first_default_value_from_env_wins(self):
 
2254
        opt = config.Option('foo', default='bar',
 
2255
                            default_from_env=['NO_VALUE', 'FOO', 'BAZ'])
 
2256
        self.overrideEnv('FOO', 'foo')
 
2257
        self.overrideEnv('BAZ', 'baz')
 
2258
        # The first env var set wins
 
2259
        self.assertEquals('foo', opt.get_default())
 
2260
 
 
2261
    def test_not_supported_list_default_value(self):
 
2262
        self.assertRaises(AssertionError, config.Option, 'foo', default=[1])
 
2263
 
 
2264
    def test_not_supported_object_default_value(self):
 
2265
        self.assertRaises(AssertionError, config.Option, 'foo',
 
2266
                          default=object())
 
2267
 
 
2268
    def test_not_supported_callable_default_value_not_unicode(self):
 
2269
        def bar_not_unicode():
 
2270
            return 'bar'
 
2271
        opt = config.Option('foo', default=bar_not_unicode)
 
2272
        self.assertRaises(AssertionError, opt.get_default)
 
2273
 
 
2274
    def test_get_help_topic(self):
 
2275
        opt = config.Option('foo')
 
2276
        self.assertEquals('foo', opt.get_help_topic())
 
2277
 
 
2278
 
 
2279
class TestOptionConverterMixin(object):
 
2280
 
 
2281
    def assertConverted(self, expected, opt, value):
 
2282
        self.assertEquals(expected, opt.convert_from_unicode(None, value))
 
2283
 
 
2284
    def assertWarns(self, opt, value):
 
2285
        warnings = []
 
2286
        def warning(*args):
 
2287
            warnings.append(args[0] % args[1:])
 
2288
        self.overrideAttr(trace, 'warning', warning)
 
2289
        self.assertEquals(None, opt.convert_from_unicode(None, value))
 
2290
        self.assertLength(1, warnings)
 
2291
        self.assertEquals(
 
2292
            'Value "%s" is not valid for "%s"' % (value, opt.name),
 
2293
            warnings[0])
 
2294
 
 
2295
    def assertErrors(self, opt, value):
 
2296
        self.assertRaises(errors.ConfigOptionValueError,
 
2297
                          opt.convert_from_unicode, None, value)
 
2298
 
 
2299
    def assertConvertInvalid(self, opt, invalid_value):
 
2300
        opt.invalid = None
 
2301
        self.assertEquals(None, opt.convert_from_unicode(None, invalid_value))
 
2302
        opt.invalid = 'warning'
 
2303
        self.assertWarns(opt, invalid_value)
 
2304
        opt.invalid = 'error'
 
2305
        self.assertErrors(opt, invalid_value)
 
2306
 
 
2307
 
 
2308
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
 
2309
 
 
2310
    def get_option(self):
 
2311
        return config.Option('foo', help='A boolean.',
 
2312
                             from_unicode=config.bool_from_store)
 
2313
 
 
2314
    def test_convert_invalid(self):
 
2315
        opt = self.get_option()
 
2316
        # A string that is not recognized as a boolean
 
2317
        self.assertConvertInvalid(opt, u'invalid-boolean')
 
2318
        # A list of strings is never recognized as a boolean
 
2319
        self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
 
2320
 
 
2321
    def test_convert_valid(self):
 
2322
        opt = self.get_option()
 
2323
        self.assertConverted(True, opt, u'True')
 
2324
        self.assertConverted(True, opt, u'1')
 
2325
        self.assertConverted(False, opt, u'False')
 
2326
 
 
2327
 
 
2328
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
 
2329
 
 
2330
    def get_option(self):
 
2331
        return config.Option('foo', help='An integer.',
 
2332
                             from_unicode=config.int_from_store)
 
2333
 
 
2334
    def test_convert_invalid(self):
 
2335
        opt = self.get_option()
 
2336
        # A string that is not recognized as an integer
 
2337
        self.assertConvertInvalid(opt, u'forty-two')
 
2338
        # A list of strings is never recognized as an integer
 
2339
        self.assertConvertInvalid(opt, [u'a', u'list'])
 
2340
 
 
2341
    def test_convert_valid(self):
 
2342
        opt = self.get_option()
 
2343
        self.assertConverted(16, opt, u'16')
 
2344
 
 
2345
 
 
2346
class TestOptionWithSIUnitConverter(tests.TestCase, TestOptionConverterMixin):
 
2347
 
 
2348
    def get_option(self):
 
2349
        return config.Option('foo', help='An integer in SI units.',
 
2350
                             from_unicode=config.int_SI_from_store)
 
2351
 
 
2352
    def test_convert_invalid(self):
 
2353
        opt = self.get_option()
 
2354
        self.assertConvertInvalid(opt, u'not-a-unit')
 
2355
        self.assertConvertInvalid(opt, u'Gb') # Forgot the int
 
2356
        self.assertConvertInvalid(opt, u'1b') # Forgot the unit
 
2357
        self.assertConvertInvalid(opt, u'1GG')
 
2358
        self.assertConvertInvalid(opt, u'1Mbb')
 
2359
        self.assertConvertInvalid(opt, u'1MM')
 
2360
 
 
2361
    def test_convert_valid(self):
 
2362
        opt = self.get_option()
 
2363
        self.assertConverted(int(5e3), opt, u'5kb')
 
2364
        self.assertConverted(int(5e6), opt, u'5M')
 
2365
        self.assertConverted(int(5e6), opt, u'5MB')
 
2366
        self.assertConverted(int(5e9), opt, u'5g')
 
2367
        self.assertConverted(int(5e9), opt, u'5gB')
 
2368
        self.assertConverted(100, opt, u'100')
 
2369
 
 
2370
 
 
2371
class TestListOption(tests.TestCase, TestOptionConverterMixin):
 
2372
 
 
2373
    def get_option(self):
 
2374
        return config.ListOption('foo', help='A list.')
 
2375
 
 
2376
    def test_convert_invalid(self):
 
2377
        opt = self.get_option()
 
2378
        # We don't even try to convert a list into a list, we only expect
 
2379
        # strings
 
2380
        self.assertConvertInvalid(opt, [1])
 
2381
        # No string is invalid as all forms can be converted to a list
 
2382
 
 
2383
    def test_convert_valid(self):
 
2384
        opt = self.get_option()
 
2385
        # An empty string is an empty list
 
2386
        self.assertConverted([], opt, '') # Using a bare str() just in case
 
2387
        self.assertConverted([], opt, u'')
 
2388
        # A boolean
 
2389
        self.assertConverted([u'True'], opt, u'True')
 
2390
        # An integer
 
2391
        self.assertConverted([u'42'], opt, u'42')
 
2392
        # A single string
 
2393
        self.assertConverted([u'bar'], opt, u'bar')
 
2394
 
 
2395
 
 
2396
class TestRegistryOption(tests.TestCase, TestOptionConverterMixin):
 
2397
 
 
2398
    def get_option(self, registry):
 
2399
        return config.RegistryOption('foo', registry,
 
2400
                help='A registry option.')
 
2401
 
 
2402
    def test_convert_invalid(self):
 
2403
        registry = _mod_registry.Registry()
 
2404
        opt = self.get_option(registry)
 
2405
        self.assertConvertInvalid(opt, [1])
 
2406
        self.assertConvertInvalid(opt, u"notregistered")
 
2407
 
 
2408
    def test_convert_valid(self):
 
2409
        registry = _mod_registry.Registry()
 
2410
        registry.register("someval", 1234)
 
2411
        opt = self.get_option(registry)
 
2412
        # Using a bare str() just in case
 
2413
        self.assertConverted(1234, opt, "someval")
 
2414
        self.assertConverted(1234, opt, u'someval')
 
2415
        self.assertConverted(None, opt, None)
 
2416
 
 
2417
    def test_help(self):
 
2418
        registry = _mod_registry.Registry()
 
2419
        registry.register("someval", 1234, help="some option")
 
2420
        registry.register("dunno", 1234, help="some other option")
 
2421
        opt = self.get_option(registry)
 
2422
        self.assertEquals(
 
2423
            'A registry option.\n'
 
2424
            '\n'
 
2425
            'The following values are supported:\n'
 
2426
            ' dunno - some other option\n'
 
2427
            ' someval - some option\n',
 
2428
            opt.help)
 
2429
 
 
2430
    def test_get_help_text(self):
 
2431
        registry = _mod_registry.Registry()
 
2432
        registry.register("someval", 1234, help="some option")
 
2433
        registry.register("dunno", 1234, help="some other option")
 
2434
        opt = self.get_option(registry)
 
2435
        self.assertEquals(
 
2436
            'A registry option.\n'
 
2437
            '\n'
 
2438
            'The following values are supported:\n'
 
2439
            ' dunno - some other option\n'
 
2440
            ' someval - some option\n',
 
2441
            opt.get_help_text())
 
2442
 
2202
2443
 
2203
2444
class TestOptionRegistry(tests.TestCase):
2204
2445
 
2205
2446
    def setUp(self):
2206
2447
        super(TestOptionRegistry, self).setUp()
2207
2448
        # Always start with an empty registry
2208
 
        self.overrideAttr(config, 'option_registry', registry.Registry())
 
2449
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2209
2450
        self.registry = config.option_registry
2210
2451
 
2211
2452
    def test_register(self):
2212
2453
        opt = config.Option('foo')
2213
 
        self.registry.register('foo', opt)
 
2454
        self.registry.register(opt)
2214
2455
        self.assertIs(opt, self.registry.get('foo'))
2215
2456
 
2216
 
    lazy_option = config.Option('lazy_foo')
2217
 
 
2218
 
    def test_register_lazy(self):
2219
 
        self.registry.register_lazy('foo', self.__module__,
2220
 
                                    'TestOptionRegistry.lazy_option')
2221
 
        self.assertIs(self.lazy_option, self.registry.get('foo'))
2222
 
 
2223
2457
    def test_registered_help(self):
2224
 
        opt = config.Option('foo')
2225
 
        self.registry.register('foo', opt, help='A simple option')
 
2458
        opt = config.Option('foo', help='A simple option')
 
2459
        self.registry.register(opt)
2226
2460
        self.assertEquals('A simple option', self.registry.get_help('foo'))
2227
2461
 
 
2462
    lazy_option = config.Option('lazy_foo', help='Lazy help')
 
2463
 
 
2464
    def test_register_lazy(self):
 
2465
        self.registry.register_lazy('lazy_foo', self.__module__,
 
2466
                                    'TestOptionRegistry.lazy_option')
 
2467
        self.assertIs(self.lazy_option, self.registry.get('lazy_foo'))
 
2468
 
 
2469
    def test_registered_lazy_help(self):
 
2470
        self.registry.register_lazy('lazy_foo', self.__module__,
 
2471
                                    'TestOptionRegistry.lazy_option')
 
2472
        self.assertEquals('Lazy help', self.registry.get_help('lazy_foo'))
 
2473
 
2228
2474
 
2229
2475
class TestRegisteredOptions(tests.TestCase):
2230
2476
    """All registered options should verify some constraints."""
2244
2490
    def test_help_is_set(self):
2245
2491
        option_help = self.registry.get_help(self.option_name)
2246
2492
        self.assertNotEquals(None, option_help)
2247
 
        # Come on, think about the user, he really wants to know whst the
 
2493
        # Come on, think about the user, he really wants to know what the
2248
2494
        # option is about
 
2495
        self.assertIsNot(None, option_help)
2249
2496
        self.assertNotEquals('', option_help)
2250
2497
 
2251
2498
 
2273
2520
 
2274
2521
class TestMutableSection(tests.TestCase):
2275
2522
 
2276
 
    # FIXME: Parametrize so that all sections (including os.environ and the
2277
 
    # ones produced by Stores) run these tests -- vila 2011-04-01
 
2523
    scenarios = [('mutable',
 
2524
                  {'get_section':
 
2525
                       lambda opts: config.MutableSection('myID', opts)},),
 
2526
        ]
2278
2527
 
2279
2528
    def test_set(self):
2280
2529
        a_dict = dict(foo='bar')
2281
 
        section = config.MutableSection('myID', a_dict)
 
2530
        section = self.get_section(a_dict)
2282
2531
        section.set('foo', 'new_value')
2283
2532
        self.assertEquals('new_value', section.get('foo'))
2284
2533
        # The change appears in the shared section
2289
2538
 
2290
2539
    def test_set_preserve_original_once(self):
2291
2540
        a_dict = dict(foo='bar')
2292
 
        section = config.MutableSection('myID', a_dict)
 
2541
        section = self.get_section(a_dict)
2293
2542
        section.set('foo', 'first_value')
2294
2543
        section.set('foo', 'second_value')
2295
2544
        # We keep track of the original value
2298
2547
 
2299
2548
    def test_remove(self):
2300
2549
        a_dict = dict(foo='bar')
2301
 
        section = config.MutableSection('myID', a_dict)
 
2550
        section = self.get_section(a_dict)
2302
2551
        section.remove('foo')
2303
2552
        # We get None for unknown options via the default value
2304
2553
        self.assertEquals(None, section.get('foo'))
2311
2560
 
2312
2561
    def test_remove_new_option(self):
2313
2562
        a_dict = dict()
2314
 
        section = config.MutableSection('myID', a_dict)
 
2563
        section = self.get_section(a_dict)
2315
2564
        section.set('foo', 'bar')
2316
2565
        section.remove('foo')
2317
2566
        self.assertFalse('foo' in section.options)
2321
2570
        self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
2322
2571
 
2323
2572
 
 
2573
class TestCommandLineStore(tests.TestCase):
 
2574
 
 
2575
    def setUp(self):
 
2576
        super(TestCommandLineStore, self).setUp()
 
2577
        self.store = config.CommandLineStore()
 
2578
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
 
2579
 
 
2580
    def get_section(self):
 
2581
        """Get the unique section for the command line overrides."""
 
2582
        sections = list(self.store.get_sections())
 
2583
        self.assertLength(1, sections)
 
2584
        store, section = sections[0]
 
2585
        self.assertEquals(self.store, store)
 
2586
        return section
 
2587
 
 
2588
    def test_no_override(self):
 
2589
        self.store._from_cmdline([])
 
2590
        section = self.get_section()
 
2591
        self.assertLength(0, list(section.iter_option_names()))
 
2592
 
 
2593
    def test_simple_override(self):
 
2594
        self.store._from_cmdline(['a=b'])
 
2595
        section = self.get_section()
 
2596
        self.assertEqual('b', section.get('a'))
 
2597
 
 
2598
    def test_list_override(self):
 
2599
        opt = config.ListOption('l')
 
2600
        config.option_registry.register(opt)
 
2601
        self.store._from_cmdline(['l=1,2,3'])
 
2602
        val = self.get_section().get('l')
 
2603
        self.assertEqual('1,2,3', val)
 
2604
        # Reminder: lists should be registered as such explicitely, otherwise
 
2605
        # the conversion needs to be done afterwards.
 
2606
        self.assertEqual(['1', '2', '3'],
 
2607
                         opt.convert_from_unicode(self.store, val))
 
2608
 
 
2609
    def test_multiple_overrides(self):
 
2610
        self.store._from_cmdline(['a=b', 'x=y'])
 
2611
        section = self.get_section()
 
2612
        self.assertEquals('b', section.get('a'))
 
2613
        self.assertEquals('y', section.get('x'))
 
2614
 
 
2615
    def test_wrong_syntax(self):
 
2616
        self.assertRaises(errors.BzrCommandError,
 
2617
                          self.store._from_cmdline, ['a=b', 'c'])
 
2618
 
 
2619
class TestStoreMinimalAPI(tests.TestCaseWithTransport):
 
2620
 
 
2621
    scenarios = [(key, {'get_store': builder}) for key, builder
 
2622
                 in config.test_store_builder_registry.iteritems()] + [
 
2623
        ('cmdline', {'get_store': lambda test: config.CommandLineStore()})]
 
2624
 
 
2625
    def test_id(self):
 
2626
        store = self.get_store(self)
 
2627
        if type(store) == config.TransportIniFileStore:
 
2628
            raise tests.TestNotApplicable(
 
2629
                "%s is not a concrete Store implementation"
 
2630
                " so it doesn't need an id" % (store.__class__.__name__,))
 
2631
        self.assertIsNot(None, store.id)
 
2632
 
 
2633
 
2324
2634
class TestStore(tests.TestCaseWithTransport):
2325
2635
 
2326
 
    def assertSectionContent(self, expected, section):
 
2636
    def assertSectionContent(self, expected, (store, section)):
2327
2637
        """Assert that some options have the proper values in a section."""
2328
2638
        expected_name, expected_options = expected
2329
2639
        self.assertEquals(expected_name, section.id)
2337
2647
    scenarios = [(key, {'get_store': builder}) for key, builder
2338
2648
                 in config.test_store_builder_registry.iteritems()]
2339
2649
 
2340
 
    def setUp(self):
2341
 
        super(TestReadonlyStore, self).setUp()
2342
 
 
2343
2650
    def test_building_delays_load(self):
2344
2651
        store = self.get_store(self)
2345
2652
        self.assertEquals(False, store.is_loaded())
2371
2678
        self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2372
2679
 
2373
2680
 
 
2681
class TestStoreQuoting(TestStore):
 
2682
 
 
2683
    scenarios = [(key, {'get_store': builder}) for key, builder
 
2684
                 in config.test_store_builder_registry.iteritems()]
 
2685
 
 
2686
    def setUp(self):
 
2687
        super(TestStoreQuoting, self).setUp()
 
2688
        self.store = self.get_store(self)
 
2689
        # We need a loaded store but any content will do
 
2690
        self.store._load_from_string('')
 
2691
 
 
2692
    def assertIdempotent(self, s):
 
2693
        """Assert that quoting an unquoted string is a no-op and vice-versa.
 
2694
 
 
2695
        What matters here is that option values, as they appear in a store, can
 
2696
        be safely round-tripped out of the store and back.
 
2697
 
 
2698
        :param s: A string, quoted if required.
 
2699
        """
 
2700
        self.assertEquals(s, self.store.quote(self.store.unquote(s)))
 
2701
        self.assertEquals(s, self.store.unquote(self.store.quote(s)))
 
2702
 
 
2703
    def test_empty_string(self):
 
2704
        if isinstance(self.store, config.IniFileStore):
 
2705
            # configobj._quote doesn't handle empty values
 
2706
            self.assertRaises(AssertionError,
 
2707
                              self.assertIdempotent, '')
 
2708
        else:
 
2709
            self.assertIdempotent('')
 
2710
        # But quoted empty strings are ok
 
2711
        self.assertIdempotent('""')
 
2712
 
 
2713
    def test_embedded_spaces(self):
 
2714
        self.assertIdempotent('" a b c "')
 
2715
 
 
2716
    def test_embedded_commas(self):
 
2717
        self.assertIdempotent('" a , b c "')
 
2718
 
 
2719
    def test_simple_comma(self):
 
2720
        if isinstance(self.store, config.IniFileStore):
 
2721
            # configobj requires that lists are special-cased
 
2722
           self.assertRaises(AssertionError,
 
2723
                             self.assertIdempotent, ',')
 
2724
        else:
 
2725
            self.assertIdempotent(',')
 
2726
        # When a single comma is required, quoting is also required
 
2727
        self.assertIdempotent('","')
 
2728
 
 
2729
    def test_list(self):
 
2730
        if isinstance(self.store, config.IniFileStore):
 
2731
            # configobj requires that lists are special-cased
 
2732
            self.assertRaises(AssertionError,
 
2733
                              self.assertIdempotent, 'a,b')
 
2734
        else:
 
2735
            self.assertIdempotent('a,b')
 
2736
 
 
2737
 
 
2738
class TestDictFromStore(tests.TestCase):
 
2739
 
 
2740
    def test_unquote_not_string(self):
 
2741
        conf = config.MemoryStack('x=2\n[a_section]\na=1\n')
 
2742
        value = conf.get('a_section')
 
2743
        # Urgh, despite 'conf' asking for the no-name section, we get the
 
2744
        # content of another section as a dict o_O
 
2745
        self.assertEquals({'a': '1'}, value)
 
2746
        unquoted = conf.store.unquote(value)
 
2747
        # Which cannot be unquoted but shouldn't crash either (the use cases
 
2748
        # are getting the value or displaying it. In the later case, '%s' will
 
2749
        # do).
 
2750
        self.assertEquals({'a': '1'}, unquoted)
 
2751
        self.assertEquals("{u'a': u'1'}", '%s' % (unquoted,))
 
2752
 
 
2753
 
2374
2754
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2375
 
    """Simulate loading a config store without content of various encodings.
 
2755
    """Simulate loading a config store with content of various encodings.
2376
2756
 
2377
2757
    All files produced by bzr are in utf8 content.
2378
2758
 
2391
2771
        utf8_content = unicode_content.encode('utf8')
2392
2772
        # Store the raw content in the config file
2393
2773
        t.put_bytes('foo.conf', utf8_content)
2394
 
        store = config.IniFileStore(t, 'foo.conf')
 
2774
        store = config.TransportIniFileStore(t, 'foo.conf')
2395
2775
        store.load()
2396
2776
        stack = config.Stack([store.get_sections], store)
2397
2777
        self.assertEquals(unicode_user, stack.get('user'))
2400
2780
        """Ensure we display a proper error on non-ascii, non utf-8 content."""
2401
2781
        t = self.get_transport()
2402
2782
        t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2403
 
        store = config.IniFileStore(t, 'foo.conf')
 
2783
        store = config.TransportIniFileStore(t, 'foo.conf')
2404
2784
        self.assertRaises(errors.ConfigContentError, store.load)
2405
2785
 
2406
2786
    def test_load_erroneous_content(self):
2407
2787
        """Ensure we display a proper error on content that can't be parsed."""
2408
2788
        t = self.get_transport()
2409
2789
        t.put_bytes('foo.conf', '[open_section\n')
2410
 
        store = config.IniFileStore(t, 'foo.conf')
 
2790
        store = config.TransportIniFileStore(t, 'foo.conf')
2411
2791
        self.assertRaises(errors.ParseConfigError, store.load)
2412
2792
 
 
2793
    def test_load_permission_denied(self):
 
2794
        """Ensure we get warned when trying to load an inaccessible file."""
 
2795
        warnings = []
 
2796
        def warning(*args):
 
2797
            warnings.append(args[0] % args[1:])
 
2798
        self.overrideAttr(trace, 'warning', warning)
 
2799
 
 
2800
        t = self.get_transport()
 
2801
 
 
2802
        def get_bytes(relpath):
 
2803
            raise errors.PermissionDenied(relpath, "")
 
2804
        t.get_bytes = get_bytes
 
2805
        store = config.TransportIniFileStore(t, 'foo.conf')
 
2806
        self.assertRaises(errors.PermissionDenied, store.load)
 
2807
        self.assertEquals(
 
2808
            warnings,
 
2809
            [u'Permission denied while trying to load configuration store %s.'
 
2810
             % store.external_url()])
 
2811
 
2413
2812
 
2414
2813
class TestIniConfigContent(tests.TestCaseWithTransport):
2415
 
    """Simulate loading a IniBasedConfig without content of various encodings.
 
2814
    """Simulate loading a IniBasedConfig with content of various encodings.
2416
2815
 
2417
2816
    All files produced by bzr are in utf8 content.
2418
2817
 
2476
2875
    def test_save_emptied_succeeds(self):
2477
2876
        store = self.get_store(self)
2478
2877
        store._load_from_string('foo=bar\n')
 
2878
        # FIXME: There should be a better way than relying on the test
 
2879
        # parametrization to identify branch.conf -- vila 2011-0526
 
2880
        if self.store_id in ('branch', 'remote_branch'):
 
2881
            # branch stores requires write locked branches
 
2882
            self.addCleanup(store.branch.lock_write().unlock)
2479
2883
        section = store.get_mutable_section(None)
2480
2884
        section.remove('foo')
2481
2885
        store.save()
2502
2906
 
2503
2907
    def test_set_option_in_empty_store(self):
2504
2908
        store = self.get_store(self)
 
2909
        # FIXME: There should be a better way than relying on the test
 
2910
        # parametrization to identify branch.conf -- vila 2011-0526
 
2911
        if self.store_id in ('branch', 'remote_branch'):
 
2912
            # branch stores requires write locked branches
 
2913
            self.addCleanup(store.branch.lock_write().unlock)
2505
2914
        section = store.get_mutable_section(None)
2506
2915
        section.set('foo', 'bar')
2507
2916
        store.save()
2513
2922
    def test_set_option_in_default_section(self):
2514
2923
        store = self.get_store(self)
2515
2924
        store._load_from_string('')
 
2925
        # FIXME: There should be a better way than relying on the test
 
2926
        # parametrization to identify branch.conf -- vila 2011-0526
 
2927
        if self.store_id in ('branch', 'remote_branch'):
 
2928
            # branch stores requires write locked branches
 
2929
            self.addCleanup(store.branch.lock_write().unlock)
2516
2930
        section = store.get_mutable_section(None)
2517
2931
        section.set('foo', 'bar')
2518
2932
        store.save()
2524
2938
    def test_set_option_in_named_section(self):
2525
2939
        store = self.get_store(self)
2526
2940
        store._load_from_string('')
 
2941
        # FIXME: There should be a better way than relying on the test
 
2942
        # parametrization to identify branch.conf -- vila 2011-0526
 
2943
        if self.store_id in ('branch', 'remote_branch'):
 
2944
            # branch stores requires write locked branches
 
2945
            self.addCleanup(store.branch.lock_write().unlock)
2527
2946
        section = store.get_mutable_section('baz')
2528
2947
        section.set('foo', 'bar')
2529
2948
        store.save()
2533
2952
        self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2534
2953
 
2535
2954
    def test_load_hook(self):
2536
 
        # We first needs to ensure that the store exists
 
2955
        # First, we need to ensure that the store exists
2537
2956
        store = self.get_store(self)
 
2957
        # FIXME: There should be a better way than relying on the test
 
2958
        # parametrization to identify branch.conf -- vila 2011-0526
 
2959
        if self.store_id in ('branch', 'remote_branch'):
 
2960
            # branch stores requires write locked branches
 
2961
            self.addCleanup(store.branch.lock_write().unlock)
2538
2962
        section = store.get_mutable_section('baz')
2539
2963
        section.set('foo', 'bar')
2540
2964
        store.save()
2556
2980
        config.ConfigHooks.install_named_hook('save', hook, None)
2557
2981
        self.assertLength(0, calls)
2558
2982
        store = self.get_store(self)
 
2983
        # FIXME: There should be a better way than relying on the test
 
2984
        # parametrization to identify branch.conf -- vila 2011-0526
 
2985
        if self.store_id in ('branch', 'remote_branch'):
 
2986
            # branch stores requires write locked branches
 
2987
            self.addCleanup(store.branch.lock_write().unlock)
2559
2988
        section = store.get_mutable_section('baz')
2560
2989
        section.set('foo', 'bar')
2561
2990
        store.save()
2562
2991
        self.assertLength(1, calls)
2563
2992
        self.assertEquals((store,), calls[0])
2564
2993
 
2565
 
 
2566
 
class TestIniFileStore(TestStore):
 
2994
    def test_set_mark_dirty(self):
 
2995
        stack = config.MemoryStack('')
 
2996
        self.assertLength(0, stack.store.dirty_sections)
 
2997
        stack.set('foo', 'baz')
 
2998
        self.assertLength(1, stack.store.dirty_sections)
 
2999
        self.assertTrue(stack.store._need_saving())
 
3000
 
 
3001
    def test_remove_mark_dirty(self):
 
3002
        stack = config.MemoryStack('foo=bar')
 
3003
        self.assertLength(0, stack.store.dirty_sections)
 
3004
        stack.remove('foo')
 
3005
        self.assertLength(1, stack.store.dirty_sections)
 
3006
        self.assertTrue(stack.store._need_saving())
 
3007
 
 
3008
 
 
3009
class TestStoreSaveChanges(tests.TestCaseWithTransport):
 
3010
    """Tests that config changes are kept in memory and saved on-demand."""
 
3011
 
 
3012
    def setUp(self):
 
3013
        super(TestStoreSaveChanges, self).setUp()
 
3014
        self.transport = self.get_transport()
 
3015
        # Most of the tests involve two stores pointing to the same persistent
 
3016
        # storage to observe the effects of concurrent changes
 
3017
        self.st1 = config.TransportIniFileStore(self.transport, 'foo.conf')
 
3018
        self.st2 = config.TransportIniFileStore(self.transport, 'foo.conf')
 
3019
        self.warnings = []
 
3020
        def warning(*args):
 
3021
            self.warnings.append(args[0] % args[1:])
 
3022
        self.overrideAttr(trace, 'warning', warning)
 
3023
 
 
3024
    def has_store(self, store):
 
3025
        store_basename = urlutils.relative_url(self.transport.external_url(),
 
3026
                                               store.external_url())
 
3027
        return self.transport.has(store_basename)
 
3028
 
 
3029
    def get_stack(self, store):
 
3030
        # Any stack will do as long as it uses the right store, just a single
 
3031
        # no-name section is enough
 
3032
        return config.Stack([store.get_sections], store)
 
3033
 
 
3034
    def test_no_changes_no_save(self):
 
3035
        s = self.get_stack(self.st1)
 
3036
        s.store.save_changes()
 
3037
        self.assertEquals(False, self.has_store(self.st1))
 
3038
 
 
3039
    def test_unrelated_concurrent_update(self):
 
3040
        s1 = self.get_stack(self.st1)
 
3041
        s2 = self.get_stack(self.st2)
 
3042
        s1.set('foo', 'bar')
 
3043
        s2.set('baz', 'quux')
 
3044
        s1.store.save()
 
3045
        # Changes don't propagate magically
 
3046
        self.assertEquals(None, s1.get('baz'))
 
3047
        s2.store.save_changes()
 
3048
        self.assertEquals('quux', s2.get('baz'))
 
3049
        # Changes are acquired when saving
 
3050
        self.assertEquals('bar', s2.get('foo'))
 
3051
        # Since there is no overlap, no warnings are emitted
 
3052
        self.assertLength(0, self.warnings)
 
3053
 
 
3054
    def test_concurrent_update_modified(self):
 
3055
        s1 = self.get_stack(self.st1)
 
3056
        s2 = self.get_stack(self.st2)
 
3057
        s1.set('foo', 'bar')
 
3058
        s2.set('foo', 'baz')
 
3059
        s1.store.save()
 
3060
        # Last speaker wins
 
3061
        s2.store.save_changes()
 
3062
        self.assertEquals('baz', s2.get('foo'))
 
3063
        # But the user get a warning
 
3064
        self.assertLength(1, self.warnings)
 
3065
        warning = self.warnings[0]
 
3066
        self.assertStartsWith(warning, 'Option foo in section None')
 
3067
        self.assertEndsWith(warning, 'was changed from <CREATED> to bar.'
 
3068
                            ' The baz value will be saved.')
 
3069
 
 
3070
    def test_concurrent_deletion(self):
 
3071
        self.st1._load_from_string('foo=bar')
 
3072
        self.st1.save()
 
3073
        s1 = self.get_stack(self.st1)
 
3074
        s2 = self.get_stack(self.st2)
 
3075
        s1.remove('foo')
 
3076
        s2.remove('foo')
 
3077
        s1.store.save_changes()
 
3078
        # No warning yet
 
3079
        self.assertLength(0, self.warnings)
 
3080
        s2.store.save_changes()
 
3081
        # Now we get one
 
3082
        self.assertLength(1, self.warnings)
 
3083
        warning = self.warnings[0]
 
3084
        self.assertStartsWith(warning, 'Option foo in section None')
 
3085
        self.assertEndsWith(warning, 'was changed from bar to <CREATED>.'
 
3086
                            ' The <DELETED> value will be saved.')
 
3087
 
 
3088
 
 
3089
class TestQuotingIniFileStore(tests.TestCaseWithTransport):
 
3090
 
 
3091
    def get_store(self):
 
3092
        return config.TransportIniFileStore(self.get_transport(), 'foo.conf')
 
3093
 
 
3094
    def test_get_quoted_string(self):
 
3095
        store = self.get_store()
 
3096
        store._load_from_string('foo= " abc "')
 
3097
        stack = config.Stack([store.get_sections])
 
3098
        self.assertEquals(' abc ', stack.get('foo'))
 
3099
 
 
3100
    def test_set_quoted_string(self):
 
3101
        store = self.get_store()
 
3102
        stack = config.Stack([store.get_sections], store)
 
3103
        stack.set('foo', ' a b c ')
 
3104
        store.save()
 
3105
        self.assertFileEqual('foo = " a b c "' + os.linesep, 'foo.conf')
 
3106
 
 
3107
 
 
3108
class TestTransportIniFileStore(TestStore):
2567
3109
 
2568
3110
    def test_loading_unknown_file_fails(self):
2569
 
        store = config.IniFileStore(self.get_transport(), 'I-do-not-exist')
 
3111
        store = config.TransportIniFileStore(self.get_transport(),
 
3112
            'I-do-not-exist')
2570
3113
        self.assertRaises(errors.NoSuchFile, store.load)
2571
3114
 
2572
3115
    def test_invalid_content(self):
2573
 
        store = config.IniFileStore(self.get_transport(), 'foo.conf', )
 
3116
        store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
2574
3117
        self.assertEquals(False, store.is_loaded())
2575
3118
        exc = self.assertRaises(
2576
3119
            errors.ParseConfigError, store._load_from_string,
2584
3127
        # option names share the same name space...)
2585
3128
        # FIXME: This should be fixed by forbidding dicts as values ?
2586
3129
        # -- vila 2011-04-05
2587
 
        store = config.IniFileStore(self.get_transport(), 'foo.conf', )
 
3130
        store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
2588
3131
        store._load_from_string('''
2589
3132
foo=bar
2590
3133
l=1,2
2600
3143
        sections = list(store.get_sections())
2601
3144
        self.assertLength(4, sections)
2602
3145
        # The default section has no name.
2603
 
        # List values are provided as lists
2604
 
        self.assertSectionContent((None, {'foo': 'bar', 'l': ['1', '2']}),
 
3146
        # List values are provided as strings and need to be explicitly
 
3147
        # converted by specifying from_unicode=list_from_store at option
 
3148
        # registration
 
3149
        self.assertSectionContent((None, {'foo': 'bar', 'l': u'1,2'}),
2605
3150
                                  sections[0])
2606
3151
        self.assertSectionContent(
2607
3152
            ('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
2637
3182
 
2638
3183
    def setUp(self):
2639
3184
        super(TestConcurrentStoreUpdates, self).setUp()
2640
 
        self._content = 'one=1\ntwo=2\n'
2641
3185
        self.stack = self.get_stack(self)
2642
3186
        if not isinstance(self.stack, config._CompatibleStack):
2643
3187
            raise tests.TestNotApplicable(
2644
3188
                '%s is not meant to be compatible with the old config design'
2645
3189
                % (self.stack,))
2646
 
        self.stack.store._load_from_string(self._content)
 
3190
        self.stack.set('one', '1')
 
3191
        self.stack.set('two', '2')
2647
3192
        # Flush the store
2648
3193
        self.stack.store.save()
2649
3194
 
2753
3298
    # FIXME: It may be worth looking into removing the lock dir when it's not
2754
3299
    # needed anymore and look at possible fallouts for concurrent lockers. This
2755
3300
    # will matter if/when we use config files outside of bazaar directories
2756
 
    # (.bazaar or .bzr) -- vila 20110-04-11
 
3301
    # (.bazaar or .bzr) -- vila 20110-04-111
2757
3302
 
2758
3303
 
2759
3304
class TestSectionMatcher(TestStore):
2760
3305
 
2761
 
    scenarios = [('location', {'matcher': config.LocationMatcher})]
 
3306
    scenarios = [('location', {'matcher': config.LocationMatcher}),
 
3307
                 ('id', {'matcher': config.NameMatcher}),]
2762
3308
 
2763
 
    def get_store(self, file_name):
2764
 
        return config.IniFileStore(self.get_readonly_transport(), file_name)
 
3309
    def setUp(self):
 
3310
        super(TestSectionMatcher, self).setUp()
 
3311
        # Any simple store is good enough
 
3312
        self.get_store = config.test_store_builder_registry.get('configobj')
2765
3313
 
2766
3314
    def test_no_matches_for_empty_stores(self):
2767
 
        store = self.get_store('foo.conf')
 
3315
        store = self.get_store(self)
2768
3316
        store._load_from_string('')
2769
3317
        matcher = self.matcher(store, '/bar')
2770
3318
        self.assertEquals([], list(matcher.get_sections()))
2771
3319
 
2772
3320
    def test_build_doesnt_load_store(self):
2773
 
        store = self.get_store('foo.conf')
 
3321
        store = self.get_store(self)
2774
3322
        matcher = self.matcher(store, '/bar')
2775
3323
        self.assertFalse(store.is_loaded())
2776
3324
 
2779
3327
 
2780
3328
    def get_section(self, options, extra_path):
2781
3329
        section = config.Section('foo', options)
2782
 
        # We don't care about the length so we use '0'
2783
 
        return config.LocationSection(section, 0, extra_path)
 
3330
        return config.LocationSection(section, extra_path)
2784
3331
 
2785
3332
    def test_simple_option(self):
2786
3333
        section = self.get_section({'foo': 'bar'}, '')
2800
3347
 
2801
3348
class TestLocationMatcher(TestStore):
2802
3349
 
2803
 
    def get_store(self, file_name):
2804
 
        return config.IniFileStore(self.get_readonly_transport(), file_name)
 
3350
    def setUp(self):
 
3351
        super(TestLocationMatcher, self).setUp()
 
3352
        # Any simple store is good enough
 
3353
        self.get_store = config.test_store_builder_registry.get('configobj')
 
3354
 
 
3355
    def test_unrelated_section_excluded(self):
 
3356
        store = self.get_store(self)
 
3357
        store._load_from_string('''
 
3358
[/foo]
 
3359
section=/foo
 
3360
[/foo/baz]
 
3361
section=/foo/baz
 
3362
[/foo/bar]
 
3363
section=/foo/bar
 
3364
[/foo/bar/baz]
 
3365
section=/foo/bar/baz
 
3366
[/quux/quux]
 
3367
section=/quux/quux
 
3368
''')
 
3369
        self.assertEquals(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
 
3370
                           '/quux/quux'],
 
3371
                          [section.id for _, section in store.get_sections()])
 
3372
        matcher = config.LocationMatcher(store, '/foo/bar/quux')
 
3373
        sections = [section for _, section in matcher.get_sections()]
 
3374
        self.assertEquals(['/foo/bar', '/foo'],
 
3375
                          [section.id for section in sections])
 
3376
        self.assertEquals(['quux', 'bar/quux'],
 
3377
                          [section.extra_path for section in sections])
2805
3378
 
2806
3379
    def test_more_specific_sections_first(self):
2807
 
        store = self.get_store('foo.conf')
 
3380
        store = self.get_store(self)
2808
3381
        store._load_from_string('''
2809
3382
[/foo]
2810
3383
section=/foo
2812
3385
section=/foo/bar
2813
3386
''')
2814
3387
        self.assertEquals(['/foo', '/foo/bar'],
2815
 
                          [section.id for section in store.get_sections()])
 
3388
                          [section.id for _, section in store.get_sections()])
2816
3389
        matcher = config.LocationMatcher(store, '/foo/bar/baz')
2817
 
        sections = list(matcher.get_sections())
2818
 
        self.assertEquals([3, 2],
2819
 
                          [section.length for section in sections])
 
3390
        sections = [section for _, section in matcher.get_sections()]
2820
3391
        self.assertEquals(['/foo/bar', '/foo'],
2821
3392
                          [section.id for section in sections])
2822
3393
        self.assertEquals(['baz', 'bar/baz'],
2825
3396
    def test_appendpath_in_no_name_section(self):
2826
3397
        # It's a bit weird to allow appendpath in a no-name section, but
2827
3398
        # someone may found a use for it
2828
 
        store = self.get_store('foo.conf')
 
3399
        store = self.get_store(self)
2829
3400
        store._load_from_string('''
2830
3401
foo=bar
2831
3402
foo:policy = appendpath
2833
3404
        matcher = config.LocationMatcher(store, 'dir/subdir')
2834
3405
        sections = list(matcher.get_sections())
2835
3406
        self.assertLength(1, sections)
2836
 
        self.assertEquals('bar/dir/subdir', sections[0].get('foo'))
 
3407
        self.assertEquals('bar/dir/subdir', sections[0][1].get('foo'))
2837
3408
 
2838
3409
    def test_file_urls_are_normalized(self):
2839
 
        store = self.get_store('foo.conf')
 
3410
        store = self.get_store(self)
2840
3411
        if sys.platform == 'win32':
2841
3412
            expected_url = 'file:///C:/dir/subdir'
2842
3413
            expected_location = 'C:/dir/subdir'
2847
3418
        self.assertEquals(expected_location, matcher.location)
2848
3419
 
2849
3420
 
2850
 
class TestStackGet(tests.TestCase):
2851
 
 
2852
 
    # FIXME: This should be parametrized for all known Stack or dedicated
2853
 
    # paramerized tests created to avoid bloating -- vila 2011-03-31
2854
 
 
2855
 
    def test_single_config_get(self):
2856
 
        conf = dict(foo='bar')
2857
 
        conf_stack = config.Stack([conf])
2858
 
        self.assertEquals('bar', conf_stack.get('foo'))
 
3421
class TestStartingPathMatcher(TestStore):
 
3422
 
 
3423
    def setUp(self):
 
3424
        super(TestStartingPathMatcher, self).setUp()
 
3425
        # Any simple store is good enough
 
3426
        self.store = config.IniFileStore()
 
3427
 
 
3428
    def assertSectionIDs(self, expected, location, content):
 
3429
        self.store._load_from_string(content)
 
3430
        matcher = config.StartingPathMatcher(self.store, location)
 
3431
        sections = list(matcher.get_sections())
 
3432
        self.assertLength(len(expected), sections)
 
3433
        self.assertEqual(expected, [section.id for _, section in sections])
 
3434
        return sections
 
3435
 
 
3436
    def test_empty(self):
 
3437
        self.assertSectionIDs([], self.get_url(), '')
 
3438
 
 
3439
    def test_url_vs_local_paths(self):
 
3440
        # The matcher location is an url and the section names are local paths
 
3441
        sections = self.assertSectionIDs(['/foo/bar', '/foo'],
 
3442
                                         'file:///foo/bar/baz', '''\
 
3443
[/foo]
 
3444
[/foo/bar]
 
3445
''')
 
3446
 
 
3447
    def test_local_path_vs_url(self):
 
3448
        # The matcher location is a local path and the section names are urls
 
3449
        sections = self.assertSectionIDs(['file:///foo/bar', 'file:///foo'],
 
3450
                                         '/foo/bar/baz', '''\
 
3451
[file:///foo]
 
3452
[file:///foo/bar]
 
3453
''')
 
3454
 
 
3455
 
 
3456
    def test_no_name_section_included_when_present(self):
 
3457
        # Note that other tests will cover the case where the no-name section
 
3458
        # is empty and as such, not included.
 
3459
        sections = self.assertSectionIDs(['/foo/bar', '/foo', None],
 
3460
                                         '/foo/bar/baz', '''\
 
3461
option = defined so the no-name section exists
 
3462
[/foo]
 
3463
[/foo/bar]
 
3464
''')
 
3465
        self.assertEquals(['baz', 'bar/baz', '/foo/bar/baz'],
 
3466
                          [s.locals['relpath'] for _, s in sections])
 
3467
 
 
3468
    def test_order_reversed(self):
 
3469
        self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
 
3470
[/foo]
 
3471
[/foo/bar]
 
3472
''')
 
3473
 
 
3474
    def test_unrelated_section_excluded(self):
 
3475
        self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
 
3476
[/foo]
 
3477
[/foo/qux]
 
3478
[/foo/bar]
 
3479
''')
 
3480
 
 
3481
    def test_glob_included(self):
 
3482
        sections = self.assertSectionIDs(['/foo/*/baz', '/foo/b*', '/foo'],
 
3483
                                         '/foo/bar/baz', '''\
 
3484
[/foo]
 
3485
[/foo/qux]
 
3486
[/foo/b*]
 
3487
[/foo/*/baz]
 
3488
''')
 
3489
        # Note that 'baz' as a relpath for /foo/b* is not fully correct, but
 
3490
        # nothing really is... as far using {relpath} to append it to something
 
3491
        # else, this seems good enough though.
 
3492
        self.assertEquals(['', 'baz', 'bar/baz'],
 
3493
                          [s.locals['relpath'] for _, s in sections])
 
3494
 
 
3495
    def test_respect_order(self):
 
3496
        self.assertSectionIDs(['/foo', '/foo/b*', '/foo/*/baz'],
 
3497
                              '/foo/bar/baz', '''\
 
3498
[/foo/*/baz]
 
3499
[/foo/qux]
 
3500
[/foo/b*]
 
3501
[/foo]
 
3502
''')
 
3503
 
 
3504
 
 
3505
class TestNameMatcher(TestStore):
 
3506
 
 
3507
    def setUp(self):
 
3508
        super(TestNameMatcher, self).setUp()
 
3509
        self.matcher = config.NameMatcher
 
3510
        # Any simple store is good enough
 
3511
        self.get_store = config.test_store_builder_registry.get('configobj')
 
3512
 
 
3513
    def get_matching_sections(self, name):
 
3514
        store = self.get_store(self)
 
3515
        store._load_from_string('''
 
3516
[foo]
 
3517
option=foo
 
3518
[foo/baz]
 
3519
option=foo/baz
 
3520
[bar]
 
3521
option=bar
 
3522
''')
 
3523
        matcher = self.matcher(store, name)
 
3524
        return list(matcher.get_sections())
 
3525
 
 
3526
    def test_matching(self):
 
3527
        sections = self.get_matching_sections('foo')
 
3528
        self.assertLength(1, sections)
 
3529
        self.assertSectionContent(('foo', {'option': 'foo'}), sections[0])
 
3530
 
 
3531
    def test_not_matching(self):
 
3532
        sections = self.get_matching_sections('baz')
 
3533
        self.assertLength(0, sections)
 
3534
 
 
3535
 
 
3536
class TestBaseStackGet(tests.TestCase):
 
3537
 
 
3538
    def setUp(self):
 
3539
        super(TestBaseStackGet, self).setUp()
 
3540
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
 
3541
 
 
3542
    def test_get_first_definition(self):
 
3543
        store1 = config.IniFileStore()
 
3544
        store1._load_from_string('foo=bar')
 
3545
        store2 = config.IniFileStore()
 
3546
        store2._load_from_string('foo=baz')
 
3547
        conf = config.Stack([store1.get_sections, store2.get_sections])
 
3548
        self.assertEquals('bar', conf.get('foo'))
2859
3549
 
2860
3550
    def test_get_with_registered_default_value(self):
2861
 
        conf_stack = config.Stack([dict()])
2862
 
        opt = config.Option('foo', default='bar')
2863
 
        self.overrideAttr(config, 'option_registry', registry.Registry())
2864
 
        config.option_registry.register('foo', opt)
 
3551
        config.option_registry.register(config.Option('foo', default='bar'))
 
3552
        conf_stack = config.Stack([])
2865
3553
        self.assertEquals('bar', conf_stack.get('foo'))
2866
3554
 
2867
3555
    def test_get_without_registered_default_value(self):
2868
 
        conf_stack = config.Stack([dict()])
2869
 
        opt = config.Option('foo')
2870
 
        self.overrideAttr(config, 'option_registry', registry.Registry())
2871
 
        config.option_registry.register('foo', opt)
 
3556
        config.option_registry.register(config.Option('foo'))
 
3557
        conf_stack = config.Stack([])
2872
3558
        self.assertEquals(None, conf_stack.get('foo'))
2873
3559
 
2874
3560
    def test_get_without_default_value_for_not_registered(self):
2875
 
        conf_stack = config.Stack([dict()])
2876
 
        opt = config.Option('foo')
2877
 
        self.overrideAttr(config, 'option_registry', registry.Registry())
 
3561
        conf_stack = config.Stack([])
2878
3562
        self.assertEquals(None, conf_stack.get('foo'))
2879
3563
 
2880
 
    def test_get_first_definition(self):
2881
 
        conf1 = dict(foo='bar')
2882
 
        conf2 = dict(foo='baz')
2883
 
        conf_stack = config.Stack([conf1, conf2])
2884
 
        self.assertEquals('bar', conf_stack.get('foo'))
2885
 
 
2886
 
    def test_get_embedded_definition(self):
2887
 
        conf1 = dict(yy='12')
2888
 
        conf2 = config.Stack([dict(xx='42'), dict(foo='baz')])
2889
 
        conf_stack = config.Stack([conf1, conf2])
2890
 
        self.assertEquals('baz', conf_stack.get('foo'))
2891
 
 
2892
3564
    def test_get_for_empty_section_callable(self):
2893
3565
        conf_stack = config.Stack([lambda : []])
2894
3566
        self.assertEquals(None, conf_stack.get('foo'))
2895
3567
 
2896
3568
    def test_get_for_broken_callable(self):
2897
3569
        # Trying to use and invalid callable raises an exception on first use
2898
 
        conf_stack = config.Stack([lambda : object()])
 
3570
        conf_stack = config.Stack([object])
2899
3571
        self.assertRaises(TypeError, conf_stack.get, 'foo')
2900
3572
 
2901
3573
 
 
3574
class TestStackWithSimpleStore(tests.TestCase):
 
3575
 
 
3576
    def setUp(self):
 
3577
        super(TestStackWithSimpleStore, self).setUp()
 
3578
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
 
3579
        self.registry = config.option_registry
 
3580
 
 
3581
    def get_conf(self, content=None):
 
3582
        return config.MemoryStack(content)
 
3583
 
 
3584
    def test_override_value_from_env(self):
 
3585
        self.registry.register(
 
3586
            config.Option('foo', default='bar', override_from_env=['FOO']))
 
3587
        self.overrideEnv('FOO', 'quux')
 
3588
        # Env variable provides a default taking over the option one
 
3589
        conf = self.get_conf('foo=store')
 
3590
        self.assertEquals('quux', conf.get('foo'))
 
3591
 
 
3592
    def test_first_override_value_from_env_wins(self):
 
3593
        self.registry.register(
 
3594
            config.Option('foo', default='bar',
 
3595
                          override_from_env=['NO_VALUE', 'FOO', 'BAZ']))
 
3596
        self.overrideEnv('FOO', 'foo')
 
3597
        self.overrideEnv('BAZ', 'baz')
 
3598
        # The first env var set wins
 
3599
        conf = self.get_conf('foo=store')
 
3600
        self.assertEquals('foo', conf.get('foo'))
 
3601
 
 
3602
 
 
3603
class TestMemoryStack(tests.TestCase):
 
3604
 
 
3605
    def test_get(self):
 
3606
        conf = config.MemoryStack('foo=bar')
 
3607
        self.assertEquals('bar', conf.get('foo'))
 
3608
 
 
3609
    def test_set(self):
 
3610
        conf = config.MemoryStack('foo=bar')
 
3611
        conf.set('foo', 'baz')
 
3612
        self.assertEquals('baz', conf.get('foo'))
 
3613
 
 
3614
    def test_no_content(self):
 
3615
        conf = config.MemoryStack()
 
3616
        # No content means no loading
 
3617
        self.assertFalse(conf.store.is_loaded())
 
3618
        self.assertRaises(NotImplementedError, conf.get, 'foo')
 
3619
        # But a content can still be provided
 
3620
        conf.store._load_from_string('foo=bar')
 
3621
        self.assertEquals('bar', conf.get('foo'))
 
3622
 
 
3623
 
 
3624
class TestStackIterSections(tests.TestCase):
 
3625
 
 
3626
    def test_empty_stack(self):
 
3627
        conf = config.Stack([])
 
3628
        sections = list(conf.iter_sections())
 
3629
        self.assertLength(0, sections)
 
3630
 
 
3631
    def test_empty_store(self):
 
3632
        store = config.IniFileStore()
 
3633
        store._load_from_string('')
 
3634
        conf = config.Stack([store.get_sections])
 
3635
        sections = list(conf.iter_sections())
 
3636
        self.assertLength(0, sections)
 
3637
 
 
3638
    def test_simple_store(self):
 
3639
        store = config.IniFileStore()
 
3640
        store._load_from_string('foo=bar')
 
3641
        conf = config.Stack([store.get_sections])
 
3642
        tuples = list(conf.iter_sections())
 
3643
        self.assertLength(1, tuples)
 
3644
        (found_store, found_section) = tuples[0]
 
3645
        self.assertIs(store, found_store)
 
3646
 
 
3647
    def test_two_stores(self):
 
3648
        store1 = config.IniFileStore()
 
3649
        store1._load_from_string('foo=bar')
 
3650
        store2 = config.IniFileStore()
 
3651
        store2._load_from_string('bar=qux')
 
3652
        conf = config.Stack([store1.get_sections, store2.get_sections])
 
3653
        tuples = list(conf.iter_sections())
 
3654
        self.assertLength(2, tuples)
 
3655
        self.assertIs(store1, tuples[0][0])
 
3656
        self.assertIs(store2, tuples[1][0])
 
3657
 
 
3658
 
2902
3659
class TestStackWithTransport(tests.TestCaseWithTransport):
2903
3660
 
2904
3661
    scenarios = [(key, {'get_stack': builder}) for key, builder
2914
3671
 
2915
3672
class TestStackGet(TestStackWithTransport):
2916
3673
 
 
3674
    def setUp(self):
 
3675
        super(TestStackGet, self).setUp()
 
3676
        self.conf = self.get_stack(self)
 
3677
 
2917
3678
    def test_get_for_empty_stack(self):
2918
 
        conf = self.get_stack(self)
2919
 
        self.assertEquals(None, conf.get('foo'))
 
3679
        self.assertEquals(None, self.conf.get('foo'))
2920
3680
 
2921
3681
    def test_get_hook(self):
2922
 
        conf = self.get_stack(self)
2923
 
        conf.store._load_from_string('foo=bar')
 
3682
        self.conf.set('foo', 'bar')
2924
3683
        calls = []
2925
3684
        def hook(*args):
2926
3685
            calls.append(args)
2927
3686
        config.ConfigHooks.install_named_hook('get', hook, None)
2928
3687
        self.assertLength(0, calls)
2929
 
        value = conf.get('foo')
 
3688
        value = self.conf.get('foo')
2930
3689
        self.assertEquals('bar', value)
2931
3690
        self.assertLength(1, calls)
2932
 
        self.assertEquals((conf, 'foo', 'bar'), calls[0])
 
3691
        self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
 
3692
 
 
3693
 
 
3694
class TestStackGetWithConverter(tests.TestCase):
 
3695
 
 
3696
    def setUp(self):
 
3697
        super(TestStackGetWithConverter, self).setUp()
 
3698
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
 
3699
        self.registry = config.option_registry
 
3700
 
 
3701
    def get_conf(self, content=None):
 
3702
        return config.MemoryStack(content)
 
3703
 
 
3704
    def register_bool_option(self, name, default=None, default_from_env=None):
 
3705
        b = config.Option(name, help='A boolean.',
 
3706
                          default=default, default_from_env=default_from_env,
 
3707
                          from_unicode=config.bool_from_store)
 
3708
        self.registry.register(b)
 
3709
 
 
3710
    def test_get_default_bool_None(self):
 
3711
        self.register_bool_option('foo')
 
3712
        conf = self.get_conf('')
 
3713
        self.assertEquals(None, conf.get('foo'))
 
3714
 
 
3715
    def test_get_default_bool_True(self):
 
3716
        self.register_bool_option('foo', u'True')
 
3717
        conf = self.get_conf('')
 
3718
        self.assertEquals(True, conf.get('foo'))
 
3719
 
 
3720
    def test_get_default_bool_False(self):
 
3721
        self.register_bool_option('foo', False)
 
3722
        conf = self.get_conf('')
 
3723
        self.assertEquals(False, conf.get('foo'))
 
3724
 
 
3725
    def test_get_default_bool_False_as_string(self):
 
3726
        self.register_bool_option('foo', u'False')
 
3727
        conf = self.get_conf('')
 
3728
        self.assertEquals(False, conf.get('foo'))
 
3729
 
 
3730
    def test_get_default_bool_from_env_converted(self):
 
3731
        self.register_bool_option('foo', u'True', default_from_env=['FOO'])
 
3732
        self.overrideEnv('FOO', 'False')
 
3733
        conf = self.get_conf('')
 
3734
        self.assertEquals(False, conf.get('foo'))
 
3735
 
 
3736
    def test_get_default_bool_when_conversion_fails(self):
 
3737
        self.register_bool_option('foo', default='True')
 
3738
        conf = self.get_conf('foo=invalid boolean')
 
3739
        self.assertEquals(True, conf.get('foo'))
 
3740
 
 
3741
    def register_integer_option(self, name,
 
3742
                                default=None, default_from_env=None):
 
3743
        i = config.Option(name, help='An integer.',
 
3744
                          default=default, default_from_env=default_from_env,
 
3745
                          from_unicode=config.int_from_store)
 
3746
        self.registry.register(i)
 
3747
 
 
3748
    def test_get_default_integer_None(self):
 
3749
        self.register_integer_option('foo')
 
3750
        conf = self.get_conf('')
 
3751
        self.assertEquals(None, conf.get('foo'))
 
3752
 
 
3753
    def test_get_default_integer(self):
 
3754
        self.register_integer_option('foo', 42)
 
3755
        conf = self.get_conf('')
 
3756
        self.assertEquals(42, conf.get('foo'))
 
3757
 
 
3758
    def test_get_default_integer_as_string(self):
 
3759
        self.register_integer_option('foo', u'42')
 
3760
        conf = self.get_conf('')
 
3761
        self.assertEquals(42, conf.get('foo'))
 
3762
 
 
3763
    def test_get_default_integer_from_env(self):
 
3764
        self.register_integer_option('foo', default_from_env=['FOO'])
 
3765
        self.overrideEnv('FOO', '18')
 
3766
        conf = self.get_conf('')
 
3767
        self.assertEquals(18, conf.get('foo'))
 
3768
 
 
3769
    def test_get_default_integer_when_conversion_fails(self):
 
3770
        self.register_integer_option('foo', default='12')
 
3771
        conf = self.get_conf('foo=invalid integer')
 
3772
        self.assertEquals(12, conf.get('foo'))
 
3773
 
 
3774
    def register_list_option(self, name, default=None, default_from_env=None):
 
3775
        l = config.ListOption(name, help='A list.', default=default,
 
3776
                              default_from_env=default_from_env)
 
3777
        self.registry.register(l)
 
3778
 
 
3779
    def test_get_default_list_None(self):
 
3780
        self.register_list_option('foo')
 
3781
        conf = self.get_conf('')
 
3782
        self.assertEquals(None, conf.get('foo'))
 
3783
 
 
3784
    def test_get_default_list_empty(self):
 
3785
        self.register_list_option('foo', '')
 
3786
        conf = self.get_conf('')
 
3787
        self.assertEquals([], conf.get('foo'))
 
3788
 
 
3789
    def test_get_default_list_from_env(self):
 
3790
        self.register_list_option('foo', default_from_env=['FOO'])
 
3791
        self.overrideEnv('FOO', '')
 
3792
        conf = self.get_conf('')
 
3793
        self.assertEquals([], conf.get('foo'))
 
3794
 
 
3795
    def test_get_with_list_converter_no_item(self):
 
3796
        self.register_list_option('foo', None)
 
3797
        conf = self.get_conf('foo=,')
 
3798
        self.assertEquals([], conf.get('foo'))
 
3799
 
 
3800
    def test_get_with_list_converter_many_items(self):
 
3801
        self.register_list_option('foo', None)
 
3802
        conf = self.get_conf('foo=m,o,r,e')
 
3803
        self.assertEquals(['m', 'o', 'r', 'e'], conf.get('foo'))
 
3804
 
 
3805
    def test_get_with_list_converter_embedded_spaces_many_items(self):
 
3806
        self.register_list_option('foo', None)
 
3807
        conf = self.get_conf('foo=" bar", "baz "')
 
3808
        self.assertEquals([' bar', 'baz '], conf.get('foo'))
 
3809
 
 
3810
    def test_get_with_list_converter_stripped_spaces_many_items(self):
 
3811
        self.register_list_option('foo', None)
 
3812
        conf = self.get_conf('foo= bar ,  baz ')
 
3813
        self.assertEquals(['bar', 'baz'], conf.get('foo'))
 
3814
 
 
3815
 
 
3816
class TestIterOptionRefs(tests.TestCase):
 
3817
    """iter_option_refs is a bit unusual, document some cases."""
 
3818
 
 
3819
    def assertRefs(self, expected, string):
 
3820
        self.assertEquals(expected, list(config.iter_option_refs(string)))
 
3821
 
 
3822
    def test_empty(self):
 
3823
        self.assertRefs([(False, '')], '')
 
3824
 
 
3825
    def test_no_refs(self):
 
3826
        self.assertRefs([(False, 'foo bar')], 'foo bar')
 
3827
 
 
3828
    def test_single_ref(self):
 
3829
        self.assertRefs([(False, ''), (True, '{foo}'), (False, '')], '{foo}')
 
3830
 
 
3831
    def test_broken_ref(self):
 
3832
        self.assertRefs([(False, '{foo')], '{foo')
 
3833
 
 
3834
    def test_embedded_ref(self):
 
3835
        self.assertRefs([(False, '{'), (True, '{foo}'), (False, '}')],
 
3836
                        '{{foo}}')
 
3837
 
 
3838
    def test_two_refs(self):
 
3839
        self.assertRefs([(False, ''), (True, '{foo}'),
 
3840
                         (False, ''), (True, '{bar}'),
 
3841
                         (False, ''),],
 
3842
                        '{foo}{bar}')
 
3843
 
 
3844
    def test_newline_in_refs_are_not_matched(self):
 
3845
        self.assertRefs([(False, '{\nxx}{xx\n}{{\n}}')], '{\nxx}{xx\n}{{\n}}')
 
3846
 
 
3847
 
 
3848
class TestStackExpandOptions(tests.TestCaseWithTransport):
 
3849
 
 
3850
    def setUp(self):
 
3851
        super(TestStackExpandOptions, self).setUp()
 
3852
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
 
3853
        self.registry = config.option_registry
 
3854
        store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
 
3855
        self.conf = config.Stack([store.get_sections], store)
 
3856
 
 
3857
    def assertExpansion(self, expected, string, env=None):
 
3858
        self.assertEquals(expected, self.conf.expand_options(string, env))
 
3859
 
 
3860
    def test_no_expansion(self):
 
3861
        self.assertExpansion('foo', 'foo')
 
3862
 
 
3863
    def test_expand_default_value(self):
 
3864
        self.conf.store._load_from_string('bar=baz')
 
3865
        self.registry.register(config.Option('foo', default=u'{bar}'))
 
3866
        self.assertEquals('baz', self.conf.get('foo', expand=True))
 
3867
 
 
3868
    def test_expand_default_from_env(self):
 
3869
        self.conf.store._load_from_string('bar=baz')
 
3870
        self.registry.register(config.Option('foo', default_from_env=['FOO']))
 
3871
        self.overrideEnv('FOO', '{bar}')
 
3872
        self.assertEquals('baz', self.conf.get('foo', expand=True))
 
3873
 
 
3874
    def test_expand_default_on_failed_conversion(self):
 
3875
        self.conf.store._load_from_string('baz=bogus\nbar=42\nfoo={baz}')
 
3876
        self.registry.register(
 
3877
            config.Option('foo', default=u'{bar}',
 
3878
                          from_unicode=config.int_from_store))
 
3879
        self.assertEquals(42, self.conf.get('foo', expand=True))
 
3880
 
 
3881
    def test_env_adding_options(self):
 
3882
        self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
 
3883
 
 
3884
    def test_env_overriding_options(self):
 
3885
        self.conf.store._load_from_string('foo=baz')
 
3886
        self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
 
3887
 
 
3888
    def test_simple_ref(self):
 
3889
        self.conf.store._load_from_string('foo=xxx')
 
3890
        self.assertExpansion('xxx', '{foo}')
 
3891
 
 
3892
    def test_unknown_ref(self):
 
3893
        self.assertRaises(errors.ExpandingUnknownOption,
 
3894
                          self.conf.expand_options, '{foo}')
 
3895
 
 
3896
    def test_indirect_ref(self):
 
3897
        self.conf.store._load_from_string('''
 
3898
foo=xxx
 
3899
bar={foo}
 
3900
''')
 
3901
        self.assertExpansion('xxx', '{bar}')
 
3902
 
 
3903
    def test_embedded_ref(self):
 
3904
        self.conf.store._load_from_string('''
 
3905
foo=xxx
 
3906
bar=foo
 
3907
''')
 
3908
        self.assertExpansion('xxx', '{{bar}}')
 
3909
 
 
3910
    def test_simple_loop(self):
 
3911
        self.conf.store._load_from_string('foo={foo}')
 
3912
        self.assertRaises(errors.OptionExpansionLoop,
 
3913
                          self.conf.expand_options, '{foo}')
 
3914
 
 
3915
    def test_indirect_loop(self):
 
3916
        self.conf.store._load_from_string('''
 
3917
foo={bar}
 
3918
bar={baz}
 
3919
baz={foo}''')
 
3920
        e = self.assertRaises(errors.OptionExpansionLoop,
 
3921
                              self.conf.expand_options, '{foo}')
 
3922
        self.assertEquals('foo->bar->baz', e.refs)
 
3923
        self.assertEquals('{foo}', e.string)
 
3924
 
 
3925
    def test_list(self):
 
3926
        self.conf.store._load_from_string('''
 
3927
foo=start
 
3928
bar=middle
 
3929
baz=end
 
3930
list={foo},{bar},{baz}
 
3931
''')
 
3932
        self.registry.register(
 
3933
            config.ListOption('list'))
 
3934
        self.assertEquals(['start', 'middle', 'end'],
 
3935
                           self.conf.get('list', expand=True))
 
3936
 
 
3937
    def test_cascading_list(self):
 
3938
        self.conf.store._load_from_string('''
 
3939
foo=start,{bar}
 
3940
bar=middle,{baz}
 
3941
baz=end
 
3942
list={foo}
 
3943
''')
 
3944
        self.registry.register(config.ListOption('list'))
 
3945
        # Register an intermediate option as a list to ensure no conversion
 
3946
        # happen while expanding. Conversion should only occur for the original
 
3947
        # option ('list' here).
 
3948
        self.registry.register(config.ListOption('baz'))
 
3949
        self.assertEquals(['start', 'middle', 'end'],
 
3950
                           self.conf.get('list', expand=True))
 
3951
 
 
3952
    def test_pathologically_hidden_list(self):
 
3953
        self.conf.store._load_from_string('''
 
3954
foo=bin
 
3955
bar=go
 
3956
start={foo
 
3957
middle=},{
 
3958
end=bar}
 
3959
hidden={start}{middle}{end}
 
3960
''')
 
3961
        # What matters is what the registration says, the conversion happens
 
3962
        # only after all expansions have been performed
 
3963
        self.registry.register(config.ListOption('hidden'))
 
3964
        self.assertEquals(['bin', 'go'],
 
3965
                          self.conf.get('hidden', expand=True))
 
3966
 
 
3967
 
 
3968
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
 
3969
 
 
3970
    def setUp(self):
 
3971
        super(TestStackCrossSectionsExpand, self).setUp()
 
3972
 
 
3973
    def get_config(self, location, string):
 
3974
        if string is None:
 
3975
            string = ''
 
3976
        # Since we don't save the config we won't strictly require to inherit
 
3977
        # from TestCaseInTempDir, but an error occurs so quickly...
 
3978
        c = config.LocationStack(location)
 
3979
        c.store._load_from_string(string)
 
3980
        return c
 
3981
 
 
3982
    def test_dont_cross_unrelated_section(self):
 
3983
        c = self.get_config('/another/branch/path','''
 
3984
[/one/branch/path]
 
3985
foo = hello
 
3986
bar = {foo}/2
 
3987
 
 
3988
[/another/branch/path]
 
3989
bar = {foo}/2
 
3990
''')
 
3991
        self.assertRaises(errors.ExpandingUnknownOption,
 
3992
                          c.get, 'bar', expand=True)
 
3993
 
 
3994
    def test_cross_related_sections(self):
 
3995
        c = self.get_config('/project/branch/path','''
 
3996
[/project]
 
3997
foo = qu
 
3998
 
 
3999
[/project/branch/path]
 
4000
bar = {foo}ux
 
4001
''')
 
4002
        self.assertEquals('quux', c.get('bar', expand=True))
 
4003
 
 
4004
 
 
4005
class TestStackCrossStoresExpand(tests.TestCaseWithTransport):
 
4006
 
 
4007
    def test_cross_global_locations(self):
 
4008
        l_store = config.LocationStore()
 
4009
        l_store._load_from_string('''
 
4010
[/branch]
 
4011
lfoo = loc-foo
 
4012
lbar = {gbar}
 
4013
''')
 
4014
        l_store.save()
 
4015
        g_store = config.GlobalStore()
 
4016
        g_store._load_from_string('''
 
4017
[DEFAULT]
 
4018
gfoo = {lfoo}
 
4019
gbar = glob-bar
 
4020
''')
 
4021
        g_store.save()
 
4022
        stack = config.LocationStack('/branch')
 
4023
        self.assertEquals('glob-bar', stack.get('lbar', expand=True))
 
4024
        self.assertEquals('loc-foo', stack.get('gfoo', expand=True))
 
4025
 
 
4026
 
 
4027
class TestStackExpandSectionLocals(tests.TestCaseWithTransport):
 
4028
 
 
4029
    def test_expand_locals_empty(self):
 
4030
        l_store = config.LocationStore()
 
4031
        l_store._load_from_string('''
 
4032
[/home/user/project]
 
4033
base = {basename}
 
4034
rel = {relpath}
 
4035
''')
 
4036
        l_store.save()
 
4037
        stack = config.LocationStack('/home/user/project/')
 
4038
        self.assertEquals('', stack.get('base', expand=True))
 
4039
        self.assertEquals('', stack.get('rel', expand=True))
 
4040
 
 
4041
    def test_expand_basename_locally(self):
 
4042
        l_store = config.LocationStore()
 
4043
        l_store._load_from_string('''
 
4044
[/home/user/project]
 
4045
bfoo = {basename}
 
4046
''')
 
4047
        l_store.save()
 
4048
        stack = config.LocationStack('/home/user/project/branch')
 
4049
        self.assertEquals('branch', stack.get('bfoo', expand=True))
 
4050
 
 
4051
    def test_expand_basename_locally_longer_path(self):
 
4052
        l_store = config.LocationStore()
 
4053
        l_store._load_from_string('''
 
4054
[/home/user]
 
4055
bfoo = {basename}
 
4056
''')
 
4057
        l_store.save()
 
4058
        stack = config.LocationStack('/home/user/project/dir/branch')
 
4059
        self.assertEquals('branch', stack.get('bfoo', expand=True))
 
4060
 
 
4061
    def test_expand_relpath_locally(self):
 
4062
        l_store = config.LocationStore()
 
4063
        l_store._load_from_string('''
 
4064
[/home/user/project]
 
4065
lfoo = loc-foo/{relpath}
 
4066
''')
 
4067
        l_store.save()
 
4068
        stack = config.LocationStack('/home/user/project/branch')
 
4069
        self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
 
4070
 
 
4071
    def test_expand_relpath_unknonw_in_global(self):
 
4072
        g_store = config.GlobalStore()
 
4073
        g_store._load_from_string('''
 
4074
[DEFAULT]
 
4075
gfoo = {relpath}
 
4076
''')
 
4077
        g_store.save()
 
4078
        stack = config.LocationStack('/home/user/project/branch')
 
4079
        self.assertRaises(errors.ExpandingUnknownOption,
 
4080
                          stack.get, 'gfoo', expand=True)
 
4081
 
 
4082
    def test_expand_local_option_locally(self):
 
4083
        l_store = config.LocationStore()
 
4084
        l_store._load_from_string('''
 
4085
[/home/user/project]
 
4086
lfoo = loc-foo/{relpath}
 
4087
lbar = {gbar}
 
4088
''')
 
4089
        l_store.save()
 
4090
        g_store = config.GlobalStore()
 
4091
        g_store._load_from_string('''
 
4092
[DEFAULT]
 
4093
gfoo = {lfoo}
 
4094
gbar = glob-bar
 
4095
''')
 
4096
        g_store.save()
 
4097
        stack = config.LocationStack('/home/user/project/branch')
 
4098
        self.assertEquals('glob-bar', stack.get('lbar', expand=True))
 
4099
        self.assertEquals('loc-foo/branch', stack.get('gfoo', expand=True))
 
4100
 
 
4101
    def test_locals_dont_leak(self):
 
4102
        """Make sure we chose the right local in presence of several sections.
 
4103
        """
 
4104
        l_store = config.LocationStore()
 
4105
        l_store._load_from_string('''
 
4106
[/home/user]
 
4107
lfoo = loc-foo/{relpath}
 
4108
[/home/user/project]
 
4109
lfoo = loc-foo/{relpath}
 
4110
''')
 
4111
        l_store.save()
 
4112
        stack = config.LocationStack('/home/user/project/branch')
 
4113
        self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
 
4114
        stack = config.LocationStack('/home/user/bar/baz')
 
4115
        self.assertEquals('loc-foo/bar/baz', stack.get('lfoo', expand=True))
 
4116
 
2933
4117
 
2934
4118
 
2935
4119
class TestStackSet(TestStackWithTransport):
2936
4120
 
2937
4121
    def test_simple_set(self):
2938
4122
        conf = self.get_stack(self)
2939
 
        conf.store._load_from_string('foo=bar')
2940
 
        self.assertEquals('bar', conf.get('foo'))
 
4123
        self.assertEquals(None, conf.get('foo'))
2941
4124
        conf.set('foo', 'baz')
2942
4125
        # Did we get it back ?
2943
4126
        self.assertEquals('baz', conf.get('foo'))
2963
4146
 
2964
4147
    def test_remove_existing(self):
2965
4148
        conf = self.get_stack(self)
2966
 
        conf.store._load_from_string('foo=bar')
 
4149
        conf.set('foo', 'bar')
2967
4150
        self.assertEquals('bar', conf.get('foo'))
2968
4151
        conf.remove('foo')
2969
4152
        # Did we get it back ?
2980
4163
        config.ConfigHooks.install_named_hook('remove', hook, None)
2981
4164
        self.assertLength(0, calls)
2982
4165
        conf = self.get_stack(self)
2983
 
        conf.store._load_from_string('foo=bar')
 
4166
        conf.set('foo', 'bar')
2984
4167
        conf.remove('foo')
2985
4168
        self.assertLength(1, calls)
2986
4169
        self.assertEquals((conf, 'foo'), calls[0])
3161
4344
        conf = config.AuthenticationConfig(_file=StringIO(
3162
4345
                'foo = bar\xff'))
3163
4346
        self.assertRaises(errors.ConfigContentError, conf._get_config)
3164
 
        
 
4347
 
3165
4348
    def test_missing_auth_section_header(self):
3166
4349
        conf = config.AuthenticationConfig(_file=StringIO('foo = bar'))
3167
4350
        self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
3648
4831
 
3649
4832
    def test_auto_user_id(self):
3650
4833
        """Automatic inference of user name.
3651
 
        
 
4834
 
3652
4835
        This is a bit hard to test in an isolated way, because it depends on
3653
4836
        system functions that go direct to /etc or perhaps somewhere else.
3654
4837
        But it's reasonable to say that on Unix, with an /etc/mailname, we ought
3664
4847
        else:
3665
4848
            self.assertEquals((None, None), (realname, address))
3666
4849
 
 
4850
 
 
4851
class EmailOptionTests(tests.TestCase):
 
4852
 
 
4853
    def test_default_email_uses_BZR_EMAIL(self):
 
4854
        conf = config.MemoryStack('email=jelmer@debian.org')
 
4855
        # BZR_EMAIL takes precedence over EMAIL
 
4856
        self.overrideEnv('BZR_EMAIL', 'jelmer@samba.org')
 
4857
        self.overrideEnv('EMAIL', 'jelmer@apache.org')
 
4858
        self.assertEquals('jelmer@samba.org', conf.get('email'))
 
4859
 
 
4860
    def test_default_email_uses_EMAIL(self):
 
4861
        conf = config.MemoryStack('')
 
4862
        self.overrideEnv('BZR_EMAIL', None)
 
4863
        self.overrideEnv('EMAIL', 'jelmer@apache.org')
 
4864
        self.assertEquals('jelmer@apache.org', conf.get('email'))
 
4865
 
 
4866
    def test_BZR_EMAIL_overrides(self):
 
4867
        conf = config.MemoryStack('email=jelmer@debian.org')
 
4868
        self.overrideEnv('BZR_EMAIL', 'jelmer@apache.org')
 
4869
        self.assertEquals('jelmer@apache.org', conf.get('email'))
 
4870
        self.overrideEnv('BZR_EMAIL', None)
 
4871
        self.overrideEnv('EMAIL', 'jelmer@samba.org')
 
4872
        self.assertEquals('jelmer@debian.org', conf.get('email'))
 
4873
 
 
4874
 
 
4875
class MailClientOptionTests(tests.TestCase):
 
4876
 
 
4877
    def test_default(self):
 
4878
        conf = config.MemoryStack('')
 
4879
        client = conf.get('mail_client')
 
4880
        self.assertIs(client, mail_client.DefaultMail)
 
4881
 
 
4882
    def test_evolution(self):
 
4883
        conf = config.MemoryStack('mail_client=evolution')
 
4884
        client = conf.get('mail_client')
 
4885
        self.assertIs(client, mail_client.Evolution)
 
4886
 
 
4887
    def test_kmail(self):
 
4888
        conf = config.MemoryStack('mail_client=kmail')
 
4889
        client = conf.get('mail_client')
 
4890
        self.assertIs(client, mail_client.KMail)
 
4891
 
 
4892
    def test_mutt(self):
 
4893
        conf = config.MemoryStack('mail_client=mutt')
 
4894
        client = conf.get('mail_client')
 
4895
        self.assertIs(client, mail_client.Mutt)
 
4896
 
 
4897
    def test_thunderbird(self):
 
4898
        conf = config.MemoryStack('mail_client=thunderbird')
 
4899
        client = conf.get('mail_client')
 
4900
        self.assertIs(client, mail_client.Thunderbird)
 
4901
 
 
4902
    def test_explicit_default(self):
 
4903
        conf = config.MemoryStack('mail_client=default')
 
4904
        client = conf.get('mail_client')
 
4905
        self.assertIs(client, mail_client.DefaultMail)
 
4906
 
 
4907
    def test_editor(self):
 
4908
        conf = config.MemoryStack('mail_client=editor')
 
4909
        client = conf.get('mail_client')
 
4910
        self.assertIs(client, mail_client.Editor)
 
4911
 
 
4912
    def test_mapi(self):
 
4913
        conf = config.MemoryStack('mail_client=mapi')
 
4914
        client = conf.get('mail_client')
 
4915
        self.assertIs(client, mail_client.MAPIClient)
 
4916
 
 
4917
    def test_xdg_email(self):
 
4918
        conf = config.MemoryStack('mail_client=xdg-email')
 
4919
        client = conf.get('mail_client')
 
4920
        self.assertIs(client, mail_client.XDGEmail)
 
4921
 
 
4922
    def test_unknown(self):
 
4923
        conf = config.MemoryStack('mail_client=firebird')
 
4924
        self.assertRaises(errors.ConfigOptionValueError, conf.get,
 
4925
                'mail_client')