~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_config.py

  • Committer: Marius Kruger
  • Date: 2012-06-10 22:48:08 UTC
  • mto: This revision was merged to the branch mainline in revision 6544.
  • Revision ID: amanic@gmail.com-20120610224808-s7qj3ep4zd21s0u7
use ui.ui_factory.show_user_warning to warn about not checking ssl certs, so that we only do it once

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."""
2243
2477
 
2244
2478
    def test_help_is_set(self):
2245
2479
        option_help = self.registry.get_help(self.option_name)
2246
 
        self.assertNotEquals(None, option_help)
2247
 
        # Come on, think about the user, he really wants to know whst the
 
2480
        # Come on, think about the user, he really wants to know what the
2248
2481
        # option is about
 
2482
        self.assertIsNot(None, option_help)
2249
2483
        self.assertNotEquals('', option_help)
2250
2484
 
2251
2485
 
2273
2507
 
2274
2508
class TestMutableSection(tests.TestCase):
2275
2509
 
2276
 
    # FIXME: Parametrize so that all sections (including os.environ and the
2277
 
    # ones produced by Stores) run these tests -- vila 2011-04-01
 
2510
    scenarios = [('mutable',
 
2511
                  {'get_section':
 
2512
                       lambda opts: config.MutableSection('myID', opts)},),
 
2513
        ]
2278
2514
 
2279
2515
    def test_set(self):
2280
2516
        a_dict = dict(foo='bar')
2281
 
        section = config.MutableSection('myID', a_dict)
 
2517
        section = self.get_section(a_dict)
2282
2518
        section.set('foo', 'new_value')
2283
2519
        self.assertEquals('new_value', section.get('foo'))
2284
2520
        # The change appears in the shared section
2289
2525
 
2290
2526
    def test_set_preserve_original_once(self):
2291
2527
        a_dict = dict(foo='bar')
2292
 
        section = config.MutableSection('myID', a_dict)
 
2528
        section = self.get_section(a_dict)
2293
2529
        section.set('foo', 'first_value')
2294
2530
        section.set('foo', 'second_value')
2295
2531
        # We keep track of the original value
2298
2534
 
2299
2535
    def test_remove(self):
2300
2536
        a_dict = dict(foo='bar')
2301
 
        section = config.MutableSection('myID', a_dict)
 
2537
        section = self.get_section(a_dict)
2302
2538
        section.remove('foo')
2303
2539
        # We get None for unknown options via the default value
2304
2540
        self.assertEquals(None, section.get('foo'))
2311
2547
 
2312
2548
    def test_remove_new_option(self):
2313
2549
        a_dict = dict()
2314
 
        section = config.MutableSection('myID', a_dict)
 
2550
        section = self.get_section(a_dict)
2315
2551
        section.set('foo', 'bar')
2316
2552
        section.remove('foo')
2317
2553
        self.assertFalse('foo' in section.options)
2321
2557
        self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
2322
2558
 
2323
2559
 
 
2560
class TestCommandLineStore(tests.TestCase):
 
2561
 
 
2562
    def setUp(self):
 
2563
        super(TestCommandLineStore, self).setUp()
 
2564
        self.store = config.CommandLineStore()
 
2565
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
 
2566
 
 
2567
    def get_section(self):
 
2568
        """Get the unique section for the command line overrides."""
 
2569
        sections = list(self.store.get_sections())
 
2570
        self.assertLength(1, sections)
 
2571
        store, section = sections[0]
 
2572
        self.assertEquals(self.store, store)
 
2573
        return section
 
2574
 
 
2575
    def test_no_override(self):
 
2576
        self.store._from_cmdline([])
 
2577
        section = self.get_section()
 
2578
        self.assertLength(0, list(section.iter_option_names()))
 
2579
 
 
2580
    def test_simple_override(self):
 
2581
        self.store._from_cmdline(['a=b'])
 
2582
        section = self.get_section()
 
2583
        self.assertEqual('b', section.get('a'))
 
2584
 
 
2585
    def test_list_override(self):
 
2586
        opt = config.ListOption('l')
 
2587
        config.option_registry.register(opt)
 
2588
        self.store._from_cmdline(['l=1,2,3'])
 
2589
        val = self.get_section().get('l')
 
2590
        self.assertEqual('1,2,3', val)
 
2591
        # Reminder: lists should be registered as such explicitely, otherwise
 
2592
        # the conversion needs to be done afterwards.
 
2593
        self.assertEqual(['1', '2', '3'],
 
2594
                         opt.convert_from_unicode(self.store, val))
 
2595
 
 
2596
    def test_multiple_overrides(self):
 
2597
        self.store._from_cmdline(['a=b', 'x=y'])
 
2598
        section = self.get_section()
 
2599
        self.assertEquals('b', section.get('a'))
 
2600
        self.assertEquals('y', section.get('x'))
 
2601
 
 
2602
    def test_wrong_syntax(self):
 
2603
        self.assertRaises(errors.BzrCommandError,
 
2604
                          self.store._from_cmdline, ['a=b', 'c'])
 
2605
 
 
2606
class TestStoreMinimalAPI(tests.TestCaseWithTransport):
 
2607
 
 
2608
    scenarios = [(key, {'get_store': builder}) for key, builder
 
2609
                 in config.test_store_builder_registry.iteritems()] + [
 
2610
        ('cmdline', {'get_store': lambda test: config.CommandLineStore()})]
 
2611
 
 
2612
    def test_id(self):
 
2613
        store = self.get_store(self)
 
2614
        if type(store) == config.TransportIniFileStore:
 
2615
            raise tests.TestNotApplicable(
 
2616
                "%s is not a concrete Store implementation"
 
2617
                " so it doesn't need an id" % (store.__class__.__name__,))
 
2618
        self.assertIsNot(None, store.id)
 
2619
 
 
2620
 
2324
2621
class TestStore(tests.TestCaseWithTransport):
2325
2622
 
2326
 
    def assertSectionContent(self, expected, section):
 
2623
    def assertSectionContent(self, expected, (store, section)):
2327
2624
        """Assert that some options have the proper values in a section."""
2328
2625
        expected_name, expected_options = expected
2329
2626
        self.assertEquals(expected_name, section.id)
2337
2634
    scenarios = [(key, {'get_store': builder}) for key, builder
2338
2635
                 in config.test_store_builder_registry.iteritems()]
2339
2636
 
2340
 
    def setUp(self):
2341
 
        super(TestReadonlyStore, self).setUp()
2342
 
 
2343
2637
    def test_building_delays_load(self):
2344
2638
        store = self.get_store(self)
2345
2639
        self.assertEquals(False, store.is_loaded())
2371
2665
        self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2372
2666
 
2373
2667
 
 
2668
class TestStoreQuoting(TestStore):
 
2669
 
 
2670
    scenarios = [(key, {'get_store': builder}) for key, builder
 
2671
                 in config.test_store_builder_registry.iteritems()]
 
2672
 
 
2673
    def setUp(self):
 
2674
        super(TestStoreQuoting, self).setUp()
 
2675
        self.store = self.get_store(self)
 
2676
        # We need a loaded store but any content will do
 
2677
        self.store._load_from_string('')
 
2678
 
 
2679
    def assertIdempotent(self, s):
 
2680
        """Assert that quoting an unquoted string is a no-op and vice-versa.
 
2681
 
 
2682
        What matters here is that option values, as they appear in a store, can
 
2683
        be safely round-tripped out of the store and back.
 
2684
 
 
2685
        :param s: A string, quoted if required.
 
