~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_config.py

  • Committer: Vincent Ladeuil
  • Date: 2012-03-14 10:17:12 UTC
  • mto: This revision was merged to the branch mainline in revision 6501.
  • Revision ID: v.ladeuil+lp@free.fr-20120314101712-8m19vlkis5yr0xtp
Yet more deprecated code removals

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:
462
476
    def test_constructs(self):
463
477
        config.Config()
464
478
 
465
 
    def test_no_default_editor(self):
466
 
        self.assertRaises(
467
 
            NotImplementedError,
468
 
            self.applyDeprecated, deprecated_in((2, 4, 0)),
469
 
            config.Config().get_editor)
470
 
 
471
479
    def test_user_email(self):
472
480
        my_config = InstrumentedConfig()
473
481
        self.assertEqual('robert.collins@example.org', my_config.user_email())
481
489
 
482
490
    def test_signatures_default(self):
483
491
        my_config = config.Config()
484
 
        self.assertFalse(my_config.signature_needed())
 
492
        self.assertFalse(
 
493
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
494
                my_config.signature_needed))
485
495
        self.assertEqual(config.CHECK_IF_POSSIBLE,
486
 
                         my_config.signature_checking())
 
496
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
497
                my_config.signature_checking))
487
498
        self.assertEqual(config.SIGN_WHEN_REQUIRED,
488
 
                         my_config.signing_policy())
 
499
                self.applyDeprecated(deprecated_in((2, 5, 0)),
 
500
                    my_config.signing_policy))
489
501
 
490
502
    def test_signatures_template_method(self):
491
503
        my_config = InstrumentedConfig()
492
 
        self.assertEqual(config.CHECK_NEVER, my_config.signature_checking())
 
504
        self.assertEqual(config.CHECK_NEVER,
 
505
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
506
                my_config.signature_checking))
493
507
        self.assertEqual(['_get_signature_checking'], my_config._calls)
494
508
 
495
509
    def test_signatures_template_method_none(self):
496
510
        my_config = InstrumentedConfig()
497
511
        my_config._signatures = None
498
512
        self.assertEqual(config.CHECK_IF_POSSIBLE,
499
 
                         my_config.signature_checking())
 
513
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
514
                             my_config.signature_checking))
500
515
        self.assertEqual(['_get_signature_checking'], my_config._calls)
501
516
 
502
517
    def test_gpg_signing_command_default(self):
503
518
        my_config = config.Config()
504
 
        self.assertEqual('gpg', my_config.gpg_signing_command())
 
519
        self.assertEqual('gpg',
 
520
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
521
                my_config.gpg_signing_command))
505
522
 
506
523
    def test_get_user_option_default(self):
507
524
        my_config = config.Config()
509
526
 
510
527
    def test_post_commit_default(self):
511
528
        my_config = config.Config()
512
 
        self.assertEqual(None, my_config.post_commit())
 
529
        self.assertEqual(None, self.applyDeprecated(deprecated_in((2, 5, 0)),
 
530
                                                    my_config.post_commit))
 
531
 
513
532
 
514
533
    def test_log_format_default(self):
515
534
        my_config = config.Config()
516
 
        self.assertEqual('long', my_config.log_format())
 
535
        self.assertEqual('long',
 
536
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
537
                                              my_config.log_format))
517
538
 
518
539
    def test_acceptable_keys_default(self):
519
540
        my_config = config.Config()
520
 
        self.assertEqual(None, my_config.acceptable_keys())
 
541
        self.assertEqual(None, self.applyDeprecated(deprecated_in((2, 5, 0)),
 
542
            my_config.acceptable_keys))
521
543
 
522
544
    def test_validate_signatures_in_log_default(self):
523
545
        my_config = config.Config()
549
571
    def test_config_dir(self):
550
572
        self.assertEqual(config.config_dir(), self.bzr_home)
551
573
 
 
574
    def test_config_dir_is_unicode(self):
 
575
        self.assertIsInstance(config.config_dir(), unicode)
 
576
 
552
577
    def test_config_filename(self):
553
578
        self.assertEqual(config.config_filename(),
554
579
                         self.bzr_home + '/bazaar.conf')
658
683
        self.assertFileEqual(content, 'test.conf')
659
684
 
660
685
 
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
686
class TestIniConfigOptionExpansion(tests.TestCase):
719
687
    """Test option expansion from the IniConfig level.
720
688
 
817
785
        self.assertEquals(['{foo', '}', '{', 'bar}'],
818
786
                          conf.get_user_option('hidden', expand=True))
819
787
 
 
788
 
820
789
class TestLocationConfigOptionExpansion(tests.TestCaseInTempDir):
821
790
 
822
791
    def get_config(self, location, string=None):
1033
1002
        # automatically cast to list
1034
1003
        self.assertEqual(['x'], get_list('one_item'))
1035
1004
 
 
1005
    def test_get_user_option_as_int_from_SI(self):
 
1006
        conf, parser = self.make_config_parser("""
 
1007
plain = 100
 
1008
si_k = 5k,
 
1009
si_kb = 5kb,
 
1010
si_m = 5M,
 
1011
si_mb = 5MB,
 
1012
si_g = 5g,
 
1013
si_gb = 5gB,
 
1014
""")
 
1015
        def get_si(s, default=None):
 
1016
            return self.applyDeprecated(
 
1017
                deprecated_in((2, 5, 0)),
 
1018
                conf.get_user_option_as_int_from_SI, s, default)
 
1019
        self.assertEqual(100, get_si('plain'))
 
1020
        self.assertEqual(5000, get_si('si_k'))
 
1021
        self.assertEqual(5000, get_si('si_kb'))
 
1022
        self.assertEqual(5000000, get_si('si_m'))
 
1023
        self.assertEqual(5000000, get_si('si_mb'))
 
1024
        self.assertEqual(5000000000, get_si('si_g'))
 
1025
        self.assertEqual(5000000000, get_si('si_gb'))
 
1026
        self.assertEqual(None, get_si('non-exist'))
 
1027
        self.assertEqual(42, get_si('non-exist-with-default',  42))
 
1028
 
1036
1029
 
1037
1030
class TestSupressWarning(TestIniConfig):
1038
1031
 
1086
1079
 
1087
1080
    def test_get_config(self):
1088
1081
        """The Branch.get_config method works properly"""
1089
 
        b = bzrdir.BzrDir.create_standalone_workingtree('.').branch
 
1082
        b = controldir.ControlDir.create_standalone_workingtree('.').branch
1090
1083
        my_config = b.get_config()
1091
1084
        self.assertIs(my_config.get_user_option('wacky'), None)
1092
1085
        my_config.set_user_option('wacky', 'unlikely')
1141
1134
        b = self.make_branch('!repo')
1142
1135
        self.assertEqual('!repo', b.get_config().get_nickname())
1143
1136
 
 
1137
    def test_autonick_uses_branch_name(self):
 
1138
        b = self.make_branch('foo', name='bar')
 
1139
        self.assertEqual('bar', b.get_config().get_nickname())
 
1140
 
1144
1141
    def test_warn_if_masked(self):
1145
1142
        warnings = []
1146
1143
        def warning(*args):
1186
1183
        my_config = config.GlobalConfig()
1187
1184
        self.assertEqual(None, my_config._get_user_id())
1188
1185
 
1189
 
    def test_configured_editor(self):
1190
 
        my_config = config.GlobalConfig.from_string(sample_config_text)
1191
 
        editor = self.applyDeprecated(
1192
 
            deprecated_in((2, 4, 0)), my_config.get_editor)
1193
 
        self.assertEqual('vim', editor)
1194
 
 
1195
1186
    def test_signatures_always(self):
1196
1187
        my_config = config.GlobalConfig.from_string(sample_always_signatures)
1197
1188
        self.assertEqual(config.CHECK_NEVER,
1198
 
                         my_config.signature_checking())
 
1189
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1190
                             my_config.signature_checking))
1199
1191
        self.assertEqual(config.SIGN_ALWAYS,
1200
 
                         my_config.signing_policy())
1201
 
        self.assertEqual(True, my_config.signature_needed())
 
1192
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1193
                             my_config.signing_policy))
 
1194
        self.assertEqual(True,
 
1195
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1196
                my_config.signature_needed))
1202
1197
 
1203
1198
    def test_signatures_if_possible(self):
1204
1199
        my_config = config.GlobalConfig.from_string(sample_maybe_signatures)
1205
1200
        self.assertEqual(config.CHECK_NEVER,
1206
 
                         my_config.signature_checking())
 
1201
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1202
                             my_config.signature_checking))
1207
1203
        self.assertEqual(config.SIGN_WHEN_REQUIRED,
1208
 
                         my_config.signing_policy())
1209
 
        self.assertEqual(False, my_config.signature_needed())
 
1204
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1205
                             my_config.signing_policy))
 
1206
        self.assertEqual(False, self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1207
            my_config.signature_needed))
1210
1208
 
1211
1209
    def test_signatures_ignore(self):
1212
1210
        my_config = config.GlobalConfig.from_string(sample_ignore_signatures)
1213
1211
        self.assertEqual(config.CHECK_ALWAYS,
1214
 
                         my_config.signature_checking())
 
1212
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1213
                             my_config.signature_checking))
1215
1214
        self.assertEqual(config.SIGN_NEVER,
1216
 
                         my_config.signing_policy())
1217
 
        self.assertEqual(False, my_config.signature_needed())
 
1215
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1216
                             my_config.signing_policy))
 
1217
        self.assertEqual(False, self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1218
            my_config.signature_needed))
1218
1219
 
1219
1220
    def _get_sample_config(self):
1220
1221
        my_config = config.GlobalConfig.from_string(sample_config_text)
1222
1223
 
1223
1224
    def test_gpg_signing_command(self):
1224
1225
        my_config = self._get_sample_config()
1225
 
        self.assertEqual("gnome-gpg", my_config.gpg_signing_command())
1226
 
        self.assertEqual(False, my_config.signature_needed())
 
1226
        self.assertEqual("gnome-gpg",
 
1227
            self.applyDeprecated(
 
1228
                deprecated_in((2, 5, 0)), my_config.gpg_signing_command))
 
1229
        self.assertEqual(False, self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1230
            my_config.signature_needed))
 
1231
 
 
1232
    def test_gpg_signing_key(self):
 
1233
        my_config = self._get_sample_config()
 
1234
        self.assertEqual("DD4D5088",
 
1235
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1236
                my_config.gpg_signing_key))
1227
1237
 
1228
1238
    def _get_empty_config(self):
1229
1239
        my_config = config.GlobalConfig()
1231
1241
 
1232
1242
    def test_gpg_signing_command_unset(self):
1233
1243
        my_config = self._get_empty_config()
1234
 
        self.assertEqual("gpg", my_config.gpg_signing_command())
 
1244
        self.assertEqual("gpg",
 
1245
            self.applyDeprecated(
 
1246
                deprecated_in((2, 5, 0)), my_config.gpg_signing_command))
1235
1247
 
1236
1248
    def test_get_user_option_default(self):
1237
1249
        my_config = self._get_empty_config()
1244
1256
 
1245
1257
    def test_post_commit_default(self):
1246
1258
        my_config = self._get_sample_config()
1247
 
        self.assertEqual(None, my_config.post_commit())
 
1259
        self.assertEqual(None,
 
1260
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1261
                                              my_config.post_commit))
1248
1262
 
1249
1263
    def test_configured_logformat(self):
1250
1264
        my_config = self._get_sample_config()
1251
 
        self.assertEqual("short", my_config.log_format())
 
1265
        self.assertEqual("short",
 
1266
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1267
                                              my_config.log_format))
1252
1268
 
1253
1269
    def test_configured_acceptable_keys(self):
1254
1270
        my_config = self._get_sample_config()
1255
 
        self.assertEqual("amy", my_config.acceptable_keys())
 
1271
        self.assertEqual("amy",
 
1272
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1273
                my_config.acceptable_keys))
1256
1274
 
1257
1275
    def test_configured_validate_signatures_in_log(self):
1258
1276
        my_config = self._get_sample_config()
1296
1314
        self.log(repr(tools))
1297
1315
        self.assertEqual(
1298
1316
            {u'funkytool' : u'funkytool "arg with spaces" {this_temp}',
1299
 
            u'sometool' : u'sometool {base} {this} {other} -o {result}'},
 
1317
            u'sometool' : u'sometool {base} {this} {other} -o {result}',
 
1318
            u'newtool' : u'"newtool with spaces" {this_temp}'},
1300
1319
            tools)
1301
1320
 
1302
1321
    def test_get_merge_tools_empty(self):
1493
1512
        self.get_branch_config('http://www.example.com',
1494
1513
                                 global_config=sample_ignore_signatures)
1495
1514
        self.assertEqual(config.CHECK_ALWAYS,
1496
 
                         self.my_config.signature_checking())
 
1515
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1516
                             self.my_config.signature_checking))
1497
1517
        self.assertEqual(config.SIGN_NEVER,
1498
 
                         self.my_config.signing_policy())
 
1518
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1519
                             self.my_config.signing_policy))
1499
1520
 
1500
1521
    def test_signatures_never(self):
1501
1522
        self.get_branch_config('/a/c')
1502
1523
        self.assertEqual(config.CHECK_NEVER,
1503
 
                         self.my_config.signature_checking())
 
1524
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1525
                             self.my_config.signature_checking))
1504
1526
 
1505
1527
    def test_signatures_when_available(self):
1506
1528
        self.get_branch_config('/a/', global_config=sample_ignore_signatures)
1507
1529
        self.assertEqual(config.CHECK_IF_POSSIBLE,
1508
 
                         self.my_config.signature_checking())
 
1530
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1531
                             self.my_config.signature_checking))
1509
1532
 
1510
1533
    def test_signatures_always(self):
1511
1534
        self.get_branch_config('/b')
1512
1535
        self.assertEqual(config.CHECK_ALWAYS,
1513
 
                         self.my_config.signature_checking())
 
1536
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1537
                         self.my_config.signature_checking))
1514
1538
 
1515
1539
    def test_gpg_signing_command(self):
1516
1540
        self.get_branch_config('/b')
1517
 
        self.assertEqual("gnome-gpg", self.my_config.gpg_signing_command())
 
1541
        self.assertEqual("gnome-gpg",
 
1542
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1543
                self.my_config.gpg_signing_command))
1518
1544
 
1519
1545
    def test_gpg_signing_command_missing(self):
1520
1546
        self.get_branch_config('/a')
1521
 
        self.assertEqual("false", self.my_config.gpg_signing_command())
 
1547
        self.assertEqual("false",
 
1548
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1549
                self.my_config.gpg_signing_command))
 
1550
 
 
1551
    def test_gpg_signing_key(self):
 
1552
        self.get_branch_config('/b')
 
1553
        self.assertEqual("DD4D5088", self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1554
            self.my_config.gpg_signing_key))
 
1555
 
 
1556
    def test_gpg_signing_key_default(self):
 
1557
        self.get_branch_config('/a')
 
1558
        self.assertEqual("erik@bagfors.nu",
 
1559
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1560
                self.my_config.gpg_signing_key))
1522
1561
 
1523
1562
    def test_get_user_option_global(self):
1524
1563
        self.get_branch_config('/a')
1612
1651
    def test_post_commit_default(self):
1613
1652
        self.get_branch_config('/a/c')
1614
1653
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1615
 
                         self.my_config.post_commit())
 
1654
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1655
                                              self.my_config.post_commit))
1616
1656
 
1617
1657
    def get_branch_config(self, location, global_config=None,
1618
1658
                          location_config=None):
1708
1748
        return my_config
1709
1749
 
1710
1750
    def test_user_id(self):
1711
 
        branch = FakeBranch(user_id='Robert Collins <robertc@example.net>')
 
1751
        branch = FakeBranch()
1712
1752
        my_config = config.BranchConfig(branch)
1713
 
        self.assertEqual("Robert Collins <robertc@example.net>",
1714
 
                         my_config.username())
 
1753
        self.assertIsNot(None, my_config.username())
1715
1754
        my_config.branch.control_files.files['email'] = "John"
1716
1755
        my_config.set_user_option('email',
1717
1756
                                  "Robert Collins <robertc@example.org>")
1718
 
        self.assertEqual("John", my_config.username())
1719
 
        del my_config.branch.control_files.files['email']
1720
1757
        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())
 
1758
                        my_config.username())
1729
1759
 
1730
1760
    def test_BZR_EMAIL_OVERRIDES(self):
1731
1761
        self.overrideEnv('BZR_EMAIL', "Robert Collins <robertc@example.org>")
1737
1767
    def test_signatures_forced(self):
1738
1768
        my_config = self.get_branch_config(
1739
1769
            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())
 
1770
        self.assertEqual(config.CHECK_NEVER,
 
1771
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1772
                my_config.signature_checking))
 
1773
        self.assertEqual(config.SIGN_ALWAYS,
 
1774
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1775
                my_config.signing_policy))
 
1776
        self.assertTrue(self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1777
            my_config.signature_needed))
1743
1778
 
1744
1779
    def test_signatures_forced_branch(self):
1745
1780
        my_config = self.get_branch_config(
1746
1781
            global_config=sample_ignore_signatures,
1747
1782
            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())
 
1783
        self.assertEqual(config.CHECK_NEVER,
 
1784
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1785
                my_config.signature_checking))
 
1786
        self.assertEqual(config.SIGN_ALWAYS,
 
1787
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1788
                my_config.signing_policy))
 
1789
        self.assertTrue(self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1790
            my_config.signature_needed))
1751
1791
 
1752
1792
    def test_gpg_signing_command(self):
1753
1793
        my_config = self.get_branch_config(
1754
1794
            global_config=sample_config_text,
1755
1795
            # branch data cannot set gpg_signing_command
1756
1796
            branch_data_config="gpg_signing_command=pgp")
1757
 
        self.assertEqual('gnome-gpg', my_config.gpg_signing_command())
 
1797
        self.assertEqual('gnome-gpg',
 
1798
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1799
                my_config.gpg_signing_command))
1758
1800
 
1759
1801
    def test_get_user_option_global(self):
1760
1802
        my_config = self.get_branch_config(global_config=sample_config_text)
1767
1809
                                      location_config=sample_branches_text)
1768
1810
        self.assertEqual(my_config.branch.base, '/a/c')
1769
1811
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1770
 
                         my_config.post_commit())
 
1812
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1813
                                              my_config.post_commit))
1771
1814
        my_config.set_user_option('post_commit', 'rmtree_root')
1772
1815
        # post-commit is ignored when present in branch data
1773
1816
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1774
 
                         my_config.post_commit())
 
1817
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1818
                                              my_config.post_commit))
1775
1819
        my_config.set_user_option('post_commit', 'rmtree_root',
1776
1820
                                  store=config.STORE_LOCATION)
1777
 
        self.assertEqual('rmtree_root', my_config.post_commit())
 
1821
        self.assertEqual('rmtree_root',
 
1822
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1823
                                              my_config.post_commit))
1778
1824
 
1779
1825
    def test_config_precedence(self):
1780
1826
        # FIXME: eager test, luckily no persitent config file makes it fail
1796
1842
            location='http://example.com/specific')
1797
1843
        self.assertEqual(my_config.get_user_option('option'), 'exact')
1798
1844
 
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
1845
 
1842
1846
class TestMailAddressExtraction(tests.TestCase):
1843
1847
 
1914
1918
        conf = config.TransportConfig(t, 'foo.conf')
1915
1919
        self.assertRaises(errors.ParseConfigError, conf._get_configobj)
1916
1920
 
 
1921
    def test_load_permission_denied(self):
 
1922
        """Ensure we get an empty config file if the file is inaccessible."""
 
1923
        warnings = []
 
1924
        def warning(*args):
 
1925
            warnings.append(args[0] % args[1:])
 
1926
        self.overrideAttr(trace, 'warning', warning)
 
1927
 
 
1928
        class DenyingTransport(object):
 
1929
 
 
1930
            def __init__(self, base):
 
1931
                self.base = base
 
1932
 
 
1933
            def get_bytes(self, relpath):
 
1934
                raise errors.PermissionDenied(relpath, "")
 
1935
 
 
1936
        cfg = config.TransportConfig(
 
1937
            DenyingTransport("nonexisting://"), 'control.conf')
 
1938
        self.assertIs(None, cfg.get_option('non-existant', 'SECTION'))
 
1939
        self.assertEquals(
 
1940
            warnings,
 
1941
            [u'Permission denied while trying to open configuration file '
 
1942
             u'nonexisting:///control.conf.'])
 
1943
 
1917
1944
    def test_get_value(self):
1918
1945
        """Test that retreiving a value from a section is possible"""
1919
1946
        bzrdir_config = config.TransportConfig(self.get_transport('.'),
2107
2134
        self.assertGetHook(remote_branch._get_config(), 'file', 'branch')
2108
2135
 
2109
2136
    def test_get_hook_remote_bzrdir(self):
2110
 
        remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
 
2137
        remote_bzrdir = controldir.ControlDir.open(self.get_url('tree'))
2111
2138
        conf = remote_bzrdir._get_config()
2112
2139
        conf.set_option('remotedir', 'file')
2113
2140
        self.assertGetHook(conf, 'file', 'remotedir')
2135
2162
    def test_set_hook_remote_bzrdir(self):
2136
2163
        remote_branch = branch.Branch.open(self.get_url('tree'))
2137
2164
        self.addCleanup(remote_branch.lock_write().unlock)
2138
 
        remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
 
2165
        remote_bzrdir = controldir.ControlDir.open(self.get_url('tree'))
2139
2166
        self.assertSetHook(remote_bzrdir._get_config(), 'file', 'remotedir')
2140
2167
 
2141
2168
    def assertLoadHook(self, expected_nb_calls, name, conf_class, *conf_args):
2158
2185
        self.assertLoadHook(1, 'file', remote.RemoteBranchConfig, remote_branch)
2159
2186
 
2160
2187
    def test_load_hook_remote_bzrdir(self):
2161
 
        remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
 
2188
        remote_bzrdir = controldir.ControlDir.open(self.get_url('tree'))
2162
2189
        # The config file doesn't exist, set an option to force its creation
2163
2190
        conf = remote_bzrdir._get_config()
2164
2191
        conf.set_option('remotedir', 'file')
2189
2216
    def test_save_hook_remote_bzrdir(self):
2190
2217
        remote_branch = branch.Branch.open(self.get_url('tree'))
2191
2218
        self.addCleanup(remote_branch.lock_write().unlock)
2192
 
        remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
 
2219
        remote_bzrdir = controldir.ControlDir.open(self.get_url('tree'))
2193
2220
        self.assertSaveHook(remote_bzrdir._get_config())
2194
2221
 
2195
2222
 
2199
2226
        opt = config.Option('foo', default='bar')
2200
2227
        self.assertEquals('bar', opt.get_default())
2201
2228
 
 
2229
    def test_callable_default_value(self):
 
2230
        def bar_as_unicode():
 
2231
            return u'bar'
 
2232
        opt = config.Option('foo', default=bar_as_unicode)
 
2233
        self.assertEquals('bar', opt.get_default())
 
2234
 
 
2235
    def test_default_value_from_env(self):
 
2236
        opt = config.Option('foo', default='bar', default_from_env=['FOO'])
 
2237
        self.overrideEnv('FOO', 'quux')
 
2238
        # Env variable provides a default taking over the option one
 
2239
        self.assertEquals('quux', opt.get_default())
 
2240
 
 
2241
    def test_first_default_value_from_env_wins(self):
 
2242
        opt = config.Option('foo', default='bar',
 
2243
                            default_from_env=['NO_VALUE', 'FOO', 'BAZ'])
 
2244
        self.overrideEnv('FOO', 'foo')
 
2245
        self.overrideEnv('BAZ', 'baz')
 
2246
        # The first env var set wins
 
2247
        self.assertEquals('foo', opt.get_default())
 
2248
 
 
2249
    def test_not_supported_list_default_value(self):
 
2250
        self.assertRaises(AssertionError, config.Option, 'foo', default=[1])
 
2251
 
 
2252
    def test_not_supported_object_default_value(self):
 
2253
        self.assertRaises(AssertionError, config.Option, 'foo',
 
2254
                          default=object())
 
2255
 
 
2256
    def test_not_supported_callable_default_value_not_unicode(self):
 
2257
        def bar_not_unicode():
 
2258
            return 'bar'
 
2259
        opt = config.Option('foo', default=bar_not_unicode)
 
2260
        self.assertRaises(AssertionError, opt.get_default)
 
2261
 
 
2262
    def test_get_help_topic(self):
 
2263
        opt = config.Option('foo')
 
2264
        self.assertEquals('foo', opt.get_help_topic())
 
2265
 
 
2266
 
 
2267
class TestOptionConverterMixin(object):
 
2268
 
 
2269
    def assertConverted(self, expected, opt, value):
 
2270
        self.assertEquals(expected, opt.convert_from_unicode(None, value))
 
2271
 
 
2272
    def assertWarns(self, opt, value):
 
2273
        warnings = []
 
2274
        def warning(*args):
 
2275
            warnings.append(args[0] % args[1:])
 
2276
        self.overrideAttr(trace, 'warning', warning)
 
2277
        self.assertEquals(None, opt.convert_from_unicode(None, value))
 
2278
        self.assertLength(1, warnings)
 
2279
        self.assertEquals(
 
2280
            'Value "%s" is not valid for "%s"' % (value, opt.name),
 
2281
            warnings[0])
 
2282
 
 
2283
    def assertErrors(self, opt, value):
 
2284
        self.assertRaises(errors.ConfigOptionValueError,
 
2285
                          opt.convert_from_unicode, None, value)
 
2286
 
 
2287
    def assertConvertInvalid(self, opt, invalid_value):
 
2288
        opt.invalid = None
 
2289
        self.assertEquals(None, opt.convert_from_unicode(None, invalid_value))
 
2290
        opt.invalid = 'warning'
 
2291
        self.assertWarns(opt, invalid_value)
 
2292
        opt.invalid = 'error'
 
2293
        self.assertErrors(opt, invalid_value)
 
2294
 
 
2295
 
 
2296
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
 
2297
 
 
2298
    def get_option(self):
 
2299
        return config.Option('foo', help='A boolean.',
 
2300
                             from_unicode=config.bool_from_store)
 
2301
 
 
2302
    def test_convert_invalid(self):
 
2303
        opt = self.get_option()
 
2304
        # A string that is not recognized as a boolean
 
2305
        self.assertConvertInvalid(opt, u'invalid-boolean')
 
2306
        # A list of strings is never recognized as a boolean
 
2307
        self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
 
2308
 
 
2309
    def test_convert_valid(self):
 
2310
        opt = self.get_option()
 
2311
        self.assertConverted(True, opt, u'True')
 
2312
        self.assertConverted(True, opt, u'1')
 
2313
        self.assertConverted(False, opt, u'False')
 
2314
 
 
2315
 
 
2316
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
 
2317
 
 
2318
    def get_option(self):
 
2319
        return config.Option('foo', help='An integer.',
 
2320
                             from_unicode=config.int_from_store)
 
2321
 
 
2322
    def test_convert_invalid(self):
 
2323
        opt = self.get_option()
 
2324
        # A string that is not recognized as an integer
 
2325
        self.assertConvertInvalid(opt, u'forty-two')
 
2326
        # A list of strings is never recognized as an integer
 
2327
        self.assertConvertInvalid(opt, [u'a', u'list'])
 
2328
 
 
2329
    def test_convert_valid(self):
 
2330
        opt = self.get_option()
 
2331
        self.assertConverted(16, opt, u'16')
 
2332
 
 
2333
 
 
2334
class TestOptionWithSIUnitConverter(tests.TestCase, TestOptionConverterMixin):
 
2335
 
 
2336
    def get_option(self):
 
2337
        return config.Option('foo', help='An integer in SI units.',
 
2338
                             from_unicode=config.int_SI_from_store)
 
2339
 
 
2340
    def test_convert_invalid(self):
 
2341
        opt = self.get_option()
 
2342
        self.assertConvertInvalid(opt, u'not-a-unit')
 
2343
        self.assertConvertInvalid(opt, u'Gb') # Forgot the int
 
2344
        self.assertConvertInvalid(opt, u'1b') # Forgot the unit
 
2345
        self.assertConvertInvalid(opt, u'1GG')
 
2346
        self.assertConvertInvalid(opt, u'1Mbb')
 
2347
        self.assertConvertInvalid(opt, u'1MM')
 
2348
 
 
2349
    def test_convert_valid(self):
 
2350
        opt = self.get_option()
 
2351
        self.assertConverted(int(5e3), opt, u'5kb')
 
2352
        self.assertConverted(int(5e6), opt, u'5M')
 
2353
        self.assertConverted(int(5e6), opt, u'5MB')
 
2354
        self.assertConverted(int(5e9), opt, u'5g')
 
2355
        self.assertConverted(int(5e9), opt, u'5gB')
 
2356
        self.assertConverted(100, opt, u'100')
 
2357
 
 
2358
 
 
2359
class TestListOption(tests.TestCase, TestOptionConverterMixin):
 
2360
 
 
2361
    def get_option(self):
 
2362
        return config.ListOption('foo', help='A list.')
 
2363
 
 
2364
    def test_convert_invalid(self):
 
2365
        opt = self.get_option()
 
2366
        # We don't even try to convert a list into a list, we only expect
 
2367
        # strings
 
2368
        self.assertConvertInvalid(opt, [1])
 
2369
        # No string is invalid as all forms can be converted to a list
 
2370
 
 
2371
    def test_convert_valid(self):
 
2372
        opt = self.get_option()
 
2373
        # An empty string is an empty list
 
2374
        self.assertConverted([], opt, '') # Using a bare str() just in case
 
2375
        self.assertConverted([], opt, u'')
 
2376
        # A boolean
 
2377
        self.assertConverted([u'True'], opt, u'True')
 
2378
        # An integer
 
2379
        self.assertConverted([u'42'], opt, u'42')
 
2380
        # A single string
 
2381
        self.assertConverted([u'bar'], opt, u'bar')
 
2382
 
 
2383
 
 
2384
class TestRegistryOption(tests.TestCase, TestOptionConverterMixin):
 
2385
 
 
2386
    def get_option(self, registry):
 
2387
        return config.RegistryOption('foo', registry,
 
2388
                help='A registry option.')
 
2389
 
 
2390
    def test_convert_invalid(self):
 
2391
        registry = _mod_registry.Registry()
 
2392
        opt = self.get_option(registry)
 
2393
        self.assertConvertInvalid(opt, [1])
 
2394
        self.assertConvertInvalid(opt, u"notregistered")
 
2395
 
 
2396
    def test_convert_valid(self):
 
2397
        registry = _mod_registry.Registry()
 
2398
        registry.register("someval", 1234)
 
2399
        opt = self.get_option(registry)
 
2400
        # Using a bare str() just in case
 
2401
        self.assertConverted(1234, opt, "someval")
 
2402
        self.assertConverted(1234, opt, u'someval')
 
2403
        self.assertConverted(None, opt, None)
 
2404
 
 
2405
    def test_help(self):
 
2406
        registry = _mod_registry.Registry()
 
2407
        registry.register("someval", 1234, help="some option")
 
2408
        registry.register("dunno", 1234, help="some other option")
 
2409
        opt = self.get_option(registry)
 
2410
        self.assertEquals(
 
2411
            'A registry option.\n'
 
2412
            '\n'
 
2413
            'The following values are supported:\n'
 
2414
            ' dunno - some other option\n'
 
2415
            ' someval - some option\n',
 
2416
            opt.help)
 
2417
 
 
2418
    def test_get_help_text(self):
 
2419
        registry = _mod_registry.Registry()
 
2420
        registry.register("someval", 1234, help="some option")
 
2421
        registry.register("dunno", 1234, help="some other option")
 
2422
        opt = self.get_option(registry)
 
2423
        self.assertEquals(
 
2424
            'A registry option.\n'
 
2425
            '\n'
 
2426
            'The following values are supported:\n'
 
2427
            ' dunno - some other option\n'
 
2428
            ' someval - some option\n',
 
2429
            opt.get_help_text())
 
2430
 
2202
2431
 
2203
2432
class TestOptionRegistry(tests.TestCase):
2204
2433
 
2205
2434
    def setUp(self):
2206
2435
        super(TestOptionRegistry, self).setUp()
2207
2436
        # Always start with an empty registry
2208
 
        self.overrideAttr(config, 'option_registry', registry.Registry())
 
2437
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2209
2438
        self.registry = config.option_registry
2210
2439
 
2211
2440
    def test_register(self):
2212
2441
        opt = config.Option('foo')
2213
 
        self.registry.register('foo', opt)
 
2442
        self.registry.register(opt)
2214
2443
        self.assertIs(opt, self.registry.get('foo'))
2215
2444
 
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
2445
    def test_registered_help(self):
2224
 
        opt = config.Option('foo')
2225
 
        self.registry.register('foo', opt, help='A simple option')
 
2446
        opt = config.Option('foo', help='A simple option')
 
2447
        self.registry.register(opt)
2226
2448
        self.assertEquals('A simple option', self.registry.get_help('foo'))
2227
2449
 
 
2450
    lazy_option = config.Option('lazy_foo', help='Lazy help')
 
2451
 
 
2452
    def test_register_lazy(self):
 
2453
        self.registry.register_lazy('lazy_foo', self.__module__,
 
2454
                                    'TestOptionRegistry.lazy_option')
 
2455
        self.assertIs(self.lazy_option, self.registry.get('lazy_foo'))
 
2456
 
 
2457
    def test_registered_lazy_help(self):
 
2458
        self.registry.register_lazy('lazy_foo', self.__module__,
 
2459
                                    'TestOptionRegistry.lazy_option')
 
2460
        self.assertEquals('Lazy help', self.registry.get_help('lazy_foo'))
 
2461
 
2228
2462
 
2229
2463
class TestRegisteredOptions(tests.TestCase):
2230
2464
    """All registered options should verify some constraints."""
2244
2478
    def test_help_is_set(self):
2245
2479
        option_help = self.registry.get_help(self.option_name)
2246
2480
        self.assertNotEquals(None, option_help)
2247
 
        # Come on, think about the user, he really wants to know whst the
 
2481
        # Come on, think about the user, he really wants to know what the
2248
2482
        # option is about
 
2483
        self.assertIsNot(None, option_help)
2249
2484
        self.assertNotEquals('', option_help)
2250
2485
 
2251
2486
 
2273
2508
 
2274
2509
class TestMutableSection(tests.TestCase):
2275
2510
 
2276
 
    # FIXME: Parametrize so that all sections (including os.environ and the
2277
 
    # ones produced by Stores) run these tests -- vila 2011-04-01
 
2511
    scenarios = [('mutable',
 
2512
                  {'get_section':
 
2513
                       lambda opts: config.MutableSection('myID', opts)},),
 
2514
        ]
2278
2515
 
2279
2516
    def test_set(self):
2280
2517
        a_dict = dict(foo='bar')
2281
 
        section = config.MutableSection('myID', a_dict)
 
2518
        section = self.get_section(a_dict)
2282
2519
        section.set('foo', 'new_value')
2283
2520
        self.assertEquals('new_value', section.get('foo'))
2284
2521
        # The change appears in the shared section
2289
2526
 
2290
2527
    def test_set_preserve_original_once(self):
2291
2528
        a_dict = dict(foo='bar')
2292
 
        section = config.MutableSection('myID', a_dict)
 
2529
        section = self.get_section(a_dict)
2293
2530
        section.set('foo', 'first_value')
2294
2531
        section.set('foo', 'second_value')
2295
2532
        # We keep track of the original value
2298
2535
 
2299
2536
    def test_remove(self):
2300
2537
        a_dict = dict(foo='bar')
2301
 
        section = config.MutableSection('myID', a_dict)
 
2538
        section = self.get_section(a_dict)
2302
2539
        section.remove('foo')
2303
2540
        # We get None for unknown options via the default value
2304
2541
        self.assertEquals(None, section.get('foo'))
2311
2548
 
2312
2549
    def test_remove_new_option(self):
2313
2550
        a_dict = dict()
2314
 
        section = config.MutableSection('myID', a_dict)
 
2551
        section = self.get_section(a_dict)
2315
2552
        section.set('foo', 'bar')
2316
2553
        section.remove('foo')
2317
2554
        self.assertFalse('foo' in section.options)
2321
2558
        self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
2322
2559
 
2323
2560
 
 
2561
class TestCommandLineStore(tests.TestCase):
 
2562
 
 
2563
    def setUp(self):
 
2564
        super(TestCommandLineStore, self).setUp()
 
2565
        self.store = config.CommandLineStore()
 
2566
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
 
2567
 
 
2568
    def get_section(self):
 
2569
        """Get the unique section for the command line overrides."""
 
2570
        sections = list(self.store.get_sections())
 
2571
        self.assertLength(1, sections)
 
2572
        store, section = sections[0]
 
2573
        self.assertEquals(self.store, store)
 
2574
        return section
 
2575
 
 
2576
    def test_no_override(self):
 
2577
        self.store._from_cmdline([])
 
2578
        section = self.get_section()
 
2579
        self.assertLength(0, list(section.iter_option_names()))
 
2580
 
 
2581
    def test_simple_override(self):
 
2582
        self.store._from_cmdline(['a=b'])
 
2583
        section = self.get_section()
 
2584
        self.assertEqual('b', section.get('a'))
 
2585
 
 
2586
    def test_list_override(self):
 
2587
        opt = config.ListOption('l')
 
2588
        config.option_registry.register(opt)
 
2589
        self.store._from_cmdline(['l=1,2,3'])
 
2590
        val = self.get_section().get('l')
 
2591
        self.assertEqual('1,2,3', val)
 
2592
        # Reminder: lists should be registered as such explicitely, otherwise
 
2593
        # the conversion needs to be done afterwards.
 
2594
        self.assertEqual(['1', '2', '3'],
 
2595
                         opt.convert_from_unicode(self.store, val))
 
2596
 
 
2597
    def test_multiple_overrides(self):
 
2598
        self.store._from_cmdline(['a=b', 'x=y'])
 
2599
        section = self.get_section()
 
2600
        self.assertEquals('b', section.get('a'))
 
2601
        self.assertEquals('y', section.get('x'))
 
2602
 
 
2603
    def test_wrong_syntax(self):
 
2604
        self.assertRaises(errors.BzrCommandError,
 
2605
                          self.store._from_cmdline, ['a=b', 'c'])
 
2606
 
 
2607
class TestStoreMinimalAPI(tests.TestCaseWithTransport):
 
2608
 
 
2609
    scenarios = [(key, {'get_store': builder}) for key, builder
 
2610
                 in config.test_store_builder_registry.iteritems()] + [
 
2611
        ('cmdline', {'get_store': lambda test: config.CommandLineStore()})]
 
2612
 
 
2613
    def test_id(self):
 
2614
        store = self.get_store(self)
 
2615
        if type(store) == config.TransportIniFileStore:
 
2616
            raise tests.TestNotApplicable(
 
2617
                "%s is not a concrete Store implementation"
 
2618
                " so it doesn't need an id" % (store.__class__.__name__,))
 
2619
        self.assertIsNot(None, store.id)
 
2620
 
 
2621
 
2324
2622
class TestStore(tests.TestCaseWithTransport):
2325
2623
 
2326
 
    def assertSectionContent(self, expected, section):
 
2624
    def assertSectionContent(self, expected, (store, section)):
2327
2625
        """Assert that some options have the proper values in a section."""
2328
2626
        expected_name, expected_options = expected
2329
2627
        self.assertEquals(expected_name, section.id)
2337
2635
    scenarios = [(key, {'get_store': builder}) for key, builder
2338
2636
                 in config.test_store_builder_registry.iteritems()]
2339
2637
 
2340
 
    def setUp(self):
2341
 
        super(TestReadonlyStore, self).setUp()
2342
 
 
2343
2638
    def test_building_delays_load(self):
2344
2639
        store = self.get_store(self)
2345
2640
        self.assertEquals(False, store.is_loaded())
2371
2666
        self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2372
2667
 
2373
2668
 
 
2669
class TestStoreQuoting(TestStore):
 
2670
 
 
2671
    scenarios = [(key, {'get_store': builder}) for key, builder
 
2672
                 in config.test_store_builder_registry.iteritems()]
 
2673
 
 
2674
    def setUp(self):
 
2675
        super(TestStoreQuoting, self).setUp()
 
2676
        self.store = self.get_store(self)
 
2677
        # We need a loaded store but any content will do
 
2678
        self.store._load_from_string('')
 
2679
 
 
2680
    def assertIdempotent(self, s):
 
2681
        """Assert that quoting an unquoted string is a no-op and vice-versa.
 