2686
        """
 
2687
        self.assertEquals(s, self.store.quote(self.store.unquote(s)))
 
2688
        self.assertEquals(s, self.store.unquote(self.store.quote(s)))
 
2689
 
 
2690
    def test_empty_string(self):
 
2691
        if isinstance(self.store, config.IniFileStore):
 
2692
            # configobj._quote doesn't handle empty values
 
2693
            self.assertRaises(AssertionError,
 
2694
                              self.assertIdempotent, '')
 
2695
        else:
 
2696
            self.assertIdempotent('')
 
2697
        # But quoted empty strings are ok
 
2698
        self.assertIdempotent('""')
 
2699
 
 
2700
    def test_embedded_spaces(self):
 
2701
        self.assertIdempotent('" a b c "')
 
2702
 
 
2703
    def test_embedded_commas(self):
 
2704
        self.assertIdempotent('" a , b c "')
 
2705
 
 
2706
    def test_simple_comma(self):
 
2707
        if isinstance(self.store, config.IniFileStore):
 
2708
            # configobj requires that lists are special-cased
 
2709
           self.assertRaises(AssertionError,
 
2710
                             self.assertIdempotent, ',')
 
2711
        else:
 
2712
            self.assertIdempotent(',')
 
2713
        # When a single comma is required, quoting is also required
 
2714
        self.assertIdempotent('","')
 
2715
 
 
2716
    def test_list(self):
 
2717
        if isinstance(self.store, config.IniFileStore):
 
2718
            # configobj requires that lists are special-cased
 
2719
            self.assertRaises(AssertionError,
 
2720
                              self.assertIdempotent, 'a,b')
 
2721
        else:
 
2722
            self.assertIdempotent('a,b')
 
2723
 
 
2724
 
 
2725
class TestDictFromStore(tests.TestCase):
 
2726
 
 
2727
    def test_unquote_not_string(self):
 
2728
        conf = config.MemoryStack('x=2\n[a_section]\na=1\n')
 
2729
        value = conf.get('a_section')
 
2730
        # Urgh, despite 'conf' asking for the no-name section, we get the
 
2731
        # content of another section as a dict o_O
 
2732
        self.assertEquals({'a': '1'}, value)
 
2733
        unquoted = conf.store.unquote(value)
 
2734
        # Which cannot be unquoted but shouldn't crash either (the use cases
 
2735
        # are getting the value or displaying it. In the later case, '%s' will
 
2736
        # do).
 
2737
        self.assertEquals({'a': '1'}, unquoted)
 
2738
        self.assertEquals("{u'a': u'1'}", '%s' % (unquoted,))
 
2739
 
 
2740
 
2374
2741
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2375
 
    """Simulate loading a config store without content of various encodings.
 
2742
    """Simulate loading a config store with content of various encodings.
2376
2743
 
2377
2744
    All files produced by bzr are in utf8 content.
2378
2745
 
2391
2758
        utf8_content = unicode_content.encode('utf8')
2392
2759
        # Store the raw content in the config file
2393
2760
        t.put_bytes('foo.conf', utf8_content)
2394
 
        store = config.IniFileStore(t, 'foo.conf')
 
2761
        store = config.TransportIniFileStore(t, 'foo.conf')
2395
2762
        store.load()
2396
2763
        stack = config.Stack([store.get_sections], store)
2397
2764
        self.assertEquals(unicode_user, stack.get('user'))
2400
2767
        """Ensure we display a proper error on non-ascii, non utf-8 content."""
2401
2768
        t = self.get_transport()
2402
2769
        t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2403
 
        store = config.IniFileStore(t, 'foo.conf')
 
2770
        store = config.TransportIniFileStore(t, 'foo.conf')
2404
2771
        self.assertRaises(errors.ConfigContentError, store.load)
2405
2772
 
2406
2773
    def test_load_erroneous_content(self):
2407
2774
        """Ensure we display a proper error on content that can't be parsed."""
2408
2775
        t = self.get_transport()
2409
2776
        t.put_bytes('foo.conf', '[open_section\n')
2410
 
        store = config.IniFileStore(t, 'foo.conf')
 
2777
        store = config.TransportIniFileStore(t, 'foo.conf')
2411
2778
        self.assertRaises(errors.ParseConfigError, store.load)
2412
2779
 
 
2780
    def test_load_permission_denied(self):
 
2781
        """Ensure we get warned when trying to load an inaccessible file."""
 
2782
        warnings = []
 
2783
        def warning(*args):
 
2784
            warnings.append(args[0] % args[1:])
 
2785
        self.overrideAttr(trace, 'warning', warning)
 
2786
 
 
2787
        t = self.get_transport()
 
2788
 
 
2789
        def get_bytes(relpath):
 
2790
            raise errors.PermissionDenied(relpath, "")
 
2791
        t.get_bytes = get_bytes
 
2792
        store = config.TransportIniFileStore(t, 'foo.conf')
 
2793
        self.assertRaises(errors.PermissionDenied, store.load)
 
2794
        self.assertEquals(
 
2795
            warnings,
 
2796
            [u'Permission denied while trying to load configuration store %s.'
 
2797
             % store.external_url()])
 
2798
 
2413
2799
 
2414
2800
class TestIniConfigContent(tests.TestCaseWithTransport):
2415
 
    """Simulate loading a IniBasedConfig without content of various encodings.
 
2801
    """Simulate loading a IniBasedConfig with content of various encodings.
2416
2802
 
2417
2803
    All files produced by bzr are in utf8 content.
2418
2804
 
2473
2859
        store.save()
2474
2860
        self.assertEquals(False, self.has_store(store))
2475
2861
 
 
2862
    def test_mutable_section_shared(self):
 
2863
        store = self.get_store(self)
 
2864
        store._load_from_string('foo=bar\n')
 
2865
        # FIXME: There should be a better way than relying on the test
 
2866
        # parametrization to identify branch.conf -- vila 2011-0526
 
2867
        if self.store_id in ('branch', 'remote_branch'):
 
2868
            # branch stores requires write locked branches
 
2869
            self.addCleanup(store.branch.lock_write().unlock)
 
2870
        section1 = store.get_mutable_section(None)
 
2871
        section2 = store.get_mutable_section(None)
 
2872
        # If we get different sections, different callers won't share the
 
2873
        # modification
 
2874
        self.assertIs(section1, section2)
 
2875
 
2476
2876
    def test_save_emptied_succeeds(self):
2477
2877
        store = self.get_store(self)
2478
2878
        store._load_from_string('foo=bar\n')
 
2879
        # FIXME: There should be a better way than relying on the test
 
2880
        # parametrization to identify branch.conf -- vila 2011-0526
 
2881
        if self.store_id in ('branch', 'remote_branch'):
 
2882
            # branch stores requires write locked branches
 
2883
            self.addCleanup(store.branch.lock_write().unlock)
2479
2884
        section = store.get_mutable_section(None)
2480
2885
        section.remove('foo')
2481
2886
        store.save()
2502
2907
 
2503
2908
    def test_set_option_in_empty_store(self):
2504
2909
        store = self.get_store(self)
 
2910
        # FIXME: There should be a better way than relying on the test
 
2911
        # parametrization to identify branch.conf -- vila 2011-0526
 
2912
        if self.store_id in ('branch', 'remote_branch'):
 
2913
            # branch stores requires write locked branches
 
2914
            self.addCleanup(store.branch.lock_write().unlock)
2505
2915
        section = store.get_mutable_section(None)
2506
2916
        section.set('foo', 'bar')
2507
2917
        store.save()
2513
2923
    def test_set_option_in_default_section(self):
2514
2924
        store = self.get_store(self)
2515
2925
        store._load_from_string('')
 
2926
        # FIXME: There should be a better way than relying on the test
 
2927
        # parametrization to identify branch.conf -- vila 2011-0526
 
2928
        if self.store_id in ('branch', 'remote_branch'):
 
2929
            # branch stores requires write locked branches
 
2930
            self.addCleanup(store.branch.lock_write().unlock)
2516
2931
        section = store.get_mutable_section(None)
2517
2932
        section.set('foo', 'bar')
2518
2933
        store.save()
2524
2939
    def test_set_option_in_named_section(self):
2525
2940
        store = self.get_store(self)
2526
2941
        store._load_from_string('')
 
2942
        # FIXME: There should be a better way than relying on the test
 
2943
        # parametrization to identify branch.conf -- vila 2011-0526
 
2944
        if self.store_id in ('branch', 'remote_branch'):
 
2945
            # branch stores requires write locked branches
 
2946
            self.addCleanup(store.branch.lock_write().unlock)
2527
2947
        section = store.get_mutable_section('baz')
2528
2948
        section.set('foo', 'bar')
2529
2949
        store.save()
2533
2953
        self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2534
2954
 
2535
2955
    def test_load_hook(self):
2536
 
        # We first needs to ensure that the store exists
 
2956
        # First, we need to ensure that the store exists
2537
2957
        store = self.get_store(self)
 
2958
        # FIXME: There should be a better way than relying on the test
 
2959
        # parametrization to identify branch.conf -- vila 2011-0526
 
2960
        if self.store_id in ('branch', 'remote_branch'):
 
2961
            # branch stores requires write locked branches
 
2962
            self.addCleanup(store.branch.lock_write().unlock)
2538
2963
        section = store.get_mutable_section('baz')
2539
2964
        section.set('foo', 'bar')
2540
2965
        store.save()
2556
2981
        config.ConfigHooks.install_named_hook('save', hook, None)
2557
2982
        self.assertLength(0, calls)
2558
2983
        store = self.get_store(self)
 
2984
        # FIXME: There should be a better way than relying on the test
 
2985
        # parametrization to identify branch.conf -- vila 2011-0526
 
2986
        if self.store_id in ('branch', 'remote_branch'):
 
2987
            # branch stores requires write locked branches
 
2988
            self.addCleanup(store.branch.lock_write().unlock)