2682
 
 
2683
        What matters here is that option values, as they appear in a store, can
 
2684
        be safely round-tripped out of the store and back.
 
2685
 
 
2686
        :param s: A string, quoted if required.
 
2687
        """
 
2688
        self.assertEquals(s, self.store.quote(self.store.unquote(s)))
 
2689
        self.assertEquals(s, self.store.unquote(self.store.quote(s)))
 
2690
 
 
2691
    def test_empty_string(self):
 
2692
        if isinstance(self.store, config.IniFileStore):
 
2693
            # configobj._quote doesn't handle empty values
 
2694
            self.assertRaises(AssertionError,
 
2695
                              self.assertIdempotent, '')
 
2696
        else:
 
2697
            self.assertIdempotent('')
 
2698
        # But quoted empty strings are ok
 
2699
        self.assertIdempotent('""')
 
2700
 
 
2701
    def test_embedded_spaces(self):
 
2702
        self.assertIdempotent('" a b c "')
 
2703
 
 
2704
    def test_embedded_commas(self):
 
2705
        self.assertIdempotent('" a , b c "')
 
2706
 
 
2707
    def test_simple_comma(self):
 
2708
        if isinstance(self.store, config.IniFileStore):
 
2709
            # configobj requires that lists are special-cased
 
2710
           self.assertRaises(AssertionError,
 
2711
                             self.assertIdempotent, ',')
 
2712
        else:
 
2713
            self.assertIdempotent(',')
 
2714
        # When a single comma is required, quoting is also required
 
2715
        self.assertIdempotent('","')
 
2716
 
 
2717
    def test_list(self):
 
2718
        if isinstance(self.store, config.IniFileStore):
 
2719
            # configobj requires that lists are special-cased
 
2720
            self.assertRaises(AssertionError,
 
2721
                              self.assertIdempotent, 'a,b')
 
2722
        else:
 
2723
            self.assertIdempotent('a,b')
 
2724
 
 
2725
 
 
2726
class TestDictFromStore(tests.TestCase):
 
2727
 
 
2728
    def test_unquote_not_string(self):
 
2729
        conf = config.MemoryStack('x=2\n[a_section]\na=1\n')
 
2730
        value = conf.get('a_section')
 
2731
        # Urgh, despite 'conf' asking for the no-name section, we get the
 
2732
        # content of another section as a dict o_O
 
2733
        self.assertEquals({'a': '1'}, value)
 
2734
        unquoted = conf.store.unquote(value)
 
2735
        # Which cannot be unquoted but shouldn't crash either (the use cases
 
2736
        # are getting the value or displaying it. In the later case, '%s' will
 
2737
        # do).
 
2738
        self.assertEquals({'a': '1'}, unquoted)
 
2739
        self.assertEquals("{u'a': u'1'}", '%s' % (unquoted,))
 
2740
 
 
2741
 
2374
2742
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2375
 
    """Simulate loading a config store without content of various encodings.
 