2559
2989
        section = store.get_mutable_section('baz')
2560
2990
        section.set('foo', 'bar')
2561
2991
        store.save()
2562
2992
        self.assertLength(1, calls)
2563
2993
        self.assertEquals((store,), calls[0])
2564
2994
 
2565
 
 
2566
 
class TestIniFileStore(TestStore):
 
2995
    def test_set_mark_dirty(self):
 
2996
        stack = config.MemoryStack('')
 
2997
        self.assertLength(0, stack.store.dirty_sections)
 
2998
        stack.set('foo', 'baz')
 
2999
        self.assertLength(1, stack.store.dirty_sections)
 
3000
        self.assertTrue(stack.store._need_saving())
 
3001
 
 
3002
    def test_remove_mark_dirty(self):
 
3003
        stack = config.MemoryStack('foo=bar')
 
3004
        self.assertLength(0, stack.store.dirty_sections)
 
3005
        stack.remove('foo')
 
3006
        self.assertLength(1, stack.store.dirty_sections)
 
3007
        self.assertTrue(stack.store._need_saving())
 
3008
 
 
3009
 
 
3010
class TestStoreSaveChanges(tests.TestCaseWithTransport):
 
3011
    """Tests that config changes are kept in memory and saved on-demand."""
 
3012
 
 
3013
    def setUp(self):
 
3014
        super(TestStoreSaveChanges, self).setUp()
 
3015
        self.transport = self.get_transport()
 
3016
        # Most of the tests involve two stores pointing to the same persistent
 
3017
        # storage to observe the effects of concurrent changes
 
3018
        self.st1 = config.TransportIniFileStore(self.transport, 'foo.conf')
 
3019
        self.st2 = config.TransportIniFileStore(self.transport, 'foo.conf')
 
3020
        self.warnings = []
 
3021
        def warning(*args):
 
3022
            self.warnings.append(args[0] % args[1:])
 
3023
        self.overrideAttr(trace, 'warning', warning)
 
3024
 
 
3025
    def has_store(self, store):
 
3026
        store_basename = urlutils.relative_url(self.transport.external_url(),
 
3027
                                               store.external_url())
 
3028
        return self.transport.has(store_basename)
 
3029
 
 
3030
    def get_stack(self, store):
 
3031
        # Any stack will do as long as it uses the right store, just a single
 
3032
        # no-name section is enough
 
3033
        return config.Stack([store.get_sections], store)
 
3034
 
 
3035
    def test_no_changes_no_save(self):
 
3036
        s = self.get_stack(self.st1)
 
3037
        s.store.save_changes()
 
3038
        self.assertEquals(False, self.has_store(self.st1))
 
3039
 
 
3040
    def test_unrelated_concurrent_update(self):
 
3041
        s1 = self.get_stack(self.st1)
 
3042
        s2 = self.get_stack(self.st2)
 
3043
        s1.set('foo', 'bar')
 
3044
        s2.set('baz', 'quux')
 
3045
        s1.store.save()
 
3046
        # Changes don't propagate magically
 
3047
        self.assertEquals(None, s1.get('baz'))
 
3048
        s2.store.save_changes()
 
3049
        self.assertEquals('quux', s2.get('baz'))
 
3050
        # Changes are acquired when saving
 
3051
        self.assertEquals('bar', s2.get('foo'))
 
3052
        # Since there is no overlap, no warnings are emitted
 
3053
        self.assertLength(0, self.warnings)
 
3054
 
 
3055
    def test_concurrent_update_modified(self):
 
3056
        s1 = self.get_stack(self.st1)
 
3057
        s2 = self.get_stack(self.st2)
 
3058
        s1.set('foo', 'bar')
 
3059
        s2.set('foo', 'baz')
 
3060
        s1.store.save()
 
3061
        # Last speaker wins
 
3062
        s2.store.save_changes()
 
3063
        self.assertEquals('baz', s2.get('foo'))
 
3064
        # But the user get a warning
 
3065
        self.assertLength(1, self.warnings)
 
3066
        warning = self.warnings[0]
 
3067
        self.assertStartsWith(warning, 'Option foo in section None')
 
3068
        self.assertEndsWith(warning, 'was changed from <CREATED> to bar.'
 
3069
                            ' The baz value will be saved.')
 
3070
 
 
3071
    def test_concurrent_deletion(self):
 
3072
        self.st1._load_from_string('foo=bar')
 
3073
        self.st1.save()
 
3074
        s1 = self.get_stack(self.st1)
 
3075
        s2 = self.get_stack(self.st2)
 
3076
        s1.remove('foo')
 
3077
        s2.remove('foo')
 
3078
        s1.store.save_changes()
 
3079
        # No warning yet
 
3080
        self.assertLength(0, self.warnings)
 
3081
        s2.store.save_changes()
 
3082
        # Now we get one
 
3083
        self.assertLength(1, self.warnings)
 
3084
        warning = self.warnings[0]
 
3085
        self.assertStartsWith(warning, 'Option foo in section None')
 
3086
        self.assertEndsWith(warning, 'was changed from bar to <CREATED>.'
 
3087
                            ' The <DELETED> value will be saved.')
 
3088
 
 
3089
 
 
3090
class TestQuotingIniFileStore(tests.TestCaseWithTransport):
 
3091
 
 
3092
    def get_store(self):
 
3093
        return config.TransportIniFileStore(self.get_transport(), 'foo.conf')
 
3094
 
 
3095
    def test_get_quoted_string(self):
 
3096
        store = self.get_store()
 
3097
        store._load_from_string('foo= " abc "')
 
3098
        stack = config.Stack([store.get_sections])
 
3099
        self.assertEquals(' abc ', stack.get('foo'))
 
3100
 
 
3101
    def test_set_quoted_string(self):
 
3102
        store = self.get_store()
 
3103
        stack = config.Stack([store.get_sections], store)
 
3104
        stack.set('foo', ' a b c ')
 
3105
        store.save()
 
3106
        self.assertFileEqual('foo = " a b c "' + os.linesep, 'foo.conf')
 
3107
 
 
3108
 
 
3109
class TestTransportIniFileStore(TestStore):
2567
3110
 
2568
3111
    def test_loading_unknown_file_fails(self):
2569
 
        store = config.IniFileStore(self.get_transport(), 'I-do-not-exist')
 
3112
        store = config.TransportIniFileStore(self.get_transport(),
 
3113
            'I-do-not-exist')
2570
3114
        self.assertRaises(errors.NoSuchFile, store.load)
2571
3115
 
2572
3116
    def test_invalid_content(self):
2573
 
        store = config.IniFileStore(self.get_transport(), 'foo.conf', )
 
3117
        store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
2574
3118
        self.assertEquals(False, store.is_loaded())