2743
    """Simulate loading a config store with content of various encodings.
2376
2744
 
2377
2745
    All files produced by bzr are in utf8 content.
2378
2746
 
2391
2759
        utf8_content = unicode_content.encode('utf8')
2392
2760
        # Store the raw content in the config file
2393
2761
        t.put_bytes('foo.conf', utf8_content)
2394
 
        store = config.IniFileStore(t, 'foo.conf')
 
2762
        store = config.TransportIniFileStore(t, 'foo.conf')
2395
2763
        store.load()
2396
2764
        stack = config.Stack([store.get_sections], store)
2397
2765
        self.assertEquals(unicode_user, stack.get('user'))
2400
2768
        """Ensure we display a proper error on non-ascii, non utf-8 content."""
2401
2769
        t = self.get_transport()
2402
2770
        t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2403
 
        store = config.IniFileStore(t, 'foo.conf')
 
2771
        store = config.TransportIniFileStore(t, 'foo.conf')
2404
2772
        self.assertRaises(errors.ConfigContentError, store.load)
2405
2773
 
2406
2774
    def test_load_erroneous_content(self):
2407
2775
        """Ensure we display a proper error on content that can't be parsed."""
2408
2776
        t = self.get_transport()
2409
2777
        t.put_bytes('foo.conf', '[open_section\n')
2410
 
        store = config.IniFileStore(t, 'foo.conf')
 
2778
        store = config.TransportIniFileStore(t, 'foo.conf')
2411
2779
        self.assertRaises(errors.ParseConfigError, store.load)
2412
2780
 
 
2781
    def test_load_permission_denied(self):
 
2782
        """Ensure we get warned when trying to load an inaccessible file."""
 
2783
        warnings = []
 
2784
        def warning(*args):
 
2785
            warnings.append(args[0] % args[1:])
 
2786
        self.overrideAttr(trace, 'warning', warning)
 
2787
 
 
2788
        t = self.get_transport()
 
2789
 
 
2790
        def get_bytes(relpath):
 
2791
            raise errors.PermissionDenied(relpath, "")
 
2792
        t.get_bytes = get_bytes
 
2793
        store = config.TransportIniFileStore(t, 'foo.conf')
 
2794
        self.assertRaises(errors.PermissionDenied, store.load)
 
2795
        self.assertEquals(
 
2796
            warnings,
 
2797
            [u'Permission denied while trying to load configuration store %s.'
 
2798
             % store.external_url()])
 
2799
 
2413
2800
 
2414
2801
class TestIniConfigContent(tests.TestCaseWithTransport):
2415
 
    """Simulate loading a IniBasedConfig without content of various encodings.
 
2802
    """Simulate loading a IniBasedConfig with content of various encodings.
2416
2803
 
2417
2804
    All files produced by bzr are in utf8 content.
2418
2805
 
2476
2863
    def test_save_emptied_succeeds(self):
2477
2864
        store = self.get_store(self)
2478
2865
        store._load_from_string('foo=bar\n')
 
2866
        # FIXME: There should be a better way than relying on the test
 
2867
        # parametrization to identify branch.conf -- vila 2011-0526
 
2868
        if self.store_id in ('branch', 'remote_branch'):
 
2869
            # branch stores requires write locked branches
 
2870
            self.addCleanup(store.branch.lock_write().unlock)
2479
2871
        section = store.get_mutable_section(None)
2480
2872
        section.remove('foo')
2481
2873
        store.save()
2502
2894
 
2503
2895
    def test_set_option_in_empty_store(self):
2504
2896
        store = self.get_store(self)
 
2897
        # FIXME: There should be a better way than relying on the test
 
2898
        # parametrization to identify branch.conf -- vila 2011-0526
 
2899
        if self.store_id in ('branch', 'remote_branch'):
 
2900
            # branch stores requires write locked branches
 
2901
            self.addCleanup(store.branch.lock_write().unlock)
2505
2902
        section = store.get_mutable_section(None)
2506
2903
        section.set('foo', 'bar')
2507
2904
        store.save()
2513
2910
    def test_set_option_in_default_section(self):
2514
2911
        store = self.get_store(self)
2515
2912
        store._load_from_string('')
 
2913
        # FIXME: There should be a better way than relying on the test
 
2914
        # parametrization to identify branch.conf -- vila 2011-0526
 
2915
        if self.store_id in ('branch', 'remote_branch'):
 
2916
            # branch stores requires write locked branches
 
2917
            self.addCleanup(store.branch.lock_write().unlock)
2516
2918
        section = store.get_mutable_section(None)
2517
2919
        section.set('foo', 'bar')
2518
2920
        store.save()
2524
2926
    def test_set_option_in_named_section(self):
2525
2927
        store = self.get_store(self)
2526
2928
        store._load_from_string('')
 
2929
        # FIXME: There should be a better way than relying on the test
 
2930
        # parametrization to identify branch.conf -- vila 2011-0526
 
2931
        if self.store_id in ('branch', 'remote_branch'):
 
2932
            # branch stores requires write locked branches
 
2933
            self.addCleanup(store.branch.lock_write().unlock)
2527
2934
        section = store.get_mutable_section('baz')
2528
2935
        section.set('foo', 'bar')
2529
2936
        store.save()
2533
2940
        self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2534
2941
 
2535
2942
    def test_load_hook(self):
2536
 
        # We first needs to ensure that the store exists
 
2943
        # First, we need to ensure that the store exists
2537
2944
        store = self.get_store(self)
 
2945
        # FIXME: There should be a better way than relying on the test
 
2946
        # parametrization to identify branch.conf -- vila 2011-0526
 
2947
        if self.store_id in ('branch', 'remote_branch'):
 
2948
            # branch stores requires write locked branches
 
2949
            self.addCleanup(store.branch.lock_write().unlock)
2538
2950
        section = store.get_mutable_section('baz')
2539
2951
        section.set('foo', 'bar')
2540
2952
        store.save()
2556
2968
        config.ConfigHooks.install_named_hook('save', hook, None)
2557
2969
        self.assertLength(0, calls)
2558
2970
        store = self.get_store(self)
 
2971
        # FIXME: There should be a better way than relying on the test
 
2972
        # parametrization to identify branch.conf -- vila 2011-0526
 
2973
        if self.store_id in ('branch', 'remote_branch'):
 
2974
            # branch stores requires write locked branches
 
2975
            self.addCleanup(store.branch.lock_write().unlock)
2559
2976
        section = store.get_mutable_section('baz')
2560
2977
        section.set('foo', 'bar')
2561
2978
        store.save()
2562
2979
        self.assertLength(1, calls)
2563
2980
        self.assertEquals((store,), calls[0])
2564
2981
 
2565
 
 
2566
 
class TestIniFileStore(TestStore):
 
2982
    def test_set_mark_dirty(self):
 
2983
        stack = config.MemoryStack('')
 
2984
        self.assertLength(0, stack.store.dirty_sections)
 
2985
        stack.set('foo', 'baz')
 
2986
        self.assertLength(1, stack.store.dirty_sections)
 