2575
3119
        exc = self.assertRaises(
2576
3120
            errors.ParseConfigError, store._load_from_string,
2584
3128
        # option names share the same name space...)
2585
3129
        # FIXME: This should be fixed by forbidding dicts as values ?
2586
3130
        # -- vila 2011-04-05
2587
 
        store = config.IniFileStore(self.get_transport(), 'foo.conf', )
 
3131
        store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
2588
3132
        store._load_from_string('''
2589
3133
foo=bar
2590
3134
l=1,2
2600
3144
        sections = list(store.get_sections())
2601
3145
        self.assertLength(4, sections)
2602
3146
        # The default section has no name.
2603
 
        # List values are provided as lists
2604
 
        self.assertSectionContent((None, {'foo': 'bar', 'l': ['1', '2']}),
 
3147
        # List values are provided as strings and need to be explicitly
 
3148
        # converted by specifying from_unicode=list_from_store at option
 
3149
        # registration
 
3150
        self.assertSectionContent((None, {'foo': 'bar', 'l': u'1,2'}),
2605
3151
                                  sections[0])
2606
3152
        self.assertSectionContent(
2607
3153
            ('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
2637
3183
 
2638
3184
    def setUp(self):
2639
3185
        super(TestConcurrentStoreUpdates, self).setUp()
2640
 
        self._content = 'one=1\ntwo=2\n'
2641
3186
        self.stack = self.get_stack(self)
2642
3187
        if not isinstance(self.stack, config._CompatibleStack):
2643
3188
            raise tests.TestNotApplicable(
2644
3189
                '%s is not meant to be compatible with the old config design'
2645
3190
                % (self.stack,))
2646
 
        self.stack.store._load_from_string(self._content)
 
3191
        self.stack.set('one', '1')
 
3192
        self.stack.set('two', '2')
2647
3193
        # Flush the store
2648
3194
        self.stack.store.save()
2649
3195
 
2753
3299
    # FIXME: It may be worth looking into removing the lock dir when it's not
2754
3300
    # needed anymore and look at possible fallouts for concurrent lockers. This
2755
3301
    # will matter if/when we use config files outside of bazaar directories
2756
 
    # (.bazaar or .bzr) -- vila 20110-04-11
 
3302
    # (.bazaar or .bzr) -- vila 20110-04-111
2757
3303
 
2758
3304
 
2759
3305
class TestSectionMatcher(TestStore):
2760
3306
 
2761
 
    scenarios = [('location', {'matcher': config.LocationMatcher})]
 
3307
    scenarios = [('location', {'matcher': config.LocationMatcher}),
 
3308
                 ('id', {'matcher': config.NameMatcher}),]
2762
3309
 
2763
 
    def get_store(self, file_name):
2764
 
        return config.IniFileStore(self.get_readonly_transport(), file_name)
 
3310
    def setUp(self):
 
3311
        super(TestSectionMatcher, self).setUp()
 
3312
        # Any simple store is good enough
 
3313
        self.get_store = config.test_store_builder_registry.get('configobj')
2765
3314
 
2766
3315
    def test_no_matches_for_empty_stores(self):
2767
 
        store = self.get_store('foo.conf')
 
3316
        store = self.get_store(self)
2768
3317
        store._load_from_string('')
2769
3318
        matcher = self.matcher(store, '/bar')
2770
3319
        self.assertEquals([], list(matcher.get_sections()))
2771
3320
 
2772
3321
    def test_build_doesnt_load_store(self):
2773
 
        store = self.get_store('foo.conf')
 
3322
        store = self.get_store(self)
2774
3323
        matcher = self.matcher(store, '/bar')
2775
3324
        self.assertFalse(store.is_loaded())
2776
3325
 
2779
3328
 
2780
3329
    def get_section(self, options, extra_path):
2781
3330
        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)
 
3331
        return config.LocationSection(section, extra_path)
2784
3332
 
2785
3333
    def test_simple_option(self):
2786
3334
        section = self.get_section({'foo': 'bar'}, '')
2800
3348
 
2801
3349
class TestLocationMatcher(TestStore):
2802
3350
 
2803
 
    def get_store(self, file_name):
2804
 
        return config.IniFileStore(self.get_readonly_transport(), file_name)
 
3351
    def setUp(self):
 
3352
        super(TestLocationMatcher, self).setUp()
 
3353
        # Any simple store is good enough
 
3354
        self.get_store = config.test_store_builder_registry.get('configobj')
 
3355
 
 
3356
    def test_unrelated_section_excluded(self):
 
3357
        store = self.get_store(self)
 
3358
        store._load_from_string('''
 
3359
[/foo]
 
3360
section=/foo
 
3361
[/foo/baz]
 
3362
section=/foo/baz
 
3363
[/foo/bar]
 
3364
section=/foo/bar
 
3365
[/foo/bar/baz]
 
3366
section=/foo/bar/baz
 
3367
[/quux/quux]
 
3368
section=/quux/quux
 
3369
''')
 
3370
        self.assertEquals(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
 
3371
                           '/quux/quux'],
 
3372
                          [section.id for _, section in store.get_sections()])
 
3373
        matcher = config.LocationMatcher(store, '/foo/bar/quux')
 
3374
        sections = [section for _, section in matcher.get_sections()]
 
3375
        self.assertEquals(['/foo/bar', '/foo'],
 
3376
                          [section.id for section in sections])
 
3377
        self.assertEquals(['quux', 'bar/quux'],
 
3378
                          [section.extra_path for section in sections])
2805
3379
 
2806
3380
    def test_more_specific_sections_first(self):
2807
 
        store = self.get_store('foo.conf')
 
3381
        store = self.get_store(self)
2808
3382
        store._load_from_string('''
2809
3383
[/foo]
2810
3384
section=/foo
2812
3386
section=/foo/bar
2813
3387
''')
2814
3388
        self.assertEquals(['/foo', '/foo/bar'],
2815
 
                          [section.id for section in store.get_sections()])
 
3389
                          [section.id for _, section in store.get_sections()])
2816
3390
        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])
 
3391
        sections = [section for _, section in matcher.get_sections()]
2820
3392
        self.assertEquals(['/foo/bar', '/foo'],
2821
3393
                          [section.id for section in sections])
2822
3394
        self.assertEquals(['baz', 'bar/baz'],
2825
3397
    def test_appendpath_in_no_name_section(self):
2826
3398
        # It's a bit weird to allow appendpath in a no-name section, but
2827
3399
        # someone may found a use for it
2828
 
        store = self.get_store('foo.conf')
 
3400
        store = self.get_store(self)
2829
3401
        store._load_from_string('''
2830
3402
foo=bar
2831
3403
foo:policy = appendpath
2833
3405
        matcher = config.LocationMatcher(store, 'dir/subdir')
2834
3406
        sections = list(matcher.get_sections())
2835
3407
        self.assertLength(1, sections)
2836
 
        self.assertEquals('bar/dir/subdir', sections[0].get('foo'))
 
3408
        self.assertEquals('bar/dir/subdir', sections[0][1].get('foo'))
2837
3409
 
2838
3410
    def test_file_urls_are_normalized(self):
2839
 
        store = self.get_store('foo.conf')
 
3411
        store = self.get_store(self)
2840
3412
        if sys.platform == 'win32':
2841
3413
            expected_url = 'file:///C:/dir/subdir'
2842
3414
            expected_location = 'C:/dir/subdir'
2847
3419
        self.assertEquals(expected_location, matcher.location)
2848
3420
 
2849
3421
 
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'))
 
3422
class TestStartingPathMatcher(TestStore):
 
3423
 
 
3424
    def setUp(self):
 
3425
        super(TestStartingPathMatcher, self).setUp()
 
3426
        # Any simple store is good enough
 
3427
        self.store = config.IniFileStore()
 
3428
 
 
3429
    def assertSectionIDs(self, expected, location, content):
 
3430
        self.store._load_from_string(content)
 
3431
        matcher = config.StartingPathMatcher(self.store, location)
 
3432
        sections = list(matcher.get_sections())
 
3433
        self.assertLength(len(expected), sections)
 
3434
        self.assertEqual(expected, [section.id for _, section in sections])
 
3435
        return sections
 
3436
 
 
3437
    def test_empty(self):
 
3438
        self.assertSectionIDs([], self.get_url(), '')
 
3439
 
 
3440
    def test_url_vs_local_paths(self):
 
3441
        # The matcher location is an url and the section names are local paths
 
3442
        sections = self.assertSectionIDs(['/foo/bar', '/foo'],
 
3443
                                         'file:///foo/bar/baz', '''\
 
3444
[/foo]
 
3445
[/foo/bar]
 
3446
''')
 
3447
 
 
3448
    def test_local_path_vs_url(self):
 
3449
        # The matcher location is a local path and the section names are urls
 
3450
        sections = self.assertSectionIDs(['file:///foo/bar', 'file:///foo'],
 
3451
                                         '/foo/bar/baz', '''\
 
3452
[file:///foo]
 
3453
[file:///foo/bar]
 
3454
''')
 
3455
 
 
3456
 
 
3457
    def test_no_name_section_included_when_present(self):
 
3458
        # Note that other tests will cover the case where the no-name section
 
3459
        # is empty and as such, not included.
 
3460
        sections = self.assertSectionIDs(['/foo/bar', '/foo', None],
 
3461
                                         '/foo/bar/baz', '''\
 
3462
option = defined so the no-name section exists
 
3463
[/foo]
 
3464
[/foo/bar]
 
3465
''')
 
3466
        self.assertEquals(['baz', 'bar/baz', '/foo/bar/baz'],
 
3467
                          [s.locals['relpath'] for _, s in sections])
 
3468
 
 
3469
    def test_order_reversed(self):
 
3470
        self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
 
3471
[/foo]
 
3472
[/foo/bar]
 
3473
''')
 
3474
 
 
3475
    def test_unrelated_section_excluded(self):
 
3476
        self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
 
3477
[/foo]
 
3478
[/foo/qux]
 
3479
[/foo/bar]
 
3480
''')
 
3481
 
 
3482
    def test_glob_included(self):
 
3483
        sections = self.assertSectionIDs(['/foo/*/baz', '/foo/b*', '/foo'],
 
3484
                                         '/foo/bar/baz', '''\
 
3485
[/foo]
 
3486
[/foo/qux]
 
3487
[/foo/b*]
 
3488
[/foo/*/baz]
 
3489
''')
 
3490
        # Note that 'baz' as a relpath for /foo/b* is not fully correct, but
 
3491
        # nothing really is... as far using {relpath} to append it to something
 
3492
        # else, this seems good enough though.
 
3493
        self.assertEquals(['', 'baz', 'bar/baz'],
 
3494
                          [s.locals['relpath'] for _, s in sections])
 
3495
 
 
3496
    def test_respect_order(self):
 
3497
        self.assertSectionIDs(['/foo', '/foo/b*', '/foo/*/baz'],
 
3498
                              '/foo/bar/baz', '''\
 
3499
[/foo/*/baz]
 
3500
[/foo/qux]
 
3501
[/foo/b*]
 
3502
[/foo]
 
3503
''')
 
3504
 
 
3505
 
 
3506
class TestNameMatcher(TestStore):
 
3507
 
 
3508
    def setUp(self):
 
3509
        super(TestNameMatcher, self).setUp()
 
3510
        self.matcher = config.NameMatcher
 
3511
        # Any simple store is good enough
 
3512
        self.get_store = config.test_store_builder_registry.get('configobj')
 
3513
 
 
3514
    def get_matching_sections(self, name):
 
3515
        store = self.get_store(self)
 
3516
        store._load_from_string('''
 
3517
[foo]
 
3518
option=foo
 
3519
[foo/baz]
 
3520
option=foo/baz
 
3521
[bar]
 
3522
option=bar
 
3523
''')
 
3524
        matcher = self.matcher(store, name)
 
3525
        return list(matcher.get_sections())
 
3526
 
 
3527
    def test_matching(self):
 
3528
        sections = self.get_matching_sections('foo')
 
3529
        self.assertLength(1, sections)
 
3530
        self.assertSectionContent(('foo', {'option': 'foo'}), sections[0])
 
3531
 
 
3532
    def test_not_matching(self):
 
3533
        sections = self.get_matching_sections('baz')
 
3534
        self.assertLength(0, sections)
 
3535
 
 
3536
 
 
3537
class TestBaseStackGet(tests.TestCase):
 
3538
 
 
3539
    def setUp(self):
 
3540
        super(TestBaseStackGet, self).setUp()
 
3541
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
 
3542
 
 
3543
    def test_get_first_definition(self):
 
3544
        store1 = config.IniFileStore()
 
3545
        store1._load_from_string('foo=bar')
 
3546
        store2 = config.IniFileStore()
 
3547
        store2._load_from_string('foo=baz')
 
3548
        conf = config.Stack([store1.get_sections, store2.get_sections])
 
3549
        self.assertEquals('bar', conf.get('foo'))
2859
3550
 
2860
3551
    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)
 