2987
        self.assertTrue(stack.store._need_saving())
 
2988
 
 
2989
    def test_remove_mark_dirty(self):
 
2990
        stack = config.MemoryStack('foo=bar')
 
2991
        self.assertLength(0, stack.store.dirty_sections)
 
2992
        stack.remove('foo')
 
2993
        self.assertLength(1, stack.store.dirty_sections)
 
2994
        self.assertTrue(stack.store._need_saving())
 
2995
 
 
2996
 
 
2997
class TestStoreSaveChanges(tests.TestCaseWithTransport):
 
2998
    """Tests that config changes are kept in memory and saved on-demand."""
 
2999
 
 
3000
    def setUp(self):
 
3001
        super(TestStoreSaveChanges, self).setUp()
 
3002
        self.transport = self.get_transport()
 
3003
        # Most of the tests involve two stores pointing to the same persistent
 
3004
        # storage to observe the effects of concurrent changes
 
3005
        self.st1 = config.TransportIniFileStore(self.transport, 'foo.conf')
 
3006
        self.st2 = config.TransportIniFileStore(self.transport, 'foo.conf')
 
3007
        self.warnings = []
 
3008
        def warning(*args):
 
3009
            self.warnings.append(args[0] % args[1:])
 
3010
        self.overrideAttr(trace, 'warning', warning)
 
3011
 
 
3012
    def has_store(self, store):
 
3013
        store_basename = urlutils.relative_url(self.transport.external_url(),
 
3014
                                               store.external_url())
 
3015
        return self.transport.has(store_basename)
 
3016
 
 
3017
    def get_stack(self, store):
 
3018
        # Any stack will do as long as it uses the right store, just a single
 
3019
        # no-name section is enough
 
3020
        return config.Stack([store.get_sections], store)
 
3021
 
 
3022
    def test_no_changes_no_save(self):
 
3023
        s = self.get_stack(self.st1)
 
3024
        s.store.save_changes()
 
3025
        self.assertEquals(False, self.has_store(self.st1))
 
3026
 
 
3027
    def test_unrelated_concurrent_update(self):
 
3028
        s1 = self.get_stack(self.st1)
 
3029
        s2 = self.get_stack(self.st2)
 
3030
        s1.set('foo', 'bar')
 
3031
        s2.set('baz', 'quux')
 
3032
        s1.store.save()
 
3033
        # Changes don't propagate magically
 
3034
        self.assertEquals(None, s1.get('baz'))
 
3035
        s2.store.save_changes()
 
3036
        self.assertEquals('quux', s2.get('baz'))
 
3037
        # Changes are acquired when saving
 
3038
        self.assertEquals('bar', s2.get('foo'))
 
3039
        # Since there is no overlap, no warnings are emitted
 
3040
        self.assertLength(0, self.warnings)
 
3041
 
 
3042
    def test_concurrent_update_modified(self):
 
3043
        s1 = self.get_stack(self.st1)
 
3044
        s2 = self.get_stack(self.st2)
 
3045
        s1.set('foo', 'bar')
 
3046
        s2.set('foo', 'baz')
 
3047
        s1.store.save()
 
3048
        # Last speaker wins
 
3049
        s2.store.save_changes()
 
3050
        self.assertEquals('baz', s2.get('foo'))
 
3051
        # But the user get a warning
 
3052
        self.assertLength(1, self.warnings)
 
3053
        warning = self.warnings[0]
 
3054
        self.assertStartsWith(warning, 'Option foo in section None')
 
3055
        self.assertEndsWith(warning, 'was changed from <CREATED> to bar.'
 
3056
                            ' The baz value will be saved.')
 
3057
 
 
3058
    def test_concurrent_deletion(self):
 
3059
        self.st1._load_from_string('foo=bar')
 
3060
        self.st1.save()
 
3061
        s1 = self.get_stack(self.st1)
 
3062
        s2 = self.get_stack(self.st2)
 
3063
        s1.remove('foo')
 
3064
        s2.remove('foo')
 
3065
        s1.store.save_changes()
 
3066
        # No warning yet
 
3067
        self.assertLength(0, self.warnings)
 
3068
        s2.store.save_changes()
 
3069
        # Now we get one
 
3070
        self.assertLength(1, self.warnings)
 
3071
        warning = self.warnings[0]
 
3072
        self.assertStartsWith(warning, 'Option foo in section None')
 
3073
        self.assertEndsWith(warning, 'was changed from bar to <CREATED>.'
 
3074
                            ' The <DELETED> value will be saved.')
 
3075
 
 
3076
 
 
3077
class TestQuotingIniFileStore(tests.TestCaseWithTransport):
 
3078
 
 
3079
    def get_store(self):
 
3080
        return config.TransportIniFileStore(self.get_transport(), 'foo.conf')
 
3081
 
 
3082
    def test_get_quoted_string(self):
 
3083
        store = self.get_store()
 
3084
        store._load_from_string('foo= " abc "')
 
3085
        stack = config.Stack([store.get_sections])
 
3086
        self.assertEquals(' abc ', stack.get('foo'))
 
3087
 
 
3088
    def test_set_quoted_string(self):
 
3089
        store = self.get_store()
 
3090
        stack = config.Stack([store.get_sections], store)
 
3091
        stack.set('foo', ' a b c ')
 
3092
        store.save()
 
3093
        self.assertFileEqual('foo = " a b c "' + os.linesep, 'foo.conf')
 
3094
 
 
3095
 
 
3096
class TestTransportIniFileStore(TestStore):
2567
3097
 
2568
3098
    def test_loading_unknown_file_fails(self):
2569
 
        store = config.IniFileStore(self.get_transport(), 'I-do-not-exist')
 
3099
        store = config.TransportIniFileStore(self.get_transport(),
 
3100
            'I-do-not-exist')
2570
3101
        self.assertRaises(errors.NoSuchFile, store.load)
2571
3102
 
2572
3103
    def test_invalid_content(self):
2573
 
        store = config.IniFileStore(self.get_transport(), 'foo.conf', )
 
3104
        store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
2574
3105
        self.assertEquals(False, store.is_loaded())
2575
3106
        exc = self.assertRaises(
2576
3107
            errors.ParseConfigError, store._load_from_string,
2584
3115
        # option names share the same name space...)
2585
3116
        # FIXME: This should be fixed by forbidding dicts as values ?
2586
3117
        # -- vila 2011-04-05
2587
 
        store = config.IniFileStore(self.get_transport(), 'foo.conf', )
 
3118
        store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
2588
3119
        store._load_from_string('''
2589
3120
foo=bar
2590
3121
l=1,2
2600
3131
        sections = list(store.get_sections())
2601
3132
        self.assertLength(4, sections)
2602
3133
        # The default section has no name.
2603
 
        # List values are provided as lists
2604
 
        self.assertSectionContent((None, {'foo': 'bar', 'l': ['1', '2']}),
 
3134
        # List values are provided as strings and need to be explicitly
 
3135
        # converted by specifying from_unicode=list_from_store at option
 
3136
        # registration
 
3137
        self.assertSectionContent((None, {'foo': 'bar', 'l': u'1,2'}),
2605
3138
                                  sections[0])
2606
3139
        self.assertSectionContent(
2607
3140
            ('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
2637
3170
 
2638
3171
    def setUp(self):
2639
3172
        super(TestConcurrentStoreUpdates, self).setUp()
2640
 
        self._content = 'one=1\ntwo=2\n'
2641
3173
        self.stack = self.get_stack(self)
2642
3174
        if not isinstance(self.stack, config._CompatibleStack):
2643
3175
            raise tests.TestNotApplicable(
2644
3176
                '%s is not meant to be compatible with the old config design'
2645
3177
                % (self.stack,))
2646
 
        self.stack.store._load_from_string(self._content)
 
3178
        self.stack.set('one', '1')
 
3179
        self.stack.set('two', '2')
2647
3180
        # Flush the store
2648
3181
        self.stack.store.save()
2649
3182
 
2753
3286
    # FIXME: It may be worth looking into removing the lock dir when it's not
2754
3287
    # needed anymore and look at possible fallouts for concurrent lockers. This
2755
3288
    # will matter if/when we use config files outside of bazaar directories
2756
 
    # (.bazaar or .bzr) -- vila 20110-04-11
 
3289
    # (.bazaar or .bzr) -- vila 20110-04-111
2757
3290
 
2758
3291
 
2759
3292
class TestSectionMatcher(TestStore):
2760
3293
 
2761
 
    scenarios = [('location', {'matcher': config.LocationMatcher})]
 
3294
    scenarios = [('location', {'matcher': config.LocationMatcher}),
 
3295
                 ('id', {'matcher': config.NameMatcher}),]
2762
3296
 
2763
 
    def get_store(self, file_name):
2764
 
        return config.IniFileStore(self.get_readonly_transport(), file_name)
 
3297
    def setUp(self):
 
3298
        super(TestSectionMatcher, self).setUp()
 
3299
        # Any simple store is good enough
 
3300
        self.get_store = config.test_store_builder_registry.get('configobj')
2765
3301
 
2766
3302
    def test_no_matches_for_empty_stores(self):
2767
 
        store = self.get_store('foo.conf')
 
3303
        store = self.get_store(self)
2768
3304
        store._load_from_string('')
2769
3305
        matcher = self.matcher(store, '/bar')
2770
3306
        self.assertEquals([], list(matcher.get_sections()))
2771
3307
 
2772
3308
    def test_build_doesnt_load_store(self):
2773
 
        store = self.get_store('foo.conf')
 
3309
        store = self.get_store(self)
2774
3310
        matcher = self.matcher(store, '/bar')
2775
3311
        self.assertFalse(store.is_loaded())
2776
3312
 
2779
3315
 
2780
3316
    def get_section(self, options, extra_path):
2781
3317
        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)
 
3318
        return config.LocationSection(section, extra_path)
2784
3319
 
2785
3320
    def test_simple_option(self):
2786
3321
        section = self.get_section({'foo': 'bar'}, '')
2800
3335
 
2801
3336
class TestLocationMatcher(TestStore):
2802
3337
 
2803
 
    def get_store(self, file_name):
2804
 
        return config.IniFileStore(self.get_readonly_transport(), file_name)
 
3338
    def setUp(self):
 
3339
        super(TestLocationMatcher, self).setUp()
 
3340
        # Any simple store is good enough
 
3341
        self.get_store = config.test_store_builder_registry.get('configobj')
 
3342
 
 
3343
    def test_unrelated_section_excluded(self):
 
3344
        store = self.get_store(self)
 
3345
        store._load_from_string('''
 
3346
[/foo]
 
3347
section=/foo
 
3348
[/foo/baz]
 
3349
section=/foo/baz
 
3350
[/foo/bar]
 
3351
section=/foo/bar
 
3352
[/foo/bar/baz]
 
3353
section=/foo/bar/baz
 
3354
[/quux/quux]
 
3355
section=/quux/quux
 
3356
''')
 
3357
        self.assertEquals(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
 
3358
                           '/quux/quux'],
 
3359
                          [section.id for _, section in store.get_sections()])
 
3360
        matcher = config.LocationMatcher(store, '/foo/bar/quux')
 
3361
        sections = [section for _, section in matcher.get_sections()]
 
3362
        self.assertEquals(['/foo/bar', '/foo'],
 
3363
                          [section.id for section in sections])
 
3364
        self.assertEquals(['quux', 'bar/quux'],
 
3365
                          [section.extra_path for section in sections])
2805
3366
 
2806
3367
    def test_more_specific_sections_first(self):
2807
 
        store = self.get_store('foo.conf')
 
3368
        store = self.get_store(self)
2808
3369
        store._load_from_string('''
2809
3370
[/foo]
2810
3371
section=/foo
2812
3373
section=/foo/bar
2813
3374
''')
2814
3375
        self.assertEquals(['/foo', '/foo/bar'],
2815
 
                          [section.id for section in store.get_sections()])
 
3376
                          [section.id for _, section in store.get_sections()])
2816
3377
        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])
 
3378
        sections = [section for _, section in matcher.get_sections()]
2820
3379
        self.assertEquals(['/foo/bar', '/foo'],
2821
3380
                          [section.id for section in sections])
2822
3381
        self.assertEquals(['baz', 'bar/baz'],
2825
3384
    def test_appendpath_in_no_name_section(self):
2826
3385
        # It's a bit weird to allow appendpath in a no-name section, but
2827
3386
        # someone may found a use for it
2828
 
        store = self.get_store('foo.conf')
 
3387
        store = self.get_store(self)
2829
3388
        store._load_from_string('''
2830
3389
foo=bar
2831
3390
foo:policy = appendpath
2833
3392
        matcher = config.LocationMatcher(store, 'dir/subdir')
2834
3393
        sections = list(matcher.get_sections())
2835
3394
        self.assertLength(1, sections)
2836
 
        self.assertEquals('bar/dir/subdir', sections[0].get('foo'))
 
3395
        self.assertEquals('bar/dir/subdir', sections[0][1].get('foo'))
2837
3396
 
2838
3397
    def test_file_urls_are_normalized(self):
2839
 
        store = self.get_store('foo.conf')
 
3398
        store = self.get_store(self)
2840
3399
        if sys.platform == 'win32':
2841
3400
            expected_url = 'file:///C:/dir/subdir'
2842
3401
            expected_location = 'C:/dir/subdir'
2847
3406
        self.assertEquals(expected_location, matcher.location)
2848
3407
 
2849
3408
 
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'))
 
3409
class TestStartingPathMatcher(TestStore):
 
3410
 
 
3411
    def setUp(self):
 
3412
        super(TestStartingPathMatcher, self).setUp()
 
3413
        # Any simple store is good enough
 
3414
        self.store = config.IniFileStore()
 
3415
 
 
3416
    def assertSectionIDs(self, expected, location, content):
 
3417
        self.store._load_from_string(content)
 
3418
        matcher = config.StartingPathMatcher(self.store, location)
 
3419
        sections = list(matcher.get_sections())
 
3420
        self.assertLength(len(expected), sections)
 
3421
        self.assertEqual(expected, [section.id for _, section in sections])
 
3422
        return sections
 
3423
 
 
3424
    def test_empty(self):
 
3425
        self.assertSectionIDs([], self.get_url(), '')
 
3426
 
 
3427
    def test_url_vs_local_paths(self):
 
3428
        # The matcher location is an url and the section names are local paths
 
3429
        sections = self.assertSectionIDs(['/foo/bar', '/foo'],
 
3430
                                         'file:///foo/bar/baz', '''\
 
3431
[/foo]
 
3432
[/foo/bar]
 
3433
''')
 
3434
 
 
3435
    def test_local_path_vs_url(self):
 
3436
        # The matcher location is a local path and the section names are urls
 
3437
        sections = self.assertSectionIDs(['file:///foo/bar', 'file:///foo'],
 
3438
                                         '/foo/bar/baz', '''\
 
3439
[file:///foo]
 
3440
[file:///foo/bar]
 
3441
''')
 
3442
 
 
3443
 
 
3444
    def test_no_name_section_included_when_present(self):
 
3445
        # Note that other tests will cover the case where the no-name section
 
3446
        # is empty and as such, not included.
 
3447
        sections = self.assertSectionIDs(['/foo/bar', '/foo', None],
 
3448
                                         '/foo/bar/baz', '''\
 
3449
option = defined so the no-name section exists
 
3450
[/foo]
 
3451
[/foo/bar]
 
3452
''')
 
3453
        self.assertEquals(['baz', 'bar/baz', '/foo/bar/baz'],
 
3454
                          [s.locals['relpath'] for _, s in sections])
 
3455
 
 
3456
    def test_order_reversed(self):
 
3457
        self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
 
3458
[/foo]
 
3459
[/foo/bar]
 
3460
''')
 
3461
 
 
3462
    def test_unrelated_section_excluded(self):
 
3463
        self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
 
3464
[/foo]
 
3465
[/foo/qux]
 
3466
[/foo/bar]
 
3467
''')
 
3468
 
 
3469
    def test_glob_included(self):
 
3470
        sections = self.assertSectionIDs(['/foo/*/baz', '/foo/b*', '/foo'],
 
3471
                                         '/foo/bar/baz', '''\
 
3472
[/foo]
 
3473
[/foo/qux]
 
3474
[/foo/b*]
 
3475
[/foo/*/baz]
 
3476
''')
 
3477
        # Note that 'baz' as a relpath for /foo/b* is not fully correct, but
 
3478
        # nothing really is... as far using {relpath} to append it to something
 
3479
        # else, this seems good enough though.
 
3480
        self.assertEquals(['', 'baz', 'bar/baz'],
 
3481
                          [s.locals['relpath'] for _, s in sections])
 
3482
 
 
3483
    def test_respect_order(self):
 
3484
        self.assertSectionIDs(['/foo', '/foo/b*', '/foo/*/baz'],
 
3485
                              '/foo/bar/baz', '''\
 
3486
[/foo/*/baz]
 
3487
[/foo/qux]
 
3488
[/foo/b*]
 
3489
[/foo]
 
3490
''')
 
3491
 
 
3492
 
 
3493
class TestNameMatcher(TestStore):
 
3494
 
 
3495
    def setUp(self):
 
3496
        super(TestNameMatcher, self).setUp()
 
3497
        self.matcher = config.NameMatcher
 
3498
        # Any simple store is good enough
 
3499
        self.get_store = config.test_store_builder_registry.get('configobj')
 
3500
 
 
3501
    def get_matching_sections(self, name):
 
3502
        store = self.get_store(self)
 
3503
        store._load_from_string('''
 
3504
[foo]
 
3505
option=foo
 
3506
[foo/baz]
 
3507
option=foo/baz
 
3508
[bar]
 
3509
option=bar
 
3510
''')
 
3511
        matcher = self.matcher(store, name)
 
3512
        return list(matcher.get_sections())
 
3513
 
 
3514
    def test_matching(self):
 
3515
        sections = self.get_matching_sections('foo')
 
3516
        self.assertLength(1, sections)
 
3517
        self.assertSectionContent(('foo', {'option': 'foo'}), sections[0])
 
3518
 
 
3519
    def test_not_matching(self):
 
3520
        sections = self.get_matching_sections('baz')
 
3521
        self.assertLength(0, sections)
 
3522
 
 
3523
 
 
3524
class TestBaseStackGet(tests.TestCase):
 
3525
 
 
3526
    def setUp(self):
 
3527
        super(TestBaseStackGet, self).setUp()
 
3528
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
 
3529
 
 
3530
    def test_get_first_definition(self):
 
3531
        store1 = config.IniFileStore()
 
3532
        store1._load_from_string('foo=bar')
 
3533
        store2 = config.IniFileStore()
 
3534
        store2._load_from_string('foo=baz')
 
3535
        conf = config.Stack([store1.get_sections, store2.get_sections])
 
3536
        self.assertEquals('bar', conf.get('foo'))
2859
3537
 
2860
3538
    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)
 
3539
        config.option_registry.register(config.Option('foo', default='bar'))
 
3540
        conf_stack = config.Stack([])
2865
3541
        self.assertEquals('bar', conf_stack.get('foo'))
2866
3542
 
2867
3543
    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)
 
3544
        config.option_registry.register(config.Option('foo'))
 
3545
        conf_stack = config.Stack([])
2872
3546
        self.assertEquals(None, conf_stack.get('foo'))
2873
3547
 
2874
3548
    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())
 
3549
        conf_stack = config.Stack([])
2878
3550
        self.assertEquals(None, conf_stack.get('foo'))
2879
3551
 
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
3552
    def test_get_for_empty_section_callable(self):
2893
3553
        conf_stack = config.Stack([lambda : []])
2894
3554
        self.assertEquals(None, conf_stack.get('foo'))
2895
3555
 
2896
3556
    def test_get_for_broken_callable(self):
2897
3557
        # Trying to use and invalid callable raises an exception on first use
2898
 
        conf_stack = config.Stack([lambda : object()])
 
3558
        conf_stack = config.Stack([object])
2899
3559
        self.assertRaises(TypeError, conf_stack.get, 'foo')
2900
3560
 
2901
3561
 
 
3562
class TestStackWithSimpleStore(tests.TestCase):
 
3563
 
 
3564
    def setUp(self):
 
3565
        super(TestStackWithSimpleStore, self).setUp()
 
3566
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
 
3567
        self.registry = config.option_registry
 
3568
 
 
3569
    def get_conf(self, content=None):
 
3570
        return config.MemoryStack(content)
 
3571
 
 
3572
    def test_override_value_from_env(self):
 
3573
        self.registry.register(
 
3574
            config.Option('foo', default='bar', override_from_env=['FOO']))
 
3575
        self.overrideEnv('FOO', 'quux')
 
3576
        # Env variable provides a default taking over the option one
 
3577
        conf = self.get_conf('foo=store')
 
3578
        self.assertEquals('quux', conf.get('foo'))
 
3579
 
 
3580
    def test_first_override_value_from_env_wins(self):
 
3581
        self.registry.register(
 
3582
            config.Option('foo', default='bar',
 
3583
                          override_from_env=['NO_VALUE', 'FOO', 'BAZ']))
 
3584
        self.overrideEnv('FOO', 'foo')
 
3585
        self.overrideEnv('BAZ', 'baz')
 
3586
        # The first env var set wins
 
3587
        conf = self.get_conf('foo=store')
 
3588
        self.assertEquals('foo', conf.get('foo'))
 
3589
 
 
3590
 
 
3591
class TestMemoryStack(tests.TestCase):
 
3592
 
 
3593
    def test_get(self):
 
3594
        conf = config.MemoryStack('foo=bar')
 
3595
        self.assertEquals('bar', conf.get('foo'))
 
3596
 
 
3597
    def test_set(self):
 
3598
        conf = config.MemoryStack('foo=bar')
 
3599
        conf.set('foo', 'baz')
 
3600
        self.assertEquals('baz', conf.get('foo'))
 
3601
 
 
3602
    def test_no_content(self):
 
3603
        conf = config.MemoryStack()
 
3604
        # No content means no loading
 
3605
        self.assertFalse(conf.store.is_loaded())
 
3606
        self.assertRaises(NotImplementedError, conf.get, 'foo')
 
3607
        # But a content can still be provided
 
3608
        conf.store._load_from_string('foo=bar')
 
3609
        self.assertEquals('bar', conf.get('foo'))
 
3610
 
 
3611
 
 
3612
class TestStackIterSections(tests.TestCase):
 
3613
 
 
3614
    def test_empty_stack(self):
 
3615
        conf = config.Stack([])
 
3616
        sections = list(conf.iter_sections())
 
3617
        self.assertLength(0, sections)
 
3618
 
 
3619
    def test_empty_store(self):
 
3620
        store = config.IniFileStore()
 
3621
        store._load_from_string('')
 
3622
        conf = config.Stack([store.get_sections])
 
3623
        sections = list(conf.iter_sections())
 
3624
        self.assertLength(0, sections)
 
3625
 
 
3626
    def test_simple_store(self):
 
3627
        store = config.IniFileStore()
 
3628
        store._load_from_string('foo=bar')
 
3629
        conf = config.Stack([store.get_sections])
 
3630
        tuples = list(conf.iter_sections())
 
3631
        self.assertLength(1, tuples)
 
3632
        (found_store, found_section) = tuples[0]
 
3633
        self.assertIs(store, found_store)
 
3634
 
 
3635
    def test_two_stores(self):
 
3636
        store1 = config.IniFileStore()
 
3637
        store1._load_from_string('foo=bar')
 
3638
        store2 = config.IniFileStore()
 
3639
        store2._load_from_string('bar=qux')
 
3640
        conf = config.Stack([store1.get_sections, store2.get_sections])
 
3641
        tuples = list(conf.iter_sections())
 
3642
        self.assertLength(2, tuples)
 
3643
        self.assertIs(store1, tuples[0][0])
 
3644
        self.assertIs(store2, tuples[1][0])
 
3645
 
 
3646
 
2902
3647
class TestStackWithTransport(tests.TestCaseWithTransport):
2903
3648
 