3552
        config.option_registry.register(config.Option('foo', default='bar'))
 
3553
        conf_stack = config.Stack([])
2865
3554
        self.assertEquals('bar', conf_stack.get('foo'))
2866
3555
 
2867
3556
    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)
 
3557
        config.option_registry.register(config.Option('foo'))
 
3558
        conf_stack = config.Stack([])
2872
3559
        self.assertEquals(None, conf_stack.get('foo'))
2873
3560
 
2874
3561
    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())
 
3562
        conf_stack = config.Stack([])
2878
3563
        self.assertEquals(None, conf_stack.get('foo'))
2879
3564
 
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
3565
    def test_get_for_empty_section_callable(self):
2893
3566
        conf_stack = config.Stack([lambda : []])
2894
3567
        self.assertEquals(None, conf_stack.get('foo'))
2895
3568
 
2896
3569
    def test_get_for_broken_callable(self):
2897
3570
        # Trying to use and invalid callable raises an exception on first use
2898
 
        conf_stack = config.Stack([lambda : object()])
 
3571
        conf_stack = config.Stack([object])
2899
3572
        self.assertRaises(TypeError, conf_stack.get, 'foo')
2900
3573
 
2901
3574
 
 
3575
class TestStackWithSimpleStore(tests.TestCase):
 
3576
 
 
3577
    def setUp(self):
 
3578
        super(TestStackWithSimpleStore, self).setUp()
 
3579
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
 
3580
        self.registry = config.option_registry
 
3581
 
 
3582
    def get_conf(self, content=None):
 
3583
        return config.MemoryStack(content)
 
3584
 
 
3585
    def test_override_value_from_env(self):
 
3586
        self.overrideEnv('FOO', None)
 
3587
        self.registry.register(
 
3588
            config.Option('foo', default='bar', override_from_env=['FOO']))
 
3589
        self.overrideEnv('FOO', 'quux')
 
3590
        # Env variable provides a default taking over the option one
 
3591
        conf = self.get_conf('foo=store')
 
3592
        self.assertEquals('quux', conf.get('foo'))
 
3593
 
 
3594
    def test_first_override_value_from_env_wins(self):
 
3595
        self.overrideEnv('NO_VALUE', None)
 
3596
        self.overrideEnv('FOO', None)
 
3597
        self.overrideEnv('BAZ', None)
 
3598
        self.registry.register(
 
3599
            config.Option('foo', default='bar',
 
3600
                          override_from_env=['NO_VALUE', 'FOO', 'BAZ']))
 
3601
        self.overrideEnv('FOO', 'foo')
 
3602
        self.overrideEnv('BAZ', 'baz')
 
3603
        # The first env var set wins
 
3604
        conf = self.get_conf('foo=store')
 
3605
        self.assertEquals('foo', conf.get('foo'))
 
3606
 
 
3607
 
 
3608
class TestMemoryStack(tests.TestCase):
 
3609
 
 
3610
    def test_get(self):
 
3611
        conf = config.MemoryStack('foo=bar')
 
3612
        self.assertEquals('bar', conf.get('foo'))
 
3613
 
 
3614
    def test_set(self):
 
3615
        conf = config.MemoryStack('foo=bar')
 
3616
        conf.set('foo', 'baz')
 
3617
        self.assertEquals('baz', conf.get('foo'))
 
3618
 
 
3619
    def test_no_content(self):
 
3620
        conf = config.MemoryStack()
 
3621
        # No content means no loading
 
3622
        self.assertFalse(conf.store.is_loaded())
 
3623
        self.assertRaises(NotImplementedError, conf.get, 'foo')
 
3624
        # But a content can still be provided
 
3625
        conf.store._load_from_string('foo=bar')
 
3626
        self.assertEquals('bar', conf.get('foo'))
 
3627
 
 
3628
 
 
3629
class TestStackIterSections(tests.TestCase):
 
3630
 
 
3631
    def test_empty_stack(self):
 
3632
        conf = config.Stack([])
 
3633
        sections = list(conf.iter_sections())
 
3634
        self.assertLength(0, sections)
 
3635
 
 
3636
    def test_empty_store(self):
 
3637
        store = config.IniFileStore()
 
3638
        store._load_from_string('')
 
3639
        conf = config.Stack([store.get_sections])
 
3640
        sections = list(conf.iter_sections())
 
3641
        self.assertLength(0, sections)
 
3642
 
 
3643
    def test_simple_store(self):
 
3644
        store = config.IniFileStore()
 
3645
        store._load_from_string('foo=bar')
 
3646
        conf = config.Stack([store.get_sections])
 
3647
        tuples = list(conf.iter_sections())
 
3648
        self.assertLength(1, tuples)
 
3649
        (found_store, found_section) = tuples[0]
 
3650
        self.assertIs(store, found_store)
 
3651
 
 
3652
    def test_two_stores(self):
 
3653
        store1 = config.IniFileStore()
 
3654
        store1._load_from_string('foo=bar')
 
3655
        store2 = config.IniFileStore()
 
3656
        store2._load_from_string('bar=qux')
 
3657
        conf = config.Stack([store1.get_sections, store2.get_sections])
 
3658
        tuples = list(conf.iter_sections())
 
3659
        self.assertLength(2, tuples)
 
3660
        self.assertIs(store1, tuples[0][0])
 
3661
        self.assertIs(store2, tuples[1][0])
 
3662
 
 
3663
 
2902
3664
class TestStackWithTransport(tests.TestCaseWithTransport):
2903
3665
 