2904
3649
    scenarios = [(key, {'get_stack': builder}) for key, builder
2914
3659
 
2915
3660
class TestStackGet(TestStackWithTransport):
2916
3661
 
 
3662
    def setUp(self):
 
3663
        super(TestStackGet, self).setUp()
 
3664
        self.conf = self.get_stack(self)
 
3665
 
2917
3666
    def test_get_for_empty_stack(self):
2918
 
        conf = self.get_stack(self)
2919
 
        self.assertEquals(None, conf.get('foo'))
 
3667
        self.assertEquals(None, self.conf.get('foo'))
2920
3668
 
2921
3669
    def test_get_hook(self):
2922
 
        conf = self.get_stack(self)
2923
 
        conf.store._load_from_string('foo=bar')
 
3670
        self.conf.set('foo', 'bar')
2924
3671
        calls = []
2925
3672
        def hook(*args):
2926
3673
            calls.append(args)
2927
3674
        config.ConfigHooks.install_named_hook('get', hook, None)
2928
3675
        self.assertLength(0, calls)
2929
 
        value = conf.get('foo')
 
3676
        value = self.conf.get('foo')
2930
3677
        self.assertEquals('bar', value)
2931
3678
        self.assertLength(1, calls)
2932
 
        self.assertEquals((conf, 'foo', 'bar'), calls[0])
 
3679
        self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
 
3680
 
 
3681
 
 
3682
class TestStackGetWithConverter(tests.TestCase):
 
3683
 
 
3684
    def setUp(self):
 
3685
        super(TestStackGetWithConverter, self).setUp()
 
3686
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
 
3687
        self.registry = config.option_registry
 
3688
 
 
3689
    def get_conf(self, content=None):
 
3690
        return config.MemoryStack(content)
 
3691
 
 
3692
    def register_bool_option(self, name, default=None, default_from_env=None):
 
3693
        b = config.Option(name, help='A boolean.',
 
3694
                          default=default, default_from_env=default_from_env,
 
3695
                          from_unicode=config.bool_from_store)
 
3696
        self.registry.register(b)
 
3697
 
 
3698
    def test_get_default_bool_None(self):
 
3699
        self.register_bool_option('foo')
 
3700
        conf = self.get_conf('')
 
3701
        self.assertEquals(None, conf.get('foo'))
 
3702
 
 
3703
    def test_get_default_bool_True(self):
 
3704
        self.register_bool_option('foo', u'True')
 
3705
        conf = self.get_conf('')
 
3706
        self.assertEquals(True, conf.get('foo'))
 
3707
 
 
3708
    def test_get_default_bool_False(self):
 
3709
        self.register_bool_option('foo', False)
 
3710
        conf = self.get_conf('')
 
3711
        self.assertEquals(False, conf.get('foo'))
 
3712
 
 
3713
    def test_get_default_bool_False_as_string(self):
 
3714
        self.register_bool_option('foo', u'False')
 
3715
        conf = self.get_conf('')
 
3716
        self.assertEquals(False, conf.get('foo'))
 
3717
 
 
3718
    def test_get_default_bool_from_env_converted(self):
 
3719
        self.register_bool_option('foo', u'True', default_from_env=['FOO'])
 
3720
        self.overrideEnv('FOO', 'False')
 
3721
        conf = self.get_conf('')
 
3722
        self.assertEquals(False, conf.get('foo'))
 
3723
 
 
3724
    def test_get_default_bool_when_conversion_fails(self):
 
3725
        self.register_bool_option('foo', default='True')
 
3726
        conf = self.get_conf('foo=invalid boolean')
 
3727
        self.assertEquals(True, conf.get('foo'))
 
3728
 
 
3729
    def register_integer_option(self, name,
 
3730
                                default=None, default_from_env=None):
 
3731
        i = config.Option(name, help='An integer.',
 
3732
                          default=default, default_from_env=default_from_env,
 
3733
                          from_unicode=config.int_from_store)
 
3734
        self.registry.register(i)
 
3735
 
 
3736
    def test_get_default_integer_None(self):
 
3737
        self.register_integer_option('foo')
 
3738
        conf = self.get_conf('')
 
3739
        self.assertEquals(None, conf.get('foo'))
 
3740
 
 
3741
    def test_get_default_integer(self):
 
3742
        self.register_integer_option('foo', 42)
 
3743
        conf = self.get_conf('')
 
3744
        self.assertEquals(42, conf.get('foo'))
 
3745
 
 
3746
    def test_get_default_integer_as_string(self):
 
3747
        self.register_integer_option('foo', u'42')
 
3748
        conf = self.get_conf('')
 
3749
        self.assertEquals(42, conf.get('foo'))
 
3750
 
 
3751
    def test_get_default_integer_from_env(self):
 
3752
        self.register_integer_option('foo', default_from_env=['FOO'])
 
3753
        self.overrideEnv('FOO', '18')
 
3754
        conf = self.get_conf('')
 
3755
        self.assertEquals(18, conf.get('foo'))
 
3756
 
 
3757
    def test_get_default_integer_when_conversion_fails(self):
 
3758
        self.register_integer_option('foo', default='12')
 
3759
        conf = self.get_conf('foo=invalid integer')
 
3760
        self.assertEquals(12, conf.get('foo'))
 
3761
 
 
3762
    def register_list_option(self, name, default=None, default_from_env=None):
 
3763
        l = config.ListOption(name, help='A list.', default=default,
 
3764
                              default_from_env=default_from_env)
 
3765
        self.registry.register(l)
 
3766
 
 
3767
    def test_get_default_list_None(self):
 
3768
        self.register_list_option('foo')
 
3769
        conf = self.get_conf('')
 
3770
        self.assertEquals(None, conf.get('foo'))
 
3771
 
 
3772
    def test_get_default_list_empty(self):
 
3773
        self.register_list_option('foo', '')
 
3774
        conf = self.get_conf('')
 
3775
        self.assertEquals([], conf.get('foo'))
 
3776
 
 
3777
    def test_get_default_list_from_env(self):
 
3778
        self.register_list_option('foo', default_from_env=['FOO'])
 
3779
        self.overrideEnv('FOO', '')
 
3780
        conf = self.get_conf('')
 
3781
        self.assertEquals([], conf.get('foo'))
 
3782
 
 
3783
    def test_get_with_list_converter_no_item(self):
 
3784
        self.register_list_option('foo', None)
 
3785
        conf = self.get_conf('foo=,')
 
3786
        self.assertEquals([], conf.get('foo'))
 
3787
 
 
3788
    def test_get_with_list_converter_many_items(self):
 
3789
        self.register_list_option('foo', None)
 
3790
        conf = self.get_conf('foo=m,o,r,e')
 
3791
        self.assertEquals(['m', 'o', 'r', 'e'], conf.get('foo'))
 
3792
 
 
3793
    def test_get_with_list_converter_embedded_spaces_many_items(self):
 
3794
        self.register_list_option('foo', None)
 
3795
        conf = self.get_conf('foo=" bar", "baz "')
 
3796
        self.assertEquals([' bar', 'baz '], conf.get('foo'))
 
3797
 
 
3798
    def test_get_with_list_converter_stripped_spaces_many_items(self):
 
3799
        self.register_list_option('foo', None)
 
3800
        conf = self.get_conf('foo= bar ,  baz ')
 
3801
        self.assertEquals(['bar', 'baz'], conf.get('foo'))
 
3802
 
 
3803
 
 
3804
class TestIterOptionRefs(tests.TestCase):
 
3805
    """iter_option_refs is a bit unusual, document some cases."""
 
3806
 
 
3807
    def assertRefs(self, expected, string):
 
3808
        self.assertEquals(expected, list(config.iter_option_refs(string)))
 
3809
 
 
3810
    def test_empty(self):
 
3811
        self.assertRefs([(False, '')], '')
 
3812
 
 
3813
    def test_no_refs(self):
 
3814
        self.assertRefs([(False, 'foo bar')], 'foo bar')
 
3815
 
 
3816
    def test_single_ref(self):
 
3817
        self.assertRefs([(False, ''), (True, '{foo}'), (False, '')], '{foo}')
 
3818
 
 
3819
    def test_broken_ref(self):
 
3820
        self.assertRefs([(False, '{foo')], '{foo')
 
3821
 
 
3822
    def test_embedded_ref(self):
 
3823
        self.assertRefs([(False, '{'), (True, '{foo}'), (False, '}')],
 
3824
                        '{{foo}}')
 
3825
 
 
3826
    def test_two_refs(self):
 
3827
        self.assertRefs([(False, ''), (True, '{foo}'),
 
3828
                         (False, ''), (True, '{bar}'),
 
3829
                         (False, ''),],
 
3830
                        '{foo}{bar}')
 
3831
 
 
3832
    def test_newline_in_refs_are_not_matched(self):
 
3833
        self.assertRefs([(False, '{\nxx}{xx\n}{{\n}}')], '{\nxx}{xx\n}{{\n}}')
 
3834
 
 
3835
 
 
3836
class TestStackExpandOptions(tests.TestCaseWithTransport):
 
3837
 
 
3838
    def setUp(self):
 
3839
        super(TestStackExpandOptions, self).setUp()
 
3840
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
 
3841
        self.registry = config.option_registry
 
3842
        store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
 
3843
        self.conf = config.Stack([store.get_sections], store)
 
3844
 
 
3845
    def assertExpansion(self, expected, string, env=None):
 
3846
        self.assertEquals(expected, self.conf.expand_options(string, env))
 
3847
 
 
3848
    def test_no_expansion(self):
 
3849
        self.assertExpansion('foo', 'foo')
 
3850
 
 
3851
    def test_expand_default_value(self):
 
3852
        self.conf.store._load_from_string('bar=baz')
 
3853
        self.registry.register(config.Option('foo', default=u'{bar}'))
 
3854
        self.assertEquals('baz', self.conf.get('foo', expand=True))
 
3855
 
 
3856
    def test_expand_default_from_env(self):
 
3857
        self.conf.store._load_from_string('bar=baz')
 
3858
        self.registry.register(config.Option('foo', default_from_env=['FOO']))
 
3859
        self.overrideEnv('FOO', '{bar}')
 
3860
        self.assertEquals('baz', self.conf.get('foo', expand=True))
 
3861
 
 
3862
    def test_expand_default_on_failed_conversion(self):
 
3863
        self.conf.store._load_from_string('baz=bogus\nbar=42\nfoo={baz}')
 
3864
        self.registry.register(
 
3865
            config.Option('foo', default=u'{bar}',
 
3866
                          from_unicode=config.int_from_store))
 
3867
        self.assertEquals(42, self.conf.get('foo', expand=True))
 
3868
 
 
3869
    def test_env_adding_options(self):
 
3870
        self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
 
3871
 
 
3872
    def test_env_overriding_options(self):
 
3873
        self.conf.store._load_from_string('foo=baz')
 
3874
        self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
 
3875
 
 
3876
    def test_simple_ref(self):
 
3877
        self.conf.store._load_from_string('foo=xxx')
 
3878
        self.assertExpansion('xxx', '{foo}')
 
3879
 
 
3880
    def test_unknown_ref(self):
 
3881
        self.assertRaises(errors.ExpandingUnknownOption,
 
3882
                          self.conf.expand_options, '{foo}')
 
3883
 
 
3884
    def test_indirect_ref(self):
 
3885
        self.conf.store._load_from_string('''
 
3886
foo=xxx
 
3887
bar={foo}
 
3888
''')
 
3889
        self.assertExpansion('xxx', '{bar}')
 
3890
 
 
3891
    def test_embedded_ref(self):
 
3892
        self.conf.store._load_from_string('''
 
3893
foo=xxx
 
3894
bar=foo
 
3895
''')
 
3896
        self.assertExpansion('xxx', '{{bar}}')
 
3897
 
 
3898
    def test_simple_loop(self):
 
3899
        self.conf.store._load_from_string('foo={foo}')
 
3900
        self.assertRaises(errors.OptionExpansionLoop,
 
3901
                          self.conf.expand_options, '{foo}')
 
3902
 
 
3903
    def test_indirect_loop(self):
 
3904
        self.conf.store._load_from_string('''
 
3905
foo={bar}
 
3906
bar={baz}
 
3907
baz={foo}''')
 
3908
        e = self.assertRaises(errors.OptionExpansionLoop,
 
3909
                              self.conf.expand_options, '{foo}')
 
3910
        self.assertEquals('foo->bar->baz', e.refs)
 
3911
        self.assertEquals('{foo}', e.string)
 
3912
 
 
3913
    def test_list(self):
 
3914
        self.conf.store._load_from_string('''
 
3915
foo=start
 
3916
bar=middle
 
3917
baz=end
 
3918
list={foo},{bar},{baz}
 
3919
''')
 
3920
        self.registry.register(
 
3921
            config.ListOption('list'))
 
3922
        self.assertEquals(['start', 'middle', 'end'],
 
3923
                           self.conf.get('list', expand=True))
 
3924
 
 
3925
    def test_cascading_list(self):
 
3926
        self.conf.store._load_from_string('''
 
3927
foo=start,{bar}
 
3928
bar=middle,{baz}
 
3929
baz=end
 
3930
list={foo}
 
3931
''')
 
3932
        self.registry.register(config.ListOption('list'))
 
3933
        # Register an intermediate option as a list to ensure no conversion
 
3934
        # happen while expanding. Conversion should only occur for the original
 
3935
        # option ('list' here).
 
3936
        self.registry.register(config.ListOption('baz'))
 
3937
        self.assertEquals(['start', 'middle', 'end'],
 
3938
                           self.conf.get('list', expand=True))
 
3939
 
 
3940
    def test_pathologically_hidden_list(self):
 
3941
        self.conf.store._load_from_string('''
 
3942
foo=bin
 
3943
bar=go
 
3944
start={foo
 
3945
middle=},{
 
3946
end=bar}
 
3947
hidden={start}{middle}{end}
 
3948
''')
 
3949
        # What matters is what the registration says, the conversion happens
 
3950
        # only after all expansions have been performed
 
3951
        self.registry.register(config.ListOption('hidden'))
 
3952
        self.assertEquals(['bin', 'go'],
 
3953
                          self.conf.get('hidden', expand=True))
 
3954
 
 
3955
 
 
3956
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
 
3957
 
 
3958
    def setUp(self):
 
3959
        super(TestStackCrossSectionsExpand, self).setUp()
 
3960
 
 
3961
    def get_config(self, location, string):
 
3962
        if string is None:
 
3963
            string = ''
 
3964
        # Since we don't save the config we won't strictly require to inherit
 
3965
        # from TestCaseInTempDir, but an error occurs so quickly...
 
3966
        c = config.LocationStack(location)
 
3967
        c.store._load_from_string(string)
 
3968
        return c
 
3969
 
 
3970
    def test_dont_cross_unrelated_section(self):
 
3971
        c = self.get_config('/another/branch/path','''
 
3972
[/one/branch/path]
 
3973
foo = hello
 
3974
bar = {foo}/2
 
3975
 
 
3976
[/another/branch/path]
 
3977
bar = {foo}/2
 
3978
''')
 
3979
        self.assertRaises(errors.ExpandingUnknownOption,
 
3980
                          c.get, 'bar', expand=True)
 
3981
 
 
3982
    def test_cross_related_sections(self):
 
3983
        c = self.get_config('/project/branch/path','''
 
3984
[/project]
 
3985
foo = qu
 
3986
 
 
3987
[/project/branch/path]
 
3988
bar = {foo}ux
 
3989
''')
 
3990
        self.assertEquals('quux', c.get('bar', expand=True))
 
3991
 
 
3992
 
 
3993
class TestStackCrossStoresExpand(tests.TestCaseWithTransport):
 
3994
 
 
3995
    def test_cross_global_locations(self):
 
3996
        l_store = config.LocationStore()
 
3997
        l_store._load_from_string('''
 
3998
[/branch]
 
3999
lfoo = loc-foo
 
4000
lbar = {gbar}
 
4001
''')
 
4002
        l_store.save()
 
4003
        g_store = config.GlobalStore()
 
4004
        g_store._load_from_string('''
 
4005
[DEFAULT]
 
4006
gfoo = {lfoo}
 
4007
gbar = glob-bar
 
4008
''')
 
4009
        g_store.save()
 
4010
        stack = config.LocationStack('/branch')
 
4011
        self.assertEquals('glob-bar', stack.get('lbar', expand=True))
 
4012
        self.assertEquals('loc-foo', stack.get('gfoo', expand=True))
 
4013
 
 
4014
 
 
4015
class TestStackExpandSectionLocals(tests.TestCaseWithTransport):
 
4016
 
 
4017
    def test_expand_locals_empty(self):
 
4018
        l_store = config.LocationStore()
 
4019
        l_store._load_from_string('''
 
4020
[/home/user/project]
 
4021
base = {basename}
 
4022
rel = {relpath}
 
4023
''')
 
4024
        l_store.save()
 
4025
        stack = config.LocationStack('/home/user/project/')
 
4026
        self.assertEquals('', stack.get('base', expand=True))
 
4027
        self.assertEquals('', stack.get('rel', expand=True))
 
4028
 
 
4029
    def test_expand_basename_locally(self):
 
4030
        l_store = config.LocationStore()
 
4031
        l_store._load_from_string('''
 
4032
[/home/user/project]
 
4033
bfoo = {basename}
 
4034
''')
 
4035
        l_store.save()
 
4036
        stack = config.LocationStack('/home/user/project/branch')
 
4037
        self.assertEquals('branch', stack.get('bfoo', expand=True))
 
4038
 
 
4039
    def test_expand_basename_locally_longer_path(self):
 
4040
        l_store = config.LocationStore()
 
4041
        l_store._load_from_string('''
 
4042
[/home/user]
 
4043
bfoo = {basename}
 
4044
''')
 
4045
        l_store.save()
 
4046
        stack = config.LocationStack('/home/user/project/dir/branch')
 
4047
        self.assertEquals('branch', stack.get('bfoo', expand=True))
 
4048
 
 
4049
    def test_expand_relpath_locally(self):
 
4050
        l_store = config.LocationStore()
 
4051
        l_store._load_from_string('''
 
4052
[/home/user/project]
 
4053
lfoo = loc-foo/{relpath}
 
4054
''')
 
4055
        l_store.save()
 
4056
        stack = config.LocationStack('/home/user/project/branch')
 
4057
        self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
 
4058
 
 
4059
    def test_expand_relpath_unknonw_in_global(self):
 
4060
        g_store = config.GlobalStore()
 
4061
        g_store._load_from_string('''
 
4062
[DEFAULT]
 
4063
gfoo = {relpath}
 
4064
''')
 
4065
        g_store.save()
 
4066
        stack = config.LocationStack('/home/user/project/branch')
 
4067
        self.assertRaises(errors.ExpandingUnknownOption,
 
4068
                          stack.get, 'gfoo', expand=True)
 
4069
 
 
4070
    def test_expand_local_option_locally(self):
 
4071
        l_store = config.LocationStore()
 
4072
        l_store._load_from_string('''
 
4073
[/home/user/project]
 
4074
lfoo = loc-foo/{relpath}
 
4075
lbar = {gbar}
 
4076
''')
 
4077
        l_store.save()
 
4078
        g_store = config.GlobalStore()
 
4079
        g_store._load_from_string('''
 
4080
[DEFAULT]
 
4081
gfoo = {lfoo}
 
4082
gbar = glob-bar
 
4083
''')
 
4084
        g_store.save()
 
4085
        stack = config.LocationStack('/home/user/project/branch')
 
4086
        self.assertEquals('glob-bar', stack.get('lbar', expand=True))
 
4087
        self.assertEquals('loc-foo/branch', stack.get('gfoo', expand=True))
 
4088
 
 
4089
    def test_locals_dont_leak(self):
 
4090
        """Make sure we chose the right local in presence of several sections.
 
4091
        """
 
4092
        l_store = config.LocationStore()
 
4093
        l_store._load_from_string('''
 
4094
[/home/user]
 
4095
lfoo = loc-foo/{relpath}
 
4096
[/home/user/project]
 
4097
lfoo = loc-foo/{relpath}
 
4098
''')
 
4099
        l_store.save()
 
4100
        stack = config.LocationStack('/home/user/project/branch')
 
4101
        self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
 
4102
        stack = config.LocationStack('/home/user/bar/baz')
 
4103
        self.assertEquals('loc-foo/bar/baz', stack.get('lfoo', expand=True))
 
4104
 
2933
4105
 
2934
4106
 
2935
4107
class TestStackSet(TestStackWithTransport):
2936
4108
 
2937
4109
    def test_simple_set(self):
2938
4110
        conf = self.get_stack(self)
2939
 
        conf.store._load_from_string('foo=bar')
2940
 
        self.assertEquals('bar', conf.get('foo'))
 
4111
        self.assertEquals(None, conf.get('foo'))
2941
4112
        conf.set('foo', 'baz')
2942
4113
        # Did we get it back ?
2943
4114
        self.assertEquals('baz', conf.get('foo'))
2963
4134
 
2964
4135
    def test_remove_existing(self):
2965
4136
        conf = self.get_stack(self)
2966
 
        conf.store._load_from_string('foo=bar')
 
4137
        conf.set('foo', 'bar')
2967
4138
        self.assertEquals('bar', conf.get('foo'))
2968
4139
        conf.remove('foo')
2969
4140
        # Did we get it back ?
2980
4151
        config.ConfigHooks.install_named_hook('remove', hook, None)
2981
4152
        self.assertLength(0, calls)
2982
4153
        conf = self.get_stack(self)
2983
 
        conf.store._load_from_string('foo=bar')
 
4154
        conf.set('foo', 'bar')
2984
4155
        conf.remove('foo')
2985
4156
        self.assertLength(1, calls)
2986
4157
        self.assertEquals((conf, 'foo'), calls[0])
3161
4332
        conf = config.AuthenticationConfig(_file=StringIO(
3162
4333
                'foo = bar\xff'))
3163
4334
        self.assertRaises(errors.ConfigContentError, conf._get_config)
3164
 
        
 
4335
 
3165
4336
    def test_missing_auth_section_header(self):
3166
4337
        conf = config.AuthenticationConfig(_file=StringIO('foo = bar'))
3167
4338
        self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
3648
4819
 
3649
4820
    def test_auto_user_id(self):
3650
4821
        """Automatic inference of user name.
3651
 
        
 
4822
 
3652
4823
        This is a bit hard to test in an isolated way, because it depends on
3653
4824
        system functions that go direct to /etc or perhaps somewhere else.
3654
4825
        But it's reasonable to say that on Unix, with an /etc/mailname, we ought
3664
4835
        else:
3665
4836
            self.assertEquals((None, None), (realname, address))
3666
4837
 
 
4838
 
 
4839
class EmailOptionTests(tests.TestCase):
 
4840
 
 
4841
    def test_default_email_uses_BZR_EMAIL(self):
 
4842
        conf = config.MemoryStack('email=jelmer@debian.org')
 
4843
        # BZR_EMAIL takes precedence over EMAIL
 
4844
        self.overrideEnv('BZR_EMAIL', 'jelmer@samba.org')
 
4845
        self.overrideEnv('EMAIL', 'jelmer@apache.org')
 
4846
        self.assertEquals('jelmer@samba.org', conf.get('email'))
 
4847
 
 
4848
    def test_default_email_uses_EMAIL(self):
 
4849
        conf = config.MemoryStack('')
 
4850
        self.overrideEnv('BZR_EMAIL', None)
 
4851
        self.overrideEnv('EMAIL', 'jelmer@apache.org')
 
4852
        self.assertEquals('jelmer@apache.org', conf.get('email'))
 
4853
 
 
4854
    def test_BZR_EMAIL_overrides(self):
 
4855
        conf = config.MemoryStack('email=jelmer@debian.org')
 
4856
        self.overrideEnv('BZR_EMAIL', 'jelmer@apache.org')
 
4857
        self.assertEquals('jelmer@apache.org', conf.get('email'))
 
4858
        self.overrideEnv('BZR_EMAIL', None)
 
4859
        self.overrideEnv('EMAIL', 'jelmer@samba.org')
 
4860
        self.assertEquals('jelmer@debian.org', conf.get('email'))
 
4861
 
 
4862
 
 
4863
class MailClientOptionTests(tests.TestCase):
 
4864
 
 
4865
    def test_default(self):
 
4866
        conf = config.MemoryStack('')
 
4867
        client = conf.get('mail_client')
 
4868
        self.assertIs(client, mail_client.DefaultMail)
 
4869
 
 
4870
    def test_evolution(self):
 
4871
        conf = config.MemoryStack('mail_client=evolution')
 
4872
        client = conf.get('mail_client')
 
4873
        self.assertIs(client, mail_client.Evolution)
 
4874
 
 
4875
    def test_kmail(self):
 
4876
        conf = config.MemoryStack('mail_client=kmail')
 
4877
        client = conf.get('mail_client')
 
4878
        self.assertIs(client, mail_client.KMail)
 
4879
 
 
4880
    def test_mutt(self):
 
4881
        conf = config.MemoryStack('mail_client=mutt')
 
4882
        client = conf.get('mail_client')
 
4883
        self.assertIs(client, mail_client.Mutt)
 
4884
 
 
4885
    def test_thunderbird(self):
 
4886
        conf = config.MemoryStack('mail_client=thunderbird')
 
4887
        client = conf.get('mail_client')
 
4888
        self.assertIs(client, mail_client.Thunderbird)
 
4889
 
 
4890
    def test_explicit_default(self):
 
4891
        conf = config.MemoryStack('mail_client=default')
 
4892
        client = conf.get('mail_client')
 
4893
        self.assertIs(client, mail_client.DefaultMail)
 
4894
 
 
4895
    def test_editor(self):
 
4896
        conf = config.MemoryStack('mail_client=editor')
 
4897
        client = conf.get('mail_client')
 
4898
        self.assertIs(client, mail_client.Editor)
 
4899
 
 
4900
    def test_mapi(self):
 
4901
        conf = config.MemoryStack('mail_client=mapi')
 
4902
        client = conf.get('mail_client')
 
4903
        self.assertIs(client, mail_client.MAPIClient)
 
4904
 
 
4905
    def test_xdg_email(self):
 
4906
        conf = config.MemoryStack('mail_client=xdg-email')
 
4907
        client = conf.get('mail_client')
 
4908
        self.assertIs(client, mail_client.XDGEmail)
 
4909
 
 
4910
    def test_unknown(self):
 
4911
        conf = config.MemoryStack('mail_client=firebird')
 
4912
        self.assertRaises(errors.ConfigOptionValueError, conf.get,
 
4913
                'mail_client')