2904
3666
    scenarios = [(key, {'get_stack': builder}) for key, builder
2914
3676
 
2915
3677
class TestStackGet(TestStackWithTransport):
2916
3678
 
 
3679
    def setUp(self):
 
3680
        super(TestStackGet, self).setUp()
 
3681
        self.conf = self.get_stack(self)
 
3682
 
2917
3683
    def test_get_for_empty_stack(self):
2918
 
        conf = self.get_stack(self)
2919
 
        self.assertEquals(None, conf.get('foo'))
 
3684
        self.assertEquals(None, self.conf.get('foo'))
2920
3685
 
2921
3686
    def test_get_hook(self):
2922
 
        conf = self.get_stack(self)
2923
 
        conf.store._load_from_string('foo=bar')
 
3687
        self.conf.set('foo', 'bar')
2924
3688
        calls = []
2925
3689
        def hook(*args):
2926
3690
            calls.append(args)
2927
3691
        config.ConfigHooks.install_named_hook('get', hook, None)
2928
3692
        self.assertLength(0, calls)
2929
 
        value = conf.get('foo')
 
3693
        value = self.conf.get('foo')
2930
3694
        self.assertEquals('bar', value)
2931
3695
        self.assertLength(1, calls)
2932
 
        self.assertEquals((conf, 'foo', 'bar'), calls[0])
 
3696
        self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
 
3697
 
 
3698
 
 
3699
class TestStackGetWithConverter(tests.TestCase):
 
3700
 
 
3701
    def setUp(self):
 
3702
        super(TestStackGetWithConverter, self).setUp()
 
3703
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
 
3704
        self.registry = config.option_registry
 
3705
 
 
3706
    def get_conf(self, content=None):
 
3707
        return config.MemoryStack(content)
 
3708
 
 
3709
    def register_bool_option(self, name, default=None, default_from_env=None):
 
3710
        b = config.Option(name, help='A boolean.',
 
3711
                          default=default, default_from_env=default_from_env,
 
3712
                          from_unicode=config.bool_from_store)
 
3713
        self.registry.register(b)
 
3714
 
 
3715
    def test_get_default_bool_None(self):
 
3716
        self.register_bool_option('foo')
 
3717
        conf = self.get_conf('')
 
3718
        self.assertEquals(None, conf.get('foo'))
 
3719
 
 
3720
    def test_get_default_bool_True(self):
 
3721
        self.register_bool_option('foo', u'True')
 
3722
        conf = self.get_conf('')
 
3723
        self.assertEquals(True, conf.get('foo'))
 
3724
 
 
3725
    def test_get_default_bool_False(self):
 
3726
        self.register_bool_option('foo', False)
 
3727
        conf = self.get_conf('')
 
3728
        self.assertEquals(False, conf.get('foo'))
 
3729
 
 
3730
    def test_get_default_bool_False_as_string(self):
 
3731
        self.register_bool_option('foo', u'False')
 
3732
        conf = self.get_conf('')
 
3733
        self.assertEquals(False, conf.get('foo'))
 
3734
 
 
3735
    def test_get_default_bool_from_env_converted(self):
 
3736
        self.register_bool_option('foo', u'True', default_from_env=['FOO'])
 
3737
        self.overrideEnv('FOO', 'False')
 
3738
        conf = self.get_conf('')
 
3739
        self.assertEquals(False, conf.get('foo'))
 
3740
 
 
3741
    def test_get_default_bool_when_conversion_fails(self):
 
3742
        self.register_bool_option('foo', default='True')
 
3743
        conf = self.get_conf('foo=invalid boolean')
 
3744
        self.assertEquals(True, conf.get('foo'))
 
3745
 
 
3746
    def register_integer_option(self, name,
 
3747
                                default=None, default_from_env=None):
 
3748
        i = config.Option(name, help='An integer.',
 
3749
                          default=default, default_from_env=default_from_env,
 
3750
                          from_unicode=config.int_from_store)
 
3751
        self.registry.register(i)
 
3752
 
 
3753
    def test_get_default_integer_None(self):
 
3754
        self.register_integer_option('foo')
 
3755
        conf = self.get_conf('')
 
3756
        self.assertEquals(None, conf.get('foo'))
 
3757
 
 
3758
    def test_get_default_integer(self):
 
3759
        self.register_integer_option('foo', 42)
 
3760
        conf = self.get_conf('')
 
3761
        self.assertEquals(42, conf.get('foo'))
 
3762
 
 
3763
    def test_get_default_integer_as_string(self):
 
3764
        self.register_integer_option('foo', u'42')
 
3765
        conf = self.get_conf('')
 
3766
        self.assertEquals(42, conf.get('foo'))
 
3767
 
 
3768
    def test_get_default_integer_from_env(self):
 
3769
        self.register_integer_option('foo', default_from_env=['FOO'])
 
3770
        self.overrideEnv('FOO', '18')
 
3771
        conf = self.get_conf('')
 
3772
        self.assertEquals(18, conf.get('foo'))
 
3773
 
 
3774
    def test_get_default_integer_when_conversion_fails(self):
 
3775
        self.register_integer_option('foo', default='12')
 
3776
        conf = self.get_conf('foo=invalid integer')
 
3777
        self.assertEquals(12, conf.get('foo'))
 
3778
 
 
3779
    def register_list_option(self, name, default=None, default_from_env=None):
 
3780
        l = config.ListOption(name, help='A list.', default=default,
 
3781
                              default_from_env=default_from_env)
 
3782
        self.registry.register(l)
 
3783
 
 
3784
    def test_get_default_list_None(self):
 
3785
        self.register_list_option('foo')
 
3786
        conf = self.get_conf('')
 
3787
        self.assertEquals(None, conf.get('foo'))
 
3788
 
 
3789
    def test_get_default_list_empty(self):
 
3790
        self.register_list_option('foo', '')
 
3791
        conf = self.get_conf('')
 
3792
        self.assertEquals([], conf.get('foo'))
 
3793
 
 
3794
    def test_get_default_list_from_env(self):
 
3795
        self.register_list_option('foo', default_from_env=['FOO'])
 
3796
        self.overrideEnv('FOO', '')
 
3797
        conf = self.get_conf('')
 
3798
        self.assertEquals([], conf.get('foo'))
 
3799
 
 
3800
    def test_get_with_list_converter_no_item(self):
 
3801
        self.register_list_option('foo', None)
 
3802
        conf = self.get_conf('foo=,')
 
3803
        self.assertEquals([], conf.get('foo'))
 
3804
 
 
3805
    def test_get_with_list_converter_many_items(self):
 
3806
        self.register_list_option('foo', None)
 
3807
        conf = self.get_conf('foo=m,o,r,e')
 
3808
        self.assertEquals(['m', 'o', 'r', 'e'], conf.get('foo'))
 
3809
 
 
3810
    def test_get_with_list_converter_embedded_spaces_many_items(self):
 
3811
        self.register_list_option('foo', None)
 
3812
        conf = self.get_conf('foo=" bar", "baz "')
 
3813
        self.assertEquals([' bar', 'baz '], conf.get('foo'))
 
3814
 
 
3815
    def test_get_with_list_converter_stripped_spaces_many_items(self):
 
3816
        self.register_list_option('foo', None)
 
3817
        conf = self.get_conf('foo= bar ,  baz ')
 
3818
        self.assertEquals(['bar', 'baz'], conf.get('foo'))
 
3819
 
 
3820
 
 
3821
class TestIterOptionRefs(tests.TestCase):
 
3822
    """iter_option_refs is a bit unusual, document some cases."""
 
3823
 
 
3824
    def assertRefs(self, expected, string):
 
3825
        self.assertEquals(expected, list(config.iter_option_refs(string)))
 
3826
 
 
3827
    def test_empty(self):
 
3828
        self.assertRefs([(False, '')], '')
 
3829
 
 
3830
    def test_no_refs(self):
 
3831
        self.assertRefs([(False, 'foo bar')], 'foo bar')
 
3832
 
 
3833
    def test_single_ref(self):
 
3834
        self.assertRefs([(False, ''), (True, '{foo}'), (False, '')], '{foo}')
 
3835
 
 
3836
    def test_broken_ref(self):
 
3837
        self.assertRefs([(False, '{foo')], '{foo')
 
3838
 
 
3839
    def test_embedded_ref(self):
 
3840
        self.assertRefs([(False, '{'), (True, '{foo}'), (False, '}')],
 
3841
                        '{{foo}}')
 
3842
 
 
3843
    def test_two_refs(self):
 
3844
        self.assertRefs([(False, ''), (True, '{foo}'),
 
3845
                         (False, ''), (True, '{bar}'),
 
3846
                         (False, ''),],
 
3847
                        '{foo}{bar}')
 
3848
 
 
3849
    def test_newline_in_refs_are_not_matched(self):
 
3850
        self.assertRefs([(False, '{\nxx}{xx\n}{{\n}}')], '{\nxx}{xx\n}{{\n}}')
 
3851
 
 
3852
 
 
3853
class TestStackExpandOptions(tests.TestCaseWithTransport):
 
3854
 
 
3855
    def setUp(self):
 
3856
        super(TestStackExpandOptions, self).setUp()
 
3857
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
 
3858
        self.registry = config.option_registry
 
3859
        store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
 
3860
        self.conf = config.Stack([store.get_sections], store)
 
3861
 
 
3862
    def assertExpansion(self, expected, string, env=None):
 
3863
        self.assertEquals(expected, self.conf.expand_options(string, env))
 
3864
 
 
3865
    def test_no_expansion(self):
 
3866
        self.assertExpansion('foo', 'foo')
 
3867
 
 
3868
    def test_expand_default_value(self):
 
3869
        self.conf.store._load_from_string('bar=baz')
 
3870
        self.registry.register(config.Option('foo', default=u'{bar}'))
 
3871
        self.assertEquals('baz', self.conf.get('foo', expand=True))
 
3872
 
 
3873
    def test_expand_default_from_env(self):
 
3874
        self.conf.store._load_from_string('bar=baz')
 
3875
        self.registry.register(config.Option('foo', default_from_env=['FOO']))
 
3876
        self.overrideEnv('FOO', '{bar}')
 
3877
        self.assertEquals('baz', self.conf.get('foo', expand=True))
 
3878
 
 
3879
    def test_expand_default_on_failed_conversion(self):
 
3880
        self.conf.store._load_from_string('baz=bogus\nbar=42\nfoo={baz}')
 
3881
        self.registry.register(
 
3882
            config.Option('foo', default=u'{bar}',
 
3883
                          from_unicode=config.int_from_store))
 
3884
        self.assertEquals(42, self.conf.get('foo', expand=True))
 
3885
 
 
3886
    def test_env_adding_options(self):
 
3887
        self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
 
3888
 
 
3889
    def test_env_overriding_options(self):
 
3890
        self.conf.store._load_from_string('foo=baz')
 
3891
        self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
 
3892
 
 
3893
    def test_simple_ref(self):
 
3894
        self.conf.store._load_from_string('foo=xxx')
 
3895
        self.assertExpansion('xxx', '{foo}')
 
3896
 
 
3897
    def test_unknown_ref(self):
 
3898
        self.assertRaises(errors.ExpandingUnknownOption,
 
3899
                          self.conf.expand_options, '{foo}')
 
3900
 
 
3901
    def test_indirect_ref(self):
 
3902
        self.conf.store._load_from_string('''
 
3903
foo=xxx
 
3904
bar={foo}
 
3905
''')
 
3906
        self.assertExpansion('xxx', '{bar}')
 
3907
 
 
3908
    def test_embedded_ref(self):
 
3909
        self.conf.store._load_from_string('''
 
3910
foo=xxx
 
3911
bar=foo
 
3912
''')
 
3913
        self.assertExpansion('xxx', '{{bar}}')
 
3914
 
 
3915
    def test_simple_loop(self):
 
3916
        self.conf.store._load_from_string('foo={foo}')
 
3917
        self.assertRaises(errors.OptionExpansionLoop,
 
3918
                          self.conf.expand_options, '{foo}')
 
3919
 
 
3920
    def test_indirect_loop(self):
 
3921
        self.conf.store._load_from_string('''
 
3922
foo={bar}
 
3923
bar={baz}
 
3924
baz={foo}''')
 
3925
        e = self.assertRaises(errors.OptionExpansionLoop,
 
3926
                              self.conf.expand_options, '{foo}')
 
3927
        self.assertEquals('foo->bar->baz', e.refs)
 
3928
        self.assertEquals('{foo}', e.string)
 
3929
 
 
3930
    def test_list(self):
 
3931
        self.conf.store._load_from_string('''
 
3932
foo=start
 
3933
bar=middle
 
3934
baz=end
 
3935
list={foo},{bar},{baz}
 
3936
''')
 
3937
        self.registry.register(
 
3938
            config.ListOption('list'))
 
3939
        self.assertEquals(['start', 'middle', 'end'],
 
3940
                           self.conf.get('list', expand=True))
 
3941
 
 
3942
    def test_cascading_list(self):
 
3943
        self.conf.store._load_from_string('''
 
3944
foo=start,{bar}
 
3945
bar=middle,{baz}
 
3946
baz=end
 
3947
list={foo}
 
3948
''')
 
3949
        self.registry.register(config.ListOption('list'))
 
3950
        # Register an intermediate option as a list to ensure no conversion
 
3951
        # happen while expanding. Conversion should only occur for the original
 
3952
        # option ('list' here).
 
3953
        self.registry.register(config.ListOption('baz'))
 
3954
        self.assertEquals(['start', 'middle', 'end'],
 
3955
                           self.conf.get('list', expand=True))
 
3956
 
 
3957
    def test_pathologically_hidden_list(self):
 
3958
        self.conf.store._load_from_string('''
 
3959
foo=bin
 
3960
bar=go
 
3961
start={foo
 
3962
middle=},{
 
3963
end=bar}
 
3964
hidden={start}{middle}{end}
 
3965
''')
 
3966
        # What matters is what the registration says, the conversion happens
 
3967
        # only after all expansions have been performed
 
3968
        self.registry.register(config.ListOption('hidden'))
 
3969
        self.assertEquals(['bin', 'go'],
 
3970
                          self.conf.get('hidden', expand=True))
 
3971
 
 
3972
 
 
3973
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
 
3974
 
 
3975
    def setUp(self):
 
3976
        super(TestStackCrossSectionsExpand, self).setUp()
 
3977
 
 
3978
    def get_config(self, location, string):
 
3979
        if string is None:
 
3980
            string = ''
 
3981
        # Since we don't save the config we won't strictly require to inherit
 
3982
        # from TestCaseInTempDir, but an error occurs so quickly...
 
3983
        c = config.LocationStack(location)
 
3984
        c.store._load_from_string(string)
 
3985
        return c
 
3986
 
 
3987
    def test_dont_cross_unrelated_section(self):
 
3988
        c = self.get_config('/another/branch/path','''
 
3989
[/one/branch/path]
 
3990
foo = hello
 
3991
bar = {foo}/2
 
3992
 
 
3993
[/another/branch/path]
 
3994
bar = {foo}/2
 
3995
''')
 
3996
        self.assertRaises(errors.ExpandingUnknownOption,
 
3997
                          c.get, 'bar', expand=True)
 
3998
 
 
3999
    def test_cross_related_sections(self):
 
4000
        c = self.get_config('/project/branch/path','''
 
4001
[/project]
 
4002
foo = qu
 
4003
 
 
4004
[/project/branch/path]
 
4005
bar = {foo}ux
 
4006
''')
 
4007
        self.assertEquals('quux', c.get('bar', expand=True))
 
4008
 
 
4009
 
 
4010
class TestStackCrossStoresExpand(tests.TestCaseWithTransport):
 
4011
 
 
4012
    def test_cross_global_locations(self):
 
4013
        l_store = config.LocationStore()
 
4014
        l_store._load_from_string('''
 
4015
[/branch]
 
4016
lfoo = loc-foo
 
4017
lbar = {gbar}
 
4018
''')
 
4019
        l_store.save()
 
4020
        g_store = config.GlobalStore()
 
4021
        g_store._load_from_string('''
 
4022
[DEFAULT]
 
4023
gfoo = {lfoo}
 
4024
gbar = glob-bar
 
4025
''')
 
4026
        g_store.save()
 
4027
        stack = config.LocationStack('/branch')
 
4028
        self.assertEquals('glob-bar', stack.get('lbar', expand=True))
 
4029
        self.assertEquals('loc-foo', stack.get('gfoo', expand=True))
 
4030
 
 
4031
 
 
4032
class TestStackExpandSectionLocals(tests.TestCaseWithTransport):
 
4033
 
 
4034
    def test_expand_locals_empty(self):
 
4035
        l_store = config.LocationStore()
 
4036
        l_store._load_from_string('''
 
4037
[/home/user/project]
 
4038
base = {basename}
 
4039
rel = {relpath}
 
4040
''')
 
4041
        l_store.save()
 
4042
        stack = config.LocationStack('/home/user/project/')
 
4043
        self.assertEquals('', stack.get('base', expand=True))
 
4044
        self.assertEquals('', stack.get('rel', expand=True))
 
4045
 
 
4046
    def test_expand_basename_locally(self):
 
4047
        l_store = config.LocationStore()
 
4048
        l_store._load_from_string('''
 
4049
[/home/user/project]
 
4050
bfoo = {basename}
 
4051
''')
 
4052
        l_store.save()
 
4053
        stack = config.LocationStack('/home/user/project/branch')
 
4054
        self.assertEquals('branch', stack.get('bfoo', expand=True))
 
4055
 
 
4056
    def test_expand_basename_locally_longer_path(self):
 
4057
        l_store = config.LocationStore()
 
4058
        l_store._load_from_string('''
 
4059
[/home/user]
 
4060
bfoo = {basename}
 
4061
''')
 
4062
        l_store.save()
 
4063
        stack = config.LocationStack('/home/user/project/dir/branch')
 
4064
        self.assertEquals('branch', stack.get('bfoo', expand=True))
 
4065
 
 
4066
    def test_expand_relpath_locally(self):
 
4067
        l_store = config.LocationStore()
 
4068
        l_store._load_from_string('''
 
4069
[/home/user/project]
 
4070
lfoo = loc-foo/{relpath}
 
4071
''')
 
4072
        l_store.save()
 
4073
        stack = config.LocationStack('/home/user/project/branch')
 
4074
        self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
 
4075
 
 
4076
    def test_expand_relpath_unknonw_in_global(self):
 
4077
        g_store = config.GlobalStore()
 
4078
        g_store._load_from_string('''
 
4079
[DEFAULT]
 
4080
gfoo = {relpath}
 
4081
''')
 
4082
        g_store.save()
 
4083
        stack = config.LocationStack('/home/user/project/branch')
 
4084
        self.assertRaises(errors.ExpandingUnknownOption,
 
4085
                          stack.get, 'gfoo', expand=True)
 
4086
 
 
4087
    def test_expand_local_option_locally(self):
 
4088
        l_store = config.LocationStore()
 
4089
        l_store._load_from_string('''
 
4090
[/home/user/project]
 
4091
lfoo = loc-foo/{relpath}
 
4092
lbar = {gbar}
 
4093
''')
 
4094
        l_store.save()
 
4095
        g_store = config.GlobalStore()
 
4096
        g_store._load_from_string('''
 
4097
[DEFAULT]
 
4098
gfoo = {lfoo}
 
4099
gbar = glob-bar
 
4100
''')
 
4101
        g_store.save()
 
4102
        stack = config.LocationStack('/home/user/project/branch')
 
4103
        self.assertEquals('glob-bar', stack.get('lbar', expand=True))
 
4104
        self.assertEquals('loc-foo/branch', stack.get('gfoo', expand=True))
 
4105
 
 
4106
    def test_locals_dont_leak(self):
 
4107
        """Make sure we chose the right local in presence of several sections.
 
4108
        """
 
4109
        l_store = config.LocationStore()
 
4110
        l_store._load_from_string('''
 
4111
[/home/user]
 
4112
lfoo = loc-foo/{relpath}
 
4113
[/home/user/project]
 
4114
lfoo = loc-foo/{relpath}
 
4115
''')
 
4116
        l_store.save()
 
4117
        stack = config.LocationStack('/home/user/project/branch')
 
4118
        self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
 
4119
        stack = config.LocationStack('/home/user/bar/baz')
 
4120
        self.assertEquals('loc-foo/bar/baz', stack.get('lfoo', expand=True))
 
4121
 
2933
4122
 
2934
4123
 
2935
4124
class TestStackSet(TestStackWithTransport):
2936
4125
 
2937
4126
    def test_simple_set(self):
2938
4127
        conf = self.get_stack(self)
2939
 
        conf.store._load_from_string('foo=bar')
2940
 
        self.assertEquals('bar', conf.get('foo'))
 
4128
        self.assertEquals(None, conf.get('foo'))
2941
4129
        conf.set('foo', 'baz')
2942
4130
        # Did we get it back ?
2943
4131
        self.assertEquals('baz', conf.get('foo'))
2963
4151
 
2964
4152
    def test_remove_existing(self):
2965
4153
        conf = self.get_stack(self)
2966
 
        conf.store._load_from_string('foo=bar')
 
4154
        conf.set('foo', 'bar')
2967
4155
        self.assertEquals('bar', conf.get('foo'))
2968
4156
        conf.remove('foo')
2969
4157
        # Did we get it back ?
2980
4168
        config.ConfigHooks.install_named_hook('remove', hook, None)
2981
4169
        self.assertLength(0, calls)
2982
4170
        conf = self.get_stack(self)
2983
 
        conf.store._load_from_string('foo=bar')
 
4171
        conf.set('foo', 'bar')
2984
4172
        conf.remove('foo')
2985
4173
        self.assertLength(1, calls)
2986
4174
        self.assertEquals((conf, 'foo'), calls[0])
3161
4349
        conf = config.AuthenticationConfig(_file=StringIO(
3162
4350
                'foo = bar\xff'))
3163
4351
        self.assertRaises(errors.ConfigContentError, conf._get_config)
3164
 
        
 
4352
 
3165
4353
    def test_missing_auth_section_header(self):
3166
4354
        conf = config.AuthenticationConfig(_file=StringIO('foo = bar'))
3167
4355
        self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
3648
4836
 
3649
4837
    def test_auto_user_id(self):
3650
4838
        """Automatic inference of user name.
3651
 
        
 
4839
 
3652
4840
        This is a bit hard to test in an isolated way, because it depends on
3653
4841
        system functions that go direct to /etc or perhaps somewhere else.
3654
4842
        But it's reasonable to say that on Unix, with an /etc/mailname, we ought
3664
4852
        else:
3665
4853
            self.assertEquals((None, None), (realname, address))
3666
4854
 
 
4855
 
 
4856
class EmailOptionTests(tests.TestCase):
 
4857
 
 
4858
    def test_default_email_uses_BZR_EMAIL(self):
 
4859
        conf = config.MemoryStack('email=jelmer@debian.org')
 
4860
        # BZR_EMAIL takes precedence over EMAIL
 
4861
        self.overrideEnv('BZR_EMAIL', 'jelmer@samba.org')
 
4862
        self.overrideEnv('EMAIL', 'jelmer@apache.org')
 
4863
        self.assertEquals('jelmer@samba.org', conf.get('email'))
 
4864
 
 
4865
    def test_default_email_uses_EMAIL(self):
 
4866
        conf = config.MemoryStack('')
 
4867
        self.overrideEnv('BZR_EMAIL', None)
 
4868
        self.overrideEnv('EMAIL', 'jelmer@apache.org')
 
4869
        self.assertEquals('jelmer@apache.org', conf.get('email'))
 
4870
 
 
4871
    def test_BZR_EMAIL_overrides(self):
 
4872
        conf = config.MemoryStack('email=jelmer@debian.org')
 
4873
        self.overrideEnv('BZR_EMAIL', 'jelmer@apache.org')
 
4874
        self.assertEquals('jelmer@apache.org', conf.get('email'))
 
4875
        self.overrideEnv('BZR_EMAIL', None)
 
4876
        self.overrideEnv('EMAIL', 'jelmer@samba.org')
 
4877
        self.assertEquals('jelmer@debian.org', conf.get('email'))
 
4878
 
 
4879
 
 
4880
class MailClientOptionTests(tests.TestCase):
 
4881
 
 
4882
    def test_default(self):
 
4883
        conf = config.MemoryStack('')
 
4884
        client = conf.get('mail_client')
 
4885
        self.assertIs(client, mail_client.DefaultMail)
 
4886
 
 
4887
    def test_evolution(self):
 
4888
        conf = config.MemoryStack('mail_client=evolution')
 
4889
        client = conf.get('mail_client')
 
4890
        self.assertIs(client, mail_client.Evolution)
 
4891
 
 
4892
    def test_kmail(self):
 
4893
        conf = config.MemoryStack('mail_client=kmail')
 
4894
        client = conf.get('mail_client')
 
4895
        self.assertIs(client, mail_client.KMail)
 
4896
 
 
4897
    def test_mutt(self):
 
4898
        conf = config.MemoryStack('mail_client=mutt')
 
4899
        client = conf.get('mail_client')
 
4900
        self.assertIs(client, mail_client.Mutt)
 
4901
 
 
4902
    def test_thunderbird(self):
 
4903
        conf = config.MemoryStack('mail_client=thunderbird')
 
4904
        client = conf.get('mail_client')
 
4905
        self.assertIs(client, mail_client.Thunderbird)
 
4906
 
 
4907
    def test_explicit_default(self):
 
4908
        conf = config.MemoryStack('mail_client=default')
 
4909
        client = conf.get('mail_client')
 
4910
        self.assertIs(client, mail_client.DefaultMail)
 
4911
 
 
4912
    def test_editor(self):
 
4913
        conf = config.MemoryStack('mail_client=editor')
 
4914
        client = conf.get('mail_client')
 
4915
        self.assertIs(client, mail_client.Editor)
 
4916
 
 
4917
    def test_mapi(self):
 
4918
        conf = config.MemoryStack('mail_client=mapi')
 
4919
        client = conf.get('mail_client')
 
4920
        self.assertIs(client, mail_client.MAPIClient)
 
4921
 
 
4922
    def test_xdg_email(self):
 
4923
        conf = config.MemoryStack('mail_client=xdg-email')
 
4924
        client = conf.get('mail_client')
 
4925
        self.assertIs(client, mail_client.XDGEmail)
 
4926
 
 
4927
    def test_unknown(self):
 
4928
        conf = config.MemoryStack('mail_client=firebird')
 
4929
        self.assertRaises(errors.ConfigOptionValueError, conf.get,
 
4930
                'mail_client')