~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_config.py

  • Committer: Vincent Ladeuil
  • Date: 2016-01-21 17:48:07 UTC
  • mto: This revision was merged to the branch mainline in revision 6613.
  • Revision ID: v.ladeuil+lp@free.fr-20160121174807-g4ybpaij9ln5wj6a
Make all transport put_bytes() raises TypeError when given unicode strings rather than bytes.

There was a mix of AssertionError or UnicodeEncodeError.

Also deleted test_put_file_unicode() which was bogus, files contain bytes not unicode strings.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
 
1
# Copyright (C) 2005-2014, 2016 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
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
"""Tests for finding and reading the bzr config file[s]."""
18
 
# import system imports here
 
18
 
19
19
from cStringIO import StringIO
 
20
from textwrap import dedent
20
21
import os
21
22
import sys
22
23
import threading
23
24
 
24
 
 
25
25
from testtools import matchers
26
26
 
27
 
#import bzrlib specific imports here
28
27
from bzrlib import (
29
28
    branch,
30
 
    bzrdir,
31
29
    config,
 
30
    controldir,
32
31
    diff,
33
32
    errors,
34
33
    osutils,
35
34
    mail_client,
36
 
    mergetools,
37
35
    ui,
38
36
    urlutils,
39
 
    registry,
 
37
    registry as _mod_registry,
40
38
    remote,
41
39
    tests,
42
40
    trace,
43
 
    transport,
44
41
    )
45
42
from bzrlib.symbol_versioning import (
46
43
    deprecated_in,
47
 
    deprecated_method,
48
44
    )
49
45
from bzrlib.transport import remote as transport_remote
50
46
from bzrlib.tests import (
71
67
 
72
68
# Register helpers to build stores
73
69
config.test_store_builder_registry.register(
74
 
    'configobj', lambda test: config.IniFileStore(test.get_transport(),
75
 
                                                  'configobj.conf'))
 
70
    'configobj', lambda test: config.TransportIniFileStore(
 
71
        test.get_transport(), 'configobj.conf'))
76
72
config.test_store_builder_registry.register(
77
73
    'bazaar', lambda test: config.GlobalStore())
78
74
config.test_store_builder_registry.register(
116
112
config.test_store_builder_registry.register('branch', build_branch_store)
117
113
 
118
114
 
 
115
def build_control_store(test):
 
116
    build_backing_branch(test, 'branch')
 
117
    b = controldir.ControlDir.open('branch')
 
118
    return config.ControlStore(b)
 
119
config.test_store_builder_registry.register('control', build_control_store)
 
120
 
 
121
 
119
122
def build_remote_branch_store(test):
120
123
    # There is only one permutation (but we won't be able to handle more with
121
124
    # this design anyway)
141
144
config.test_stack_builder_registry.register('branch', build_branch_stack)
142
145
 
143
146
 
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)
 
147
def build_branch_only_stack(test):
 
148
    # There is only one permutation (but we won't be able to handle more with
 
149
    # this design anyway)
 
150
    (transport_class,
 
151
     server_class) = transport_remote.get_test_permutations()[0]
 
152
    build_backing_branch(test, 'branch', transport_class, server_class)
 
153
    b = branch.Branch.open(test.get_url('branch'))
 
154
    return config.BranchOnlyStack(b)
 
155
config.test_stack_builder_registry.register('branch_only',
 
156
                                            build_branch_only_stack)
 
157
 
 
158
def build_remote_control_stack(test):
 
159
    # There is only one permutation (but we won't be able to handle more with
 
160
    # this design anyway)
 
161
    (transport_class,
 
162
     server_class) = transport_remote.get_test_permutations()[0]
 
163
    # We need only a bzrdir for this, not a full branch, but it's not worth
 
164
    # creating a dedicated helper to create only the bzrdir
 
165
    build_backing_branch(test, 'branch', transport_class, server_class)
 
166
    b = branch.Branch.open(test.get_url('branch'))
 
167
    return config.RemoteControlStack(b.bzrdir)
 
168
config.test_stack_builder_registry.register('remote_control',
 
169
                                            build_remote_control_stack)
154
170
 
155
171
 
156
172
sample_long_alias="log -r-15..-1 --line"
160
176
editor=vim
161
177
change_editor=vimdiff -of @new_path @old_path
162
178
gpg_signing_command=gnome-gpg
 
179
gpg_signing_key=DD4D5088
163
180
log_format=short
164
181
validate_signatures_in_log=true
165
182
acceptable_keys=amy
166
183
user_global_option=something
167
184
bzr.mergetool.sometool=sometool {base} {this} {other} -o {result}
168
185
bzr.mergetool.funkytool=funkytool "arg with spaces" {this_temp}
 
186
bzr.mergetool.newtool='"newtool with spaces" {this_temp}'
169
187
bzr.default_mergetool=sometool
170
188
[ALIASES]
171
189
h=help
214
232
[/a/]
215
233
check_signatures=check-available
216
234
gpg_signing_command=false
 
235
gpg_signing_key=default
217
236
user_local_option=local
218
237
# test trailing / matching
219
238
[/a/*]
309
328
 
310
329
class FakeBranch(object):
311
330
 
312
 
    def __init__(self, base=None, user_id=None):
 
331
    def __init__(self, base=None):
313
332
        if base is None:
314
333
            self.base = "http://example.com/branches/demo"
315
334
        else:
316
335
            self.base = base
317
336
        self._transport = self.control_files = \
318
 
            FakeControlFilesAndTransport(user_id=user_id)
 
337
            FakeControlFilesAndTransport()
319
338
 
320
339
    def _get_config(self):
321
340
        return config.TransportConfig(self._transport, 'branch.conf')
329
348
 
330
349
class FakeControlFilesAndTransport(object):
331
350
 
332
 
    def __init__(self, user_id=None):
 
351
    def __init__(self):
333
352
        self.files = {}
334
 
        if user_id:
335
 
            self.files['email'] = user_id
336
353
        self._transport = self
337
354
 
338
 
    def get_utf8(self, filename):
339
 
        # from LockableFiles
340
 
        raise AssertionError("get_utf8 should no longer be used")
341
 
 
342
355
    def get(self, filename):
343
356
        # from Transport
344
357
        try:
462
475
    def test_constructs(self):
463
476
        config.Config()
464
477
 
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
478
    def test_user_email(self):
472
479
        my_config = InstrumentedConfig()
473
480
        self.assertEqual('robert.collins@example.org', my_config.user_email())
481
488
 
482
489
    def test_signatures_default(self):
483
490
        my_config = config.Config()
484
 
        self.assertFalse(my_config.signature_needed())
 
491
        self.assertFalse(
 
492
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
493
                my_config.signature_needed))
485
494
        self.assertEqual(config.CHECK_IF_POSSIBLE,
486
 
                         my_config.signature_checking())
 
495
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
496
                my_config.signature_checking))
487
497
        self.assertEqual(config.SIGN_WHEN_REQUIRED,
488
 
                         my_config.signing_policy())
 
498
                self.applyDeprecated(deprecated_in((2, 5, 0)),
 
499
                    my_config.signing_policy))
489
500
 
490
501
    def test_signatures_template_method(self):
491
502
        my_config = InstrumentedConfig()
492
 
        self.assertEqual(config.CHECK_NEVER, my_config.signature_checking())
 
503
        self.assertEqual(config.CHECK_NEVER,
 
504
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
505
                my_config.signature_checking))
493
506
        self.assertEqual(['_get_signature_checking'], my_config._calls)
494
507
 
495
508
    def test_signatures_template_method_none(self):
496
509
        my_config = InstrumentedConfig()
497
510
        my_config._signatures = None
498
511
        self.assertEqual(config.CHECK_IF_POSSIBLE,
499
 
                         my_config.signature_checking())
 
512
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
513
                             my_config.signature_checking))
500
514
        self.assertEqual(['_get_signature_checking'], my_config._calls)
501
515
 
502
516
    def test_gpg_signing_command_default(self):
503
517
        my_config = config.Config()
504
 
        self.assertEqual('gpg', my_config.gpg_signing_command())
 
518
        self.assertEqual('gpg',
 
519
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
520
                my_config.gpg_signing_command))
505
521
 
506
522
    def test_get_user_option_default(self):
507
523
        my_config = config.Config()
509
525
 
510
526
    def test_post_commit_default(self):
511
527
        my_config = config.Config()
512
 
        self.assertEqual(None, my_config.post_commit())
 
528
        self.assertEqual(None, self.applyDeprecated(deprecated_in((2, 5, 0)),
 
529
                                                    my_config.post_commit))
 
530
 
513
531
 
514
532
    def test_log_format_default(self):
515
533
        my_config = config.Config()
516
 
        self.assertEqual('long', my_config.log_format())
 
534
        self.assertEqual('long',
 
535
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
536
                                              my_config.log_format))
517
537
 
518
538
    def test_acceptable_keys_default(self):
519
539
        my_config = config.Config()
520
 
        self.assertEqual(None, my_config.acceptable_keys())
 
540
        self.assertEqual(None, self.applyDeprecated(deprecated_in((2, 5, 0)),
 
541
            my_config.acceptable_keys))
521
542
 
522
543
    def test_validate_signatures_in_log_default(self):
523
544
        my_config = config.Config()
537
558
    def setUp(self):
538
559
        super(TestConfigPath, self).setUp()
539
560
        self.overrideEnv('HOME', '/home/bogus')
540
 
        self.overrideEnv('XDG_CACHE_DIR', '')
 
561
        self.overrideEnv('XDG_CACHE_HOME', '')
541
562
        if sys.platform == 'win32':
542
563
            self.overrideEnv(
543
564
                'BZR_HOME', r'C:\Documents and Settings\bogus\Application Data')
549
570
    def test_config_dir(self):
550
571
        self.assertEqual(config.config_dir(), self.bzr_home)
551
572
 
 
573
    def test_config_dir_is_unicode(self):
 
574
        self.assertIsInstance(config.config_dir(), unicode)
 
575
 
552
576
    def test_config_filename(self):
553
577
        self.assertEqual(config.config_filename(),
554
578
                         self.bzr_home + '/bazaar.conf')
571
595
    # subdirectory of $XDG_CONFIG_HOME
572
596
 
573
597
    def setUp(self):
574
 
        if sys.platform in ('darwin', 'win32'):
 
598
        if sys.platform == 'win32':
575
599
            raise tests.TestNotApplicable(
576
600
                'XDG config dir not used on this platform')
577
601
        super(TestXDGConfigDir, self).setUp()
604
628
class TestIniConfigBuilding(TestIniConfig):
605
629
 
606
630
    def test_contructs(self):
607
 
        my_config = config.IniBasedConfig()
 
631
        config.IniBasedConfig()
608
632
 
609
633
    def test_from_fp(self):
610
634
        my_config = config.IniBasedConfig.from_string(sample_config_text)
653
677
 
654
678
    def test_saved_with_content(self):
655
679
        content = 'foo = bar\n'
656
 
        conf = config.IniBasedConfig.from_string(
657
 
            content, file_name='./test.conf', save=True)
 
680
        config.IniBasedConfig.from_string(content, file_name='./test.conf',
 
681
                                          save=True)
658
682
        self.assertFileEqual(content, 'test.conf')
659
683
 
660
684
 
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
685
class TestIniConfigOptionExpansion(tests.TestCase):
719
686
    """Test option expansion from the IniConfig level.
720
687
 
817
784
        self.assertEquals(['{foo', '}', '{', 'bar}'],
818
785
                          conf.get_user_option('hidden', expand=True))
819
786
 
 
787
 
820
788
class TestLocationConfigOptionExpansion(tests.TestCaseInTempDir):
821
789
 
822
790
    def get_config(self, location, string=None):
1033
1001
        # automatically cast to list
1034
1002
        self.assertEqual(['x'], get_list('one_item'))
1035
1003
 
 
1004
    def test_get_user_option_as_int_from_SI(self):
 
1005
        conf, parser = self.make_config_parser("""
 
1006
plain = 100
 
1007
si_k = 5k,
 
1008
si_kb = 5kb,
 
1009
si_m = 5M,
 
1010
si_mb = 5MB,
 
1011
si_g = 5g,
 
1012
si_gb = 5gB,
 
1013
""")
 
1014
        def get_si(s, default=None):
 
1015
            return self.applyDeprecated(
 
1016
                deprecated_in((2, 5, 0)),
 
1017
                conf.get_user_option_as_int_from_SI, s, default)
 
1018
        self.assertEqual(100, get_si('plain'))
 
1019
        self.assertEqual(5000, get_si('si_k'))
 
1020
        self.assertEqual(5000, get_si('si_kb'))
 
1021
        self.assertEqual(5000000, get_si('si_m'))
 
1022
        self.assertEqual(5000000, get_si('si_mb'))
 
1023
        self.assertEqual(5000000000, get_si('si_g'))
 
1024
        self.assertEqual(5000000000, get_si('si_gb'))
 
1025
        self.assertEqual(None, get_si('non-exist'))
 
1026
        self.assertEqual(42, get_si('non-exist-with-default',  42))
 
1027
 
1036
1028
 
1037
1029
class TestSupressWarning(TestIniConfig):
1038
1030
 
1054
1046
class TestGetConfig(tests.TestCase):
1055
1047
 
1056
1048
    def test_constructs(self):
1057
 
        my_config = config.GlobalConfig()
 
1049
        config.GlobalConfig()
1058
1050
 
1059
1051
    def test_calls_read_filenames(self):
1060
1052
        # replace the class that is constructed, to check its parameters
1072
1064
 
1073
1065
class TestBranchConfig(tests.TestCaseWithTransport):
1074
1066
 
1075
 
    def test_constructs(self):
 
1067
    def test_constructs_valid(self):
1076
1068
        branch = FakeBranch()
1077
1069
        my_config = config.BranchConfig(branch)
 
1070
        self.assertIsNot(None, my_config)
 
1071
 
 
1072
    def test_constructs_error(self):
1078
1073
        self.assertRaises(TypeError, config.BranchConfig)
1079
1074
 
1080
1075
    def test_get_location_config(self):
1086
1081
 
1087
1082
    def test_get_config(self):
1088
1083
        """The Branch.get_config method works properly"""
1089
 
        b = bzrdir.BzrDir.create_standalone_workingtree('.').branch
 
1084
        b = controldir.ControlDir.create_standalone_workingtree('.').branch
1090
1085
        my_config = b.get_config()
1091
1086
        self.assertIs(my_config.get_user_option('wacky'), None)
1092
1087
        my_config.set_user_option('wacky', 'unlikely')
1112
1107
        conf = config.LocationConfig.from_string(
1113
1108
            '[%s]\nnickname = foobar' % (local_url,),
1114
1109
            local_url, save=True)
 
1110
        self.assertIsNot(None, conf)
1115
1111
        self.assertEqual('foobar', branch.nick)
1116
1112
 
1117
1113
    def test_config_local_path(self):
1120
1116
        self.assertEqual('branch', branch.nick)
1121
1117
 
1122
1118
        local_path = osutils.getcwd().encode('utf8')
1123
 
        conf = config.LocationConfig.from_string(
 
1119
        config.LocationConfig.from_string(
1124
1120
            '[%s/branch]\nnickname = barry' % (local_path,),
1125
1121
            'branch',  save=True)
 
1122
        # Now the branch will find its nick via the location config
1126
1123
        self.assertEqual('barry', branch.nick)
1127
1124
 
1128
1125
    def test_config_creates_local(self):
1141
1138
        b = self.make_branch('!repo')
1142
1139
        self.assertEqual('!repo', b.get_config().get_nickname())
1143
1140
 
 
1141
    def test_autonick_uses_branch_name(self):
 
1142
        b = self.make_branch('foo', name='bar')
 
1143
        self.assertEqual('bar', b.get_config().get_nickname())
 
1144
 
1144
1145
    def test_warn_if_masked(self):
1145
1146
        warnings = []
1146
1147
        def warning(*args):
1186
1187
        my_config = config.GlobalConfig()
1187
1188
        self.assertEqual(None, my_config._get_user_id())
1188
1189
 
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
1190
    def test_signatures_always(self):
1196
1191
        my_config = config.GlobalConfig.from_string(sample_always_signatures)
1197
1192
        self.assertEqual(config.CHECK_NEVER,
1198
 
                         my_config.signature_checking())
 
1193
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1194
                             my_config.signature_checking))
1199
1195
        self.assertEqual(config.SIGN_ALWAYS,
1200
 
                         my_config.signing_policy())
1201
 
        self.assertEqual(True, my_config.signature_needed())
 
1196
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1197
                             my_config.signing_policy))
 
1198
        self.assertEqual(True,
 
1199
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1200
                my_config.signature_needed))
1202
1201
 
1203
1202
    def test_signatures_if_possible(self):
1204
1203
        my_config = config.GlobalConfig.from_string(sample_maybe_signatures)
1205
1204
        self.assertEqual(config.CHECK_NEVER,
1206
 
                         my_config.signature_checking())
 
1205
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1206
                             my_config.signature_checking))
1207
1207
        self.assertEqual(config.SIGN_WHEN_REQUIRED,
1208
 
                         my_config.signing_policy())
1209
 
        self.assertEqual(False, my_config.signature_needed())
 
1208
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1209
                             my_config.signing_policy))
 
1210
        self.assertEqual(False, self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1211
            my_config.signature_needed))
1210
1212
 
1211
1213
    def test_signatures_ignore(self):
1212
1214
        my_config = config.GlobalConfig.from_string(sample_ignore_signatures)
1213
1215
        self.assertEqual(config.CHECK_ALWAYS,
1214
 
                         my_config.signature_checking())
 
1216
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1217
                             my_config.signature_checking))
1215
1218
        self.assertEqual(config.SIGN_NEVER,
1216
 
                         my_config.signing_policy())
1217
 
        self.assertEqual(False, my_config.signature_needed())
 
1219
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1220
                             my_config.signing_policy))
 
1221
        self.assertEqual(False, self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1222
            my_config.signature_needed))
1218
1223
 
1219
1224
    def _get_sample_config(self):
1220
1225
        my_config = config.GlobalConfig.from_string(sample_config_text)
1222
1227
 
1223
1228
    def test_gpg_signing_command(self):
1224
1229
        my_config = self._get_sample_config()
1225
 
        self.assertEqual("gnome-gpg", my_config.gpg_signing_command())
1226
 
        self.assertEqual(False, my_config.signature_needed())
 
1230
        self.assertEqual("gnome-gpg",
 
1231
            self.applyDeprecated(
 
1232
                deprecated_in((2, 5, 0)), my_config.gpg_signing_command))
 
1233
        self.assertEqual(False, self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1234
            my_config.signature_needed))
 
1235
 
 
1236
    def test_gpg_signing_key(self):
 
1237
        my_config = self._get_sample_config()
 
1238
        self.assertEqual("DD4D5088",
 
1239
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1240
                my_config.gpg_signing_key))
1227
1241
 
1228
1242
    def _get_empty_config(self):
1229
1243
        my_config = config.GlobalConfig()
1231
1245
 
1232
1246
    def test_gpg_signing_command_unset(self):
1233
1247
        my_config = self._get_empty_config()
1234
 
        self.assertEqual("gpg", my_config.gpg_signing_command())
 
1248
        self.assertEqual("gpg",
 
1249
            self.applyDeprecated(
 
1250
                deprecated_in((2, 5, 0)), my_config.gpg_signing_command))
1235
1251
 
1236
1252
    def test_get_user_option_default(self):
1237
1253
        my_config = self._get_empty_config()
1244
1260
 
1245
1261
    def test_post_commit_default(self):
1246
1262
        my_config = self._get_sample_config()
1247
 
        self.assertEqual(None, my_config.post_commit())
 
1263
        self.assertEqual(None,
 
1264
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1265
                                              my_config.post_commit))
1248
1266
 
1249
1267
    def test_configured_logformat(self):
1250
1268
        my_config = self._get_sample_config()
1251
 
        self.assertEqual("short", my_config.log_format())
 
1269
        self.assertEqual("short",
 
1270
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1271
                                              my_config.log_format))
1252
1272
 
1253
1273
    def test_configured_acceptable_keys(self):
1254
1274
        my_config = self._get_sample_config()
1255
 
        self.assertEqual("amy", my_config.acceptable_keys())
 
1275
        self.assertEqual("amy",
 
1276
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1277
                my_config.acceptable_keys))
1256
1278
 
1257
1279
    def test_configured_validate_signatures_in_log(self):
1258
1280
        my_config = self._get_sample_config()
1296
1318
        self.log(repr(tools))
1297
1319
        self.assertEqual(
1298
1320
            {u'funkytool' : u'funkytool "arg with spaces" {this_temp}',
1299
 
            u'sometool' : u'sometool {base} {this} {other} -o {result}'},
 
1321
            u'sometool' : u'sometool {base} {this} {other} -o {result}',
 
1322
            u'newtool' : u'"newtool with spaces" {this_temp}'},
1300
1323
            tools)
1301
1324
 
1302
1325
    def test_get_merge_tools_empty(self):
1350
1373
 
1351
1374
class TestLocationConfig(tests.TestCaseInTempDir, TestOptionsMixin):
1352
1375
 
1353
 
    def test_constructs(self):
1354
 
        my_config = config.LocationConfig('http://example.com')
 
1376
    def test_constructs_valid(self):
 
1377
        config.LocationConfig('http://example.com')
 
1378
 
 
1379
    def test_constructs_error(self):
1355
1380
        self.assertRaises(TypeError, config.LocationConfig)
1356
1381
 
1357
1382
    def test_branch_calls_read_filenames(self):
1493
1518
        self.get_branch_config('http://www.example.com',
1494
1519
                                 global_config=sample_ignore_signatures)
1495
1520
        self.assertEqual(config.CHECK_ALWAYS,
1496
 
                         self.my_config.signature_checking())
 
1521
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1522
                             self.my_config.signature_checking))
1497
1523
        self.assertEqual(config.SIGN_NEVER,
1498
 
                         self.my_config.signing_policy())
 
1524
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1525
                             self.my_config.signing_policy))
1499
1526
 
1500
1527
    def test_signatures_never(self):
1501
1528
        self.get_branch_config('/a/c')
1502
1529
        self.assertEqual(config.CHECK_NEVER,
1503
 
                         self.my_config.signature_checking())
 
1530
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1531
                             self.my_config.signature_checking))
1504
1532
 
1505
1533
    def test_signatures_when_available(self):
1506
1534
        self.get_branch_config('/a/', global_config=sample_ignore_signatures)
1507
1535
        self.assertEqual(config.CHECK_IF_POSSIBLE,
1508
 
                         self.my_config.signature_checking())
 
1536
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1537
                             self.my_config.signature_checking))
1509
1538
 
1510
1539
    def test_signatures_always(self):
1511
1540
        self.get_branch_config('/b')
1512
1541
        self.assertEqual(config.CHECK_ALWAYS,
1513
 
                         self.my_config.signature_checking())
 
1542
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1543
                         self.my_config.signature_checking))
1514
1544
 
1515
1545
    def test_gpg_signing_command(self):
1516
1546
        self.get_branch_config('/b')
1517
 
        self.assertEqual("gnome-gpg", self.my_config.gpg_signing_command())
 
1547
        self.assertEqual("gnome-gpg",
 
1548
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1549
                self.my_config.gpg_signing_command))
1518
1550
 
1519
1551
    def test_gpg_signing_command_missing(self):
1520
1552
        self.get_branch_config('/a')
1521
 
        self.assertEqual("false", self.my_config.gpg_signing_command())
 
1553
        self.assertEqual("false",
 
1554
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1555
                self.my_config.gpg_signing_command))
 
1556
 
 
1557
    def test_gpg_signing_key(self):
 
1558
        self.get_branch_config('/b')
 
1559
        self.assertEqual("DD4D5088", self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1560
            self.my_config.gpg_signing_key))
 
1561
 
 
1562
    def test_gpg_signing_key_default(self):
 
1563
        self.get_branch_config('/a')
 
1564
        self.assertEqual("erik@bagfors.nu",
 
1565
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1566
                self.my_config.gpg_signing_key))
1522
1567
 
1523
1568
    def test_get_user_option_global(self):
1524
1569
        self.get_branch_config('/a')
1612
1657
    def test_post_commit_default(self):
1613
1658
        self.get_branch_config('/a/c')
1614
1659
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1615
 
                         self.my_config.post_commit())
 
1660
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1661
                                              self.my_config.post_commit))
1616
1662
 
1617
1663
    def get_branch_config(self, location, global_config=None,
1618
1664
                          location_config=None):
1622
1668
        if location_config is None:
1623
1669
            location_config = sample_branches_text
1624
1670
 
1625
 
        my_global_config = config.GlobalConfig.from_string(global_config,
1626
 
                                                           save=True)
1627
 
        my_location_config = config.LocationConfig.from_string(
1628
 
            location_config, my_branch.base, save=True)
 
1671
        config.GlobalConfig.from_string(global_config, save=True)
 
1672
        config.LocationConfig.from_string(location_config, my_branch.base,
 
1673
                                          save=True)
1629
1674
        my_config = config.BranchConfig(my_branch)
1630
1675
        self.my_config = my_config
1631
1676
        self.my_location_config = my_config._get_location_config()
1696
1741
                          location_config=None, branch_data_config=None):
1697
1742
        my_branch = FakeBranch(location)
1698
1743
        if global_config is not None:
1699
 
            my_global_config = config.GlobalConfig.from_string(global_config,
1700
 
                                                               save=True)
 
1744
            config.GlobalConfig.from_string(global_config, save=True)
1701
1745
        if location_config is not None:
1702
 
            my_location_config = config.LocationConfig.from_string(
1703
 
                location_config, my_branch.base, save=True)
 
1746
            config.LocationConfig.from_string(location_config, my_branch.base,
 
1747
                                              save=True)
1704
1748
        my_config = config.BranchConfig(my_branch)
1705
1749
        if branch_data_config is not None:
1706
1750
            my_config.branch.control_files.files['branch.conf'] = \
1708
1752
        return my_config
1709
1753
 
1710
1754
    def test_user_id(self):
1711
 
        branch = FakeBranch(user_id='Robert Collins <robertc@example.net>')
 
1755
        branch = FakeBranch()
1712
1756
        my_config = config.BranchConfig(branch)
1713
 
        self.assertEqual("Robert Collins <robertc@example.net>",
1714
 
                         my_config.username())
 
1757
        self.assertIsNot(None, my_config.username())
1715
1758
        my_config.branch.control_files.files['email'] = "John"
1716
1759
        my_config.set_user_option('email',
1717
1760
                                  "Robert Collins <robertc@example.org>")
1718
 
        self.assertEqual("John", my_config.username())
1719
 
        del my_config.branch.control_files.files['email']
1720
1761
        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())
 
1762
                        my_config.username())
1729
1763
 
1730
1764
    def test_BZR_EMAIL_OVERRIDES(self):
1731
1765
        self.overrideEnv('BZR_EMAIL', "Robert Collins <robertc@example.org>")
1737
1771
    def test_signatures_forced(self):
1738
1772
        my_config = self.get_branch_config(
1739
1773
            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())
 
1774
        self.assertEqual(config.CHECK_NEVER,
 
1775
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1776
                my_config.signature_checking))
 
1777
        self.assertEqual(config.SIGN_ALWAYS,
 
1778
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1779
                my_config.signing_policy))
 
1780
        self.assertTrue(self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1781
            my_config.signature_needed))
1743
1782
 
1744
1783
    def test_signatures_forced_branch(self):
1745
1784
        my_config = self.get_branch_config(
1746
1785
            global_config=sample_ignore_signatures,
1747
1786
            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())
 
1787
        self.assertEqual(config.CHECK_NEVER,
 
1788
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1789
                my_config.signature_checking))
 
1790
        self.assertEqual(config.SIGN_ALWAYS,
 
1791
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1792
                my_config.signing_policy))
 
1793
        self.assertTrue(self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1794
            my_config.signature_needed))
1751
1795
 
1752
1796
    def test_gpg_signing_command(self):
1753
1797
        my_config = self.get_branch_config(
1754
1798
            global_config=sample_config_text,
1755
1799
            # branch data cannot set gpg_signing_command
1756
1800
            branch_data_config="gpg_signing_command=pgp")
1757
 
        self.assertEqual('gnome-gpg', my_config.gpg_signing_command())
 
1801
        self.assertEqual('gnome-gpg',
 
1802
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1803
                my_config.gpg_signing_command))
1758
1804
 
1759
1805
    def test_get_user_option_global(self):
1760
1806
        my_config = self.get_branch_config(global_config=sample_config_text)
1767
1813
                                      location_config=sample_branches_text)
1768
1814
        self.assertEqual(my_config.branch.base, '/a/c')
1769
1815
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1770
 
                         my_config.post_commit())
 
1816
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1817
                                              my_config.post_commit))
1771
1818
        my_config.set_user_option('post_commit', 'rmtree_root')
1772
1819
        # post-commit is ignored when present in branch data
1773
1820
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1774
 
                         my_config.post_commit())
 
1821
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1822
                                              my_config.post_commit))
1775
1823
        my_config.set_user_option('post_commit', 'rmtree_root',
1776
1824
                                  store=config.STORE_LOCATION)
1777
 
        self.assertEqual('rmtree_root', my_config.post_commit())
 
1825
        self.assertEqual('rmtree_root',
 
1826
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1827
                                              my_config.post_commit))
1778
1828
 
1779
1829
    def test_config_precedence(self):
1780
1830
        # FIXME: eager test, luckily no persitent config file makes it fail
1796
1846
            location='http://example.com/specific')
1797
1847
        self.assertEqual(my_config.get_user_option('option'), 'exact')
1798
1848
 
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
1849
 
1842
1850
class TestMailAddressExtraction(tests.TestCase):
1843
1851
 
1914
1922
        conf = config.TransportConfig(t, 'foo.conf')
1915
1923
        self.assertRaises(errors.ParseConfigError, conf._get_configobj)
1916
1924
 
 
1925
    def test_load_permission_denied(self):
 
1926
        """Ensure we get an empty config file if the file is inaccessible."""
 
1927
        warnings = []
 
1928
        def warning(*args):
 
1929
            warnings.append(args[0] % args[1:])
 
1930
        self.overrideAttr(trace, 'warning', warning)
 
1931
 
 
1932
        class DenyingTransport(object):
 
1933
 
 
1934
            def __init__(self, base):
 
1935
                self.base = base
 
1936
 
 
1937
            def get_bytes(self, relpath):
 
1938
                raise errors.PermissionDenied(relpath, "")
 
1939
 
 
1940
        cfg = config.TransportConfig(
 
1941
            DenyingTransport("nonexisting://"), 'control.conf')
 
1942
        self.assertIs(None, cfg.get_option('non-existant', 'SECTION'))
 
1943
        self.assertEquals(
 
1944
            warnings,
 
1945
            [u'Permission denied while trying to open configuration file '
 
1946
             u'nonexisting:///control.conf.'])
 
1947
 
1917
1948
    def test_get_value(self):
1918
1949
        """Test that retreiving a value from a section is possible"""
1919
1950
        bzrdir_config = config.TransportConfig(self.get_transport('.'),
2107
2138
        self.assertGetHook(remote_branch._get_config(), 'file', 'branch')
2108
2139
 
2109
2140
    def test_get_hook_remote_bzrdir(self):
2110
 
        remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
 
2141
        remote_bzrdir = controldir.ControlDir.open(self.get_url('tree'))
2111
2142
        conf = remote_bzrdir._get_config()
2112
2143
        conf.set_option('remotedir', 'file')
2113
2144
        self.assertGetHook(conf, 'file', 'remotedir')
2135
2166
    def test_set_hook_remote_bzrdir(self):
2136
2167
        remote_branch = branch.Branch.open(self.get_url('tree'))
2137
2168
        self.addCleanup(remote_branch.lock_write().unlock)
2138
 
        remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
 
2169
        remote_bzrdir = controldir.ControlDir.open(self.get_url('tree'))
2139
2170
        self.assertSetHook(remote_bzrdir._get_config(), 'file', 'remotedir')
2140
2171
 
2141
2172
    def assertLoadHook(self, expected_nb_calls, name, conf_class, *conf_args):
2158
2189
        self.assertLoadHook(1, 'file', remote.RemoteBranchConfig, remote_branch)
2159
2190
 
2160
2191
    def test_load_hook_remote_bzrdir(self):
2161
 
        remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
 
2192
        remote_bzrdir = controldir.ControlDir.open(self.get_url('tree'))
2162
2193
        # The config file doesn't exist, set an option to force its creation
2163
2194
        conf = remote_bzrdir._get_config()
2164
2195
        conf.set_option('remotedir', 'file')
2189
2220
    def test_save_hook_remote_bzrdir(self):
2190
2221
        remote_branch = branch.Branch.open(self.get_url('tree'))
2191
2222
        self.addCleanup(remote_branch.lock_write().unlock)
2192
 
        remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
 
2223
        remote_bzrdir = controldir.ControlDir.open(self.get_url('tree'))
2193
2224
        self.assertSaveHook(remote_bzrdir._get_config())
2194
2225
 
2195
2226
 
 
2227
class TestOptionNames(tests.TestCase):
 
2228
 
 
2229
    def is_valid(self, name):
 
2230
        return config._option_ref_re.match('{%s}' % name) is not None
 
2231
 
 
2232
    def test_valid_names(self):
 
2233
        self.assertTrue(self.is_valid('foo'))
 
2234
        self.assertTrue(self.is_valid('foo.bar'))
 
2235
        self.assertTrue(self.is_valid('f1'))
 
2236
        self.assertTrue(self.is_valid('_'))
 
2237
        self.assertTrue(self.is_valid('__bar__'))
 
2238
        self.assertTrue(self.is_valid('a_'))
 
2239
        self.assertTrue(self.is_valid('a1'))
 
2240
 
 
2241
    def test_invalid_names(self):
 
2242
        self.assertFalse(self.is_valid(' foo'))
 
2243
        self.assertFalse(self.is_valid('foo '))
 
2244
        self.assertFalse(self.is_valid('1'))
 
2245
        self.assertFalse(self.is_valid('1,2'))
 
2246
        self.assertFalse(self.is_valid('foo$'))
 
2247
        self.assertFalse(self.is_valid('!foo'))
 
2248
        self.assertFalse(self.is_valid('foo.'))
 
2249
        self.assertFalse(self.is_valid('foo..bar'))
 
2250
        self.assertFalse(self.is_valid('{}'))
 
2251
        self.assertFalse(self.is_valid('{a}'))
 
2252
        self.assertFalse(self.is_valid('a\n'))
 
2253
 
 
2254
    def assertSingleGroup(self, reference):
 
2255
        # the regexp is used with split and as such should match the reference
 
2256
        # *only*, if more groups needs to be defined, (?:...) should be used.
 
2257
        m = config._option_ref_re.match('{a}')
 
2258
        self.assertLength(1, m.groups())
 
2259
 
 
2260
    def test_valid_references(self):
 
2261
        self.assertSingleGroup('{a}')
 
2262
        self.assertSingleGroup('{{a}}')
 
2263
 
 
2264
 
2196
2265
class TestOption(tests.TestCase):
2197
2266
 
2198
2267
    def test_default_value(self):
2199
2268
        opt = config.Option('foo', default='bar')
2200
2269
        self.assertEquals('bar', opt.get_default())
2201
2270
 
 
2271
    def test_callable_default_value(self):
 
2272
        def bar_as_unicode():
 
2273
            return u'bar'
 
2274
        opt = config.Option('foo', default=bar_as_unicode)
 
2275
        self.assertEquals('bar', opt.get_default())
 
2276
 
 
2277
    def test_default_value_from_env(self):
 
2278
        opt = config.Option('foo', default='bar', default_from_env=['FOO'])
 
2279
        self.overrideEnv('FOO', 'quux')
 
2280
        # Env variable provides a default taking over the option one
 
2281
        self.assertEquals('quux', opt.get_default())
 
2282
 
 
2283
    def test_first_default_value_from_env_wins(self):
 
2284
        opt = config.Option('foo', default='bar',
 
2285
                            default_from_env=['NO_VALUE', 'FOO', 'BAZ'])
 
2286
        self.overrideEnv('FOO', 'foo')
 
2287
        self.overrideEnv('BAZ', 'baz')
 
2288
        # The first env var set wins
 
2289
        self.assertEquals('foo', opt.get_default())
 
2290
 
 
2291
    def test_not_supported_list_default_value(self):
 
2292
        self.assertRaises(AssertionError, config.Option, 'foo', default=[1])
 
2293
 
 
2294
    def test_not_supported_object_default_value(self):
 
2295
        self.assertRaises(AssertionError, config.Option, 'foo',
 
2296
                          default=object())
 
2297
 
 
2298
    def test_not_supported_callable_default_value_not_unicode(self):
 
2299
        def bar_not_unicode():
 
2300
            return 'bar'
 
2301
        opt = config.Option('foo', default=bar_not_unicode)
 
2302
        self.assertRaises(AssertionError, opt.get_default)
 
2303
 
 
2304
    def test_get_help_topic(self):
 
2305
        opt = config.Option('foo')
 
2306
        self.assertEquals('foo', opt.get_help_topic())
 
2307
 
 
2308
 
 
2309
class TestOptionConverter(tests.TestCase):
 
2310
 
 
2311
    def assertConverted(self, expected, opt, value):
 
2312
        self.assertEquals(expected, opt.convert_from_unicode(None, value))
 
2313
 
 
2314
    def assertCallsWarning(self, opt, value):
 
2315
        warnings = []
 
2316
 
 
2317
        def warning(*args):
 
2318
            warnings.append(args[0] % args[1:])
 
2319
        self.overrideAttr(trace, 'warning', warning)
 
2320
        self.assertEquals(None, opt.convert_from_unicode(None, value))
 
2321
        self.assertLength(1, warnings)
 
2322
        self.assertEquals(
 
2323
            'Value "%s" is not valid for "%s"' % (value, opt.name),
 
2324
            warnings[0])
 
2325
 
 
2326
    def assertCallsError(self, opt, value):
 
2327
        self.assertRaises(errors.ConfigOptionValueError,
 
2328
                          opt.convert_from_unicode, None, value)
 
2329
 
 
2330
    def assertConvertInvalid(self, opt, invalid_value):
 
2331
        opt.invalid = None
 
2332
        self.assertEquals(None, opt.convert_from_unicode(None, invalid_value))
 
2333
        opt.invalid = 'warning'
 
2334
        self.assertCallsWarning(opt, invalid_value)
 
2335
        opt.invalid = 'error'
 
2336
        self.assertCallsError(opt, invalid_value)
 
2337
 
 
2338
 
 
2339
class TestOptionWithBooleanConverter(TestOptionConverter):
 
2340
 
 
2341
    def get_option(self):
 
2342
        return config.Option('foo', help='A boolean.',
 
2343
                             from_unicode=config.bool_from_store)
 
2344
 
 
2345
    def test_convert_invalid(self):
 
2346
        opt = self.get_option()
 
2347
        # A string that is not recognized as a boolean
 
2348
        self.assertConvertInvalid(opt, u'invalid-boolean')
 
2349
        # A list of strings is never recognized as a boolean
 
2350
        self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
 
2351
 
 
2352
    def test_convert_valid(self):
 
2353
        opt = self.get_option()
 
2354
        self.assertConverted(True, opt, u'True')
 
2355
        self.assertConverted(True, opt, u'1')
 
2356
        self.assertConverted(False, opt, u'False')
 
2357
 
 
2358
 
 
2359
class TestOptionWithIntegerConverter(TestOptionConverter):
 
2360
 
 
2361
    def get_option(self):
 
2362
        return config.Option('foo', help='An integer.',
 
2363
                             from_unicode=config.int_from_store)
 
2364
 
 
2365
    def test_convert_invalid(self):
 
2366
        opt = self.get_option()
 
2367
        # A string that is not recognized as an integer
 
2368
        self.assertConvertInvalid(opt, u'forty-two')
 
2369
        # A list of strings is never recognized as an integer
 
2370
        self.assertConvertInvalid(opt, [u'a', u'list'])
 
2371
 
 
2372
    def test_convert_valid(self):
 
2373
        opt = self.get_option()
 
2374
        self.assertConverted(16, opt, u'16')
 
2375
 
 
2376
 
 
2377
class TestOptionWithSIUnitConverter(TestOptionConverter):
 
2378
 
 
2379
    def get_option(self):
 
2380
        return config.Option('foo', help='An integer in SI units.',
 
2381
                             from_unicode=config.int_SI_from_store)
 
2382
 
 
2383
    def test_convert_invalid(self):
 
2384
        opt = self.get_option()
 
2385
        self.assertConvertInvalid(opt, u'not-a-unit')
 
2386
        self.assertConvertInvalid(opt, u'Gb')  # Forgot the value
 
2387
        self.assertConvertInvalid(opt, u'1b')  # Forgot the unit
 
2388
        self.assertConvertInvalid(opt, u'1GG')
 
2389
        self.assertConvertInvalid(opt, u'1Mbb')
 
2390
        self.assertConvertInvalid(opt, u'1MM')
 
2391
 
 
2392
    def test_convert_valid(self):
 
2393
        opt = self.get_option()
 
2394
        self.assertConverted(int(5e3), opt, u'5kb')
 
2395
        self.assertConverted(int(5e6), opt, u'5M')
 
2396
        self.assertConverted(int(5e6), opt, u'5MB')
 
2397
        self.assertConverted(int(5e9), opt, u'5g')
 
2398
        self.assertConverted(int(5e9), opt, u'5gB')
 
2399
        self.assertConverted(100, opt, u'100')
 
2400
 
 
2401
 
 
2402
class TestListOption(TestOptionConverter):
 
2403
 
 
2404
    def get_option(self):
 
2405
        return config.ListOption('foo', help='A list.')
 
2406
 
 
2407
    def test_convert_invalid(self):
 
2408
        opt = self.get_option()
 
2409
        # We don't even try to convert a list into a list, we only expect
 
2410
        # strings
 
2411
        self.assertConvertInvalid(opt, [1])
 
2412
        # No string is invalid as all forms can be converted to a list
 
2413
 
 
2414
    def test_convert_valid(self):
 
2415
        opt = self.get_option()
 
2416
        # An empty string is an empty list
 
2417
        self.assertConverted([], opt, '')  # Using a bare str() just in case
 
2418
        self.assertConverted([], opt, u'')
 
2419
        # A boolean
 
2420
        self.assertConverted([u'True'], opt, u'True')
 
2421
        # An integer
 
2422
        self.assertConverted([u'42'], opt, u'42')
 
2423
        # A single string
 
2424
        self.assertConverted([u'bar'], opt, u'bar')
 
2425
 
 
2426
 
 
2427
class TestRegistryOption(TestOptionConverter):
 
2428
 
 
2429
    def get_option(self, registry):
 
2430
        return config.RegistryOption('foo', registry,
 
2431
                                     help='A registry option.')
 
2432
 
 
2433
    def test_convert_invalid(self):
 
2434
        registry = _mod_registry.Registry()
 
2435
        opt = self.get_option(registry)
 
2436
        self.assertConvertInvalid(opt, [1])
 
2437
        self.assertConvertInvalid(opt, u"notregistered")
 
2438
 
 
2439
    def test_convert_valid(self):
 
2440
        registry = _mod_registry.Registry()
 
2441
        registry.register("someval", 1234)
 
2442
        opt = self.get_option(registry)
 
2443
        # Using a bare str() just in case
 
2444
        self.assertConverted(1234, opt, "someval")
 
2445
        self.assertConverted(1234, opt, u'someval')
 
2446
        self.assertConverted(None, opt, None)
 
2447
 
 
2448
    def test_help(self):
 
2449
        registry = _mod_registry.Registry()
 
2450
        registry.register("someval", 1234, help="some option")
 
2451
        registry.register("dunno", 1234, help="some other option")
 
2452
        opt = self.get_option(registry)
 
2453
        self.assertEquals(
 
2454
            'A registry option.\n'
 
2455
            '\n'
 
2456
            'The following values are supported:\n'
 
2457
            ' dunno - some other option\n'
 
2458
            ' someval - some option\n',
 
2459
            opt.help)
 
2460
 
 
2461
    def test_get_help_text(self):
 
2462
        registry = _mod_registry.Registry()
 
2463
        registry.register("someval", 1234, help="some option")
 
2464
        registry.register("dunno", 1234, help="some other option")
 
2465
        opt = self.get_option(registry)
 
2466
        self.assertEquals(
 
2467
            'A registry option.\n'
 
2468
            '\n'
 
2469
            'The following values are supported:\n'
 
2470
            ' dunno - some other option\n'
 
2471
            ' someval - some option\n',
 
2472
            opt.get_help_text())
 
2473
 
2202
2474
 
2203
2475
class TestOptionRegistry(tests.TestCase):
2204
2476
 
2205
2477
    def setUp(self):
2206
2478
        super(TestOptionRegistry, self).setUp()
2207
2479
        # Always start with an empty registry
2208
 
        self.overrideAttr(config, 'option_registry', registry.Registry())
 
2480
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2209
2481
        self.registry = config.option_registry
2210
2482
 
2211
2483
    def test_register(self):
2212
2484
        opt = config.Option('foo')
2213
 
        self.registry.register('foo', opt)
 
2485
        self.registry.register(opt)
2214
2486
        self.assertIs(opt, self.registry.get('foo'))
2215
2487
 
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
2488
    def test_registered_help(self):
2224
 
        opt = config.Option('foo')
2225
 
        self.registry.register('foo', opt, help='A simple option')
 
2489
        opt = config.Option('foo', help='A simple option')
 
2490
        self.registry.register(opt)
2226
2491
        self.assertEquals('A simple option', self.registry.get_help('foo'))
2227
2492
 
 
2493
    def test_dont_register_illegal_name(self):
 
2494
        self.assertRaises(errors.IllegalOptionName,
 
2495
                          self.registry.register, config.Option(' foo'))
 
2496
        self.assertRaises(errors.IllegalOptionName,
 
2497
                          self.registry.register, config.Option('bar,'))
 
2498
 
 
2499
    lazy_option = config.Option('lazy_foo', help='Lazy help')
 
2500
 
 
2501
    def test_register_lazy(self):
 
2502
        self.registry.register_lazy('lazy_foo', self.__module__,
 
2503
                                    'TestOptionRegistry.lazy_option')
 
2504
        self.assertIs(self.lazy_option, self.registry.get('lazy_foo'))
 
2505
 
 
2506
    def test_registered_lazy_help(self):
 
2507
        self.registry.register_lazy('lazy_foo', self.__module__,
 
2508
                                    'TestOptionRegistry.lazy_option')
 
2509
        self.assertEquals('Lazy help', self.registry.get_help('lazy_foo'))
 
2510
 
 
2511
    def test_dont_lazy_register_illegal_name(self):
 
2512
        # This is where the root cause of http://pad.lv/1235099 is better
 
2513
        # understood: 'register_lazy' doc string mentions that key should match
 
2514
        # the option name which indirectly requires that the option name is a
 
2515
        # valid python identifier. We violate that rule here (using a key that
 
2516
        # doesn't match the option name) to test the option name checking.
 
2517
        self.assertRaises(errors.IllegalOptionName,
 
2518
                          self.registry.register_lazy, ' foo', self.__module__,
 
2519
                          'TestOptionRegistry.lazy_option')
 
2520
        self.assertRaises(errors.IllegalOptionName,
 
2521
                          self.registry.register_lazy, '1,2', self.__module__,
 
2522
                          'TestOptionRegistry.lazy_option')
 
2523
 
2228
2524
 
2229
2525
class TestRegisteredOptions(tests.TestCase):
2230
2526
    """All registered options should verify some constraints."""
2243
2539
 
2244
2540
    def test_help_is_set(self):
2245
2541
        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
 
2542
        # Come on, think about the user, he really wants to know what the
2248
2543
        # option is about
 
2544
        self.assertIsNot(None, option_help)
2249
2545
        self.assertNotEquals('', option_help)
2250
2546
 
2251
2547
 
2273
2569
 
2274
2570
class TestMutableSection(tests.TestCase):
2275
2571
 
2276
 
    # FIXME: Parametrize so that all sections (including os.environ and the
2277
 
    # ones produced by Stores) run these tests -- vila 2011-04-01
 
2572
    scenarios = [('mutable',
 
2573
                  {'get_section':
 
2574
                       lambda opts: config.MutableSection('myID', opts)},),
 
2575
        ]
2278
2576
 
2279
2577
    def test_set(self):
2280
2578
        a_dict = dict(foo='bar')
2281
 
        section = config.MutableSection('myID', a_dict)
 
2579
        section = self.get_section(a_dict)
2282
2580
        section.set('foo', 'new_value')
2283
2581
        self.assertEquals('new_value', section.get('foo'))
2284
2582
        # The change appears in the shared section
2289
2587
 
2290
2588
    def test_set_preserve_original_once(self):
2291
2589
        a_dict = dict(foo='bar')
2292
 
        section = config.MutableSection('myID', a_dict)
 
2590
        section = self.get_section(a_dict)
2293
2591
        section.set('foo', 'first_value')
2294
2592
        section.set('foo', 'second_value')
2295
2593
        # We keep track of the original value
2298
2596
 
2299
2597
    def test_remove(self):
2300
2598
        a_dict = dict(foo='bar')
2301
 
        section = config.MutableSection('myID', a_dict)
 
2599
        section = self.get_section(a_dict)
2302
2600
        section.remove('foo')
2303
2601
        # We get None for unknown options via the default value
2304
2602
        self.assertEquals(None, section.get('foo'))
2311
2609
 
2312
2610
    def test_remove_new_option(self):
2313
2611
        a_dict = dict()
2314
 
        section = config.MutableSection('myID', a_dict)
 
2612
        section = self.get_section(a_dict)
2315
2613
        section.set('foo', 'bar')
2316
2614
        section.remove('foo')
2317
2615
        self.assertFalse('foo' in section.options)
2321
2619
        self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
2322
2620
 
2323
2621
 
 
2622
class TestCommandLineStore(tests.TestCase):
 
2623
 
 
2624
    def setUp(self):
 
2625
        super(TestCommandLineStore, self).setUp()
 
2626
        self.store = config.CommandLineStore()
 
2627
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
 
2628
 
 
2629
    def get_section(self):
 
2630
        """Get the unique section for the command line overrides."""
 
2631
        sections = list(self.store.get_sections())
 
2632
        self.assertLength(1, sections)
 
2633
        store, section = sections[0]
 
2634
        self.assertEquals(self.store, store)
 
2635
        return section
 
2636
 
 
2637
    def test_no_override(self):
 
2638
        self.store._from_cmdline([])
 
2639
        section = self.get_section()
 
2640
        self.assertLength(0, list(section.iter_option_names()))
 
2641
 
 
2642
    def test_simple_override(self):
 
2643
        self.store._from_cmdline(['a=b'])
 
2644
        section = self.get_section()
 
2645
        self.assertEqual('b', section.get('a'))
 
2646
 
 
2647
    def test_list_override(self):
 
2648
        opt = config.ListOption('l')
 
2649
        config.option_registry.register(opt)
 
2650
        self.store._from_cmdline(['l=1,2,3'])
 
2651
        val = self.get_section().get('l')
 
2652
        self.assertEqual('1,2,3', val)
 
2653
        # Reminder: lists should be registered as such explicitely, otherwise
 
2654
        # the conversion needs to be done afterwards.
 
2655
        self.assertEqual(['1', '2', '3'],
 
2656
                         opt.convert_from_unicode(self.store, val))
 
2657
 
 
2658
    def test_multiple_overrides(self):
 
2659
        self.store._from_cmdline(['a=b', 'x=y'])
 
2660
        section = self.get_section()
 
2661
        self.assertEquals('b', section.get('a'))
 
2662
        self.assertEquals('y', section.get('x'))
 
2663
 
 
2664
    def test_wrong_syntax(self):
 
2665
        self.assertRaises(errors.BzrCommandError,
 
2666
                          self.store._from_cmdline, ['a=b', 'c'])
 
2667
 
 
2668
class TestStoreMinimalAPI(tests.TestCaseWithTransport):
 
2669
 
 
2670
    scenarios = [(key, {'get_store': builder}) for key, builder
 
2671
                 in config.test_store_builder_registry.iteritems()] + [
 
2672
        ('cmdline', {'get_store': lambda test: config.CommandLineStore()})]
 
2673
 
 
2674
    def test_id(self):
 
2675
        store = self.get_store(self)
 
2676
        if type(store) == config.TransportIniFileStore:
 
2677
            raise tests.TestNotApplicable(
 
2678
                "%s is not a concrete Store implementation"
 
2679
                " so it doesn't need an id" % (store.__class__.__name__,))
 
2680
        self.assertIsNot(None, store.id)
 
2681
 
 
2682
 
2324
2683
class TestStore(tests.TestCaseWithTransport):
2325
2684
 
2326
 
    def assertSectionContent(self, expected, section):
 
2685
    def assertSectionContent(self, expected, (store, section)):
2327
2686
        """Assert that some options have the proper values in a section."""
2328
2687
        expected_name, expected_options = expected
2329
2688
        self.assertEquals(expected_name, section.id)
2337
2696
    scenarios = [(key, {'get_store': builder}) for key, builder
2338
2697
                 in config.test_store_builder_registry.iteritems()]
2339
2698
 
2340
 
    def setUp(self):
2341
 
        super(TestReadonlyStore, self).setUp()
2342
 
 
2343
2699
    def test_building_delays_load(self):
2344
2700
        store = self.get_store(self)
2345
2701
        self.assertEquals(False, store.is_loaded())
2371
2727
        self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2372
2728
 
2373
2729
 
 
2730
class TestStoreQuoting(TestStore):
 
2731
 
 
2732
    scenarios = [(key, {'get_store': builder}) for key, builder
 
2733
                 in config.test_store_builder_registry.iteritems()]
 
2734
 
 
2735
    def setUp(self):
 
2736
        super(TestStoreQuoting, self).setUp()
 
2737
        self.store = self.get_store(self)
 
2738
        # We need a loaded store but any content will do
 
2739
        self.store._load_from_string('')
 
2740
 
 
2741
    def assertIdempotent(self, s):
 
2742
        """Assert that quoting an unquoted string is a no-op and vice-versa.
 
2743
 
 
2744
        What matters here is that option values, as they appear in a store, can
 
2745
        be safely round-tripped out of the store and back.
 
2746
 
 
2747
        :param s: A string, quoted if required.
 
2748
        """
 
2749
        self.assertEquals(s, self.store.quote(self.store.unquote(s)))
 
2750
        self.assertEquals(s, self.store.unquote(self.store.quote(s)))
 
2751
 
 
2752
    def test_empty_string(self):
 
2753
        if isinstance(self.store, config.IniFileStore):
 
2754
            # configobj._quote doesn't handle empty values
 
2755
            self.assertRaises(AssertionError,
 
2756
                              self.assertIdempotent, '')
 
2757
        else:
 
2758
            self.assertIdempotent('')
 
2759
        # But quoted empty strings are ok
 
2760
        self.assertIdempotent('""')
 
2761
 
 
2762
    def test_embedded_spaces(self):
 
2763
        self.assertIdempotent('" a b c "')
 
2764
 
 
2765
    def test_embedded_commas(self):
 
2766
        self.assertIdempotent('" a , b c "')
 
2767
 
 
2768
    def test_simple_comma(self):
 
2769
        if isinstance(self.store, config.IniFileStore):
 
2770
            # configobj requires that lists are special-cased
 
2771
           self.assertRaises(AssertionError,
 
2772
                             self.assertIdempotent, ',')
 
2773
        else:
 
2774
            self.assertIdempotent(',')
 
2775
        # When a single comma is required, quoting is also required
 
2776
        self.assertIdempotent('","')
 
2777
 
 
2778
    def test_list(self):
 
2779
        if isinstance(self.store, config.IniFileStore):
 
2780
            # configobj requires that lists are special-cased
 
2781
            self.assertRaises(AssertionError,
 
2782
                              self.assertIdempotent, 'a,b')
 
2783
        else:
 
2784
            self.assertIdempotent('a,b')
 
2785
 
 
2786
 
 
2787
class TestDictFromStore(tests.TestCase):
 
2788
 
 
2789
    def test_unquote_not_string(self):
 
2790
        conf = config.MemoryStack('x=2\n[a_section]\na=1\n')
 
2791
        value = conf.get('a_section')
 
2792
        # Urgh, despite 'conf' asking for the no-name section, we get the
 
2793
        # content of another section as a dict o_O
 
2794
        self.assertEquals({'a': '1'}, value)
 
2795
        unquoted = conf.store.unquote(value)
 
2796
        # Which cannot be unquoted but shouldn't crash either (the use cases
 
2797
        # are getting the value or displaying it. In the later case, '%s' will
 
2798
        # do).
 
2799
        self.assertEquals({'a': '1'}, unquoted)
 
2800
        self.assertEquals("{u'a': u'1'}", '%s' % (unquoted,))
 
2801
 
 
2802
 
2374
2803
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2375
 
    """Simulate loading a config store without content of various encodings.
 
2804
    """Simulate loading a config store with content of various encodings.
2376
2805
 
2377
2806
    All files produced by bzr are in utf8 content.
2378
2807
 
2391
2820
        utf8_content = unicode_content.encode('utf8')
2392
2821
        # Store the raw content in the config file
2393
2822
        t.put_bytes('foo.conf', utf8_content)
2394
 
        store = config.IniFileStore(t, 'foo.conf')
 
2823
        store = config.TransportIniFileStore(t, 'foo.conf')
2395
2824
        store.load()
2396
2825
        stack = config.Stack([store.get_sections], store)
2397
2826
        self.assertEquals(unicode_user, stack.get('user'))
2400
2829
        """Ensure we display a proper error on non-ascii, non utf-8 content."""
2401
2830
        t = self.get_transport()
2402
2831
        t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2403
 
        store = config.IniFileStore(t, 'foo.conf')
 
2832
        store = config.TransportIniFileStore(t, 'foo.conf')
2404
2833
        self.assertRaises(errors.ConfigContentError, store.load)
2405
2834
 
2406
2835
    def test_load_erroneous_content(self):
2407
2836
        """Ensure we display a proper error on content that can't be parsed."""
2408
2837
        t = self.get_transport()
2409
2838
        t.put_bytes('foo.conf', '[open_section\n')
2410
 
        store = config.IniFileStore(t, 'foo.conf')
 
2839
        store = config.TransportIniFileStore(t, 'foo.conf')
2411
2840
        self.assertRaises(errors.ParseConfigError, store.load)
2412
2841
 
 
2842
    def test_load_permission_denied(self):
 
2843
        """Ensure we get warned when trying to load an inaccessible file."""
 
2844
        warnings = []
 
2845
        def warning(*args):
 
2846
            warnings.append(args[0] % args[1:])
 
2847
        self.overrideAttr(trace, 'warning', warning)
 
2848
 
 
2849
        t = self.get_transport()
 
2850
 
 
2851
        def get_bytes(relpath):
 
2852
            raise errors.PermissionDenied(relpath, "")
 
2853
        t.get_bytes = get_bytes
 
2854
        store = config.TransportIniFileStore(t, 'foo.conf')
 
2855
        self.assertRaises(errors.PermissionDenied, store.load)
 
2856
        self.assertEquals(
 
2857
            warnings,
 
2858
            [u'Permission denied while trying to load configuration store %s.'
 
2859
             % store.external_url()])
 
2860
 
2413
2861
 
2414
2862
class TestIniConfigContent(tests.TestCaseWithTransport):
2415
 
    """Simulate loading a IniBasedConfig without content of various encodings.
 
2863
    """Simulate loading a IniBasedConfig with content of various encodings.
2416
2864
 
2417
2865
    All files produced by bzr are in utf8 content.
2418
2866
 
2473
2921
        store.save()
2474
2922
        self.assertEquals(False, self.has_store(store))
2475
2923
 
 
2924
    def test_mutable_section_shared(self):
 
2925
        store = self.get_store(self)
 
2926
        store._load_from_string('foo=bar\n')
 
2927
        # FIXME: There should be a better way than relying on the test
 
2928
        # parametrization to identify branch.conf -- vila 2011-0526
 
2929
        if self.store_id in ('branch', 'remote_branch'):
 
2930
            # branch stores requires write locked branches
 
2931
            self.addCleanup(store.branch.lock_write().unlock)
 
2932
        section1 = store.get_mutable_section(None)
 
2933
        section2 = store.get_mutable_section(None)
 
2934
        # If we get different sections, different callers won't share the
 
2935
        # modification
 
2936
        self.assertIs(section1, section2)
 
2937
 
2476
2938
    def test_save_emptied_succeeds(self):
2477
2939
        store = self.get_store(self)
2478
2940
        store._load_from_string('foo=bar\n')
 
2941
        # FIXME: There should be a better way than relying on the test
 
2942
        # parametrization to identify branch.conf -- vila 2011-0526
 
2943
        if self.store_id in ('branch', 'remote_branch'):
 
2944
            # branch stores requires write locked branches
 
2945
            self.addCleanup(store.branch.lock_write().unlock)
2479
2946
        section = store.get_mutable_section(None)
2480
2947
        section.remove('foo')
2481
2948
        store.save()
2502
2969
 
2503
2970
    def test_set_option_in_empty_store(self):
2504
2971
        store = self.get_store(self)
 
2972
        # FIXME: There should be a better way than relying on the test
 
2973
        # parametrization to identify branch.conf -- vila 2011-0526
 
2974
        if self.store_id in ('branch', 'remote_branch'):
 
2975
            # branch stores requires write locked branches
 
2976
            self.addCleanup(store.branch.lock_write().unlock)
2505
2977
        section = store.get_mutable_section(None)
2506
2978
        section.set('foo', 'bar')
2507
2979
        store.save()
2513
2985
    def test_set_option_in_default_section(self):
2514
2986
        store = self.get_store(self)
2515
2987
        store._load_from_string('')
 
2988
        # FIXME: There should be a better way than relying on the test
 
2989
        # parametrization to identify branch.conf -- vila 2011-0526
 
2990
        if self.store_id in ('branch', 'remote_branch'):
 
2991
            # branch stores requires write locked branches
 
2992
            self.addCleanup(store.branch.lock_write().unlock)
2516
2993
        section = store.get_mutable_section(None)
2517
2994
        section.set('foo', 'bar')
2518
2995
        store.save()
2524
3001
    def test_set_option_in_named_section(self):
2525
3002
        store = self.get_store(self)
2526
3003
        store._load_from_string('')
 
3004
        # FIXME: There should be a better way than relying on the test
 
3005
        # parametrization to identify branch.conf -- vila 2011-0526
 
3006
        if self.store_id in ('branch', 'remote_branch'):
 
3007
            # branch stores requires write locked branches
 
3008
            self.addCleanup(store.branch.lock_write().unlock)
2527
3009
        section = store.get_mutable_section('baz')
2528
3010
        section.set('foo', 'bar')
2529
3011
        store.save()
2533
3015
        self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2534
3016
 
2535
3017
    def test_load_hook(self):
2536
 
        # We first needs to ensure that the store exists
 
3018
        # First, we need to ensure that the store exists
2537
3019
        store = self.get_store(self)
 
3020
        # FIXME: There should be a better way than relying on the test
 
3021
        # parametrization to identify branch.conf -- vila 2011-0526
 
3022
        if self.store_id in ('branch', 'remote_branch'):
 
3023
            # branch stores requires write locked branches
 
3024
            self.addCleanup(store.branch.lock_write().unlock)
2538
3025
        section = store.get_mutable_section('baz')
2539
3026
        section.set('foo', 'bar')
2540
3027
        store.save()
2556
3043
        config.ConfigHooks.install_named_hook('save', hook, None)
2557
3044
        self.assertLength(0, calls)
2558
3045
        store = self.get_store(self)
 
3046
        # FIXME: There should be a better way than relying on the test
 
3047
        # parametrization to identify branch.conf -- vila 2011-0526
 
3048
        if self.store_id in ('branch', 'remote_branch'):
 
3049
            # branch stores requires write locked branches
 
3050
            self.addCleanup(store.branch.lock_write().unlock)
2559
3051
        section = store.get_mutable_section('baz')
2560
3052
        section.set('foo', 'bar')
2561
3053
        store.save()
2562
3054
        self.assertLength(1, calls)
2563
3055
        self.assertEquals((store,), calls[0])
2564
3056
 
2565
 
 
2566
 
class TestIniFileStore(TestStore):
 
3057
    def test_set_mark_dirty(self):
 
3058
        stack = config.MemoryStack('')
 
3059
        self.assertLength(0, stack.store.dirty_sections)
 
3060
        stack.set('foo', 'baz')
 
3061
        self.assertLength(1, stack.store.dirty_sections)
 
3062
        self.assertTrue(stack.store._need_saving())
 
3063
 
 
3064
    def test_remove_mark_dirty(self):
 
3065
        stack = config.MemoryStack('foo=bar')
 
3066
        self.assertLength(0, stack.store.dirty_sections)
 
3067
        stack.remove('foo')
 
3068
        self.assertLength(1, stack.store.dirty_sections)
 
3069
        self.assertTrue(stack.store._need_saving())
 
3070
 
 
3071
 
 
3072
class TestStoreSaveChanges(tests.TestCaseWithTransport):
 
3073
    """Tests that config changes are kept in memory and saved on-demand."""
 
3074
 
 
3075
    def setUp(self):
 
3076
        super(TestStoreSaveChanges, self).setUp()
 
3077
        self.transport = self.get_transport()
 
3078
        # Most of the tests involve two stores pointing to the same persistent
 
3079
        # storage to observe the effects of concurrent changes
 
3080
        self.st1 = config.TransportIniFileStore(self.transport, 'foo.conf')
 
3081
        self.st2 = config.TransportIniFileStore(self.transport, 'foo.conf')
 
3082
        self.warnings = []
 
3083
        def warning(*args):
 
3084
            self.warnings.append(args[0] % args[1:])
 
3085
        self.overrideAttr(trace, 'warning', warning)
 
3086
 
 
3087
    def has_store(self, store):
 
3088
        store_basename = urlutils.relative_url(self.transport.external_url(),
 
3089
                                               store.external_url())
 
3090
        return self.transport.has(store_basename)
 
3091
 
 
3092
    def get_stack(self, store):
 
3093
        # Any stack will do as long as it uses the right store, just a single
 
3094
        # no-name section is enough
 
3095
        return config.Stack([store.get_sections], store)
 
3096
 
 
3097
    def test_no_changes_no_save(self):
 
3098
        s = self.get_stack(self.st1)
 
3099
        s.store.save_changes()
 
3100
        self.assertEquals(False, self.has_store(self.st1))
 
3101
 
 
3102
    def test_unrelated_concurrent_update(self):
 
3103
        s1 = self.get_stack(self.st1)
 
3104
        s2 = self.get_stack(self.st2)
 
3105
        s1.set('foo', 'bar')
 
3106
        s2.set('baz', 'quux')
 
3107
        s1.store.save()
 
3108
        # Changes don't propagate magically
 
3109
        self.assertEquals(None, s1.get('baz'))
 
3110
        s2.store.save_changes()
 
3111
        self.assertEquals('quux', s2.get('baz'))
 
3112
        # Changes are acquired when saving
 
3113
        self.assertEquals('bar', s2.get('foo'))
 
3114
        # Since there is no overlap, no warnings are emitted
 
3115
        self.assertLength(0, self.warnings)
 
3116
 
 
3117
    def test_concurrent_update_modified(self):
 
3118
        s1 = self.get_stack(self.st1)
 
3119
        s2 = self.get_stack(self.st2)
 
3120
        s1.set('foo', 'bar')
 
3121
        s2.set('foo', 'baz')
 
3122
        s1.store.save()
 
3123
        # Last speaker wins
 
3124
        s2.store.save_changes()
 
3125
        self.assertEquals('baz', s2.get('foo'))
 
3126
        # But the user get a warning
 
3127
        self.assertLength(1, self.warnings)
 
3128
        warning = self.warnings[0]
 
3129
        self.assertStartsWith(warning, 'Option foo in section None')
 
3130
        self.assertEndsWith(warning, 'was changed from <CREATED> to bar.'
 
3131
                            ' The baz value will be saved.')
 
3132
 
 
3133
    def test_concurrent_deletion(self):
 
3134
        self.st1._load_from_string('foo=bar')
 
3135
        self.st1.save()
 
3136
        s1 = self.get_stack(self.st1)
 
3137
        s2 = self.get_stack(self.st2)
 
3138
        s1.remove('foo')
 
3139
        s2.remove('foo')
 
3140
        s1.store.save_changes()
 
3141
        # No warning yet
 
3142
        self.assertLength(0, self.warnings)
 
3143
        s2.store.save_changes()
 
3144
        # Now we get one
 
3145
        self.assertLength(1, self.warnings)
 
3146
        warning = self.warnings[0]
 
3147
        self.assertStartsWith(warning, 'Option foo in section None')
 
3148
        self.assertEndsWith(warning, 'was changed from bar to <CREATED>.'
 
3149
                            ' The <DELETED> value will be saved.')
 
3150
 
 
3151
 
 
3152
class TestQuotingIniFileStore(tests.TestCaseWithTransport):
 
3153
 
 
3154
    def get_store(self):
 
3155
        return config.TransportIniFileStore(self.get_transport(), 'foo.conf')
 
3156
 
 
3157
    def test_get_quoted_string(self):
 
3158
        store = self.get_store()
 
3159
        store._load_from_string('foo= " abc "')
 
3160
        stack = config.Stack([store.get_sections])
 
3161
        self.assertEquals(' abc ', stack.get('foo'))
 
3162
 
 
3163
    def test_set_quoted_string(self):
 
3164
        store = self.get_store()
 
3165
        stack = config.Stack([store.get_sections], store)
 
3166
        stack.set('foo', ' a b c ')
 
3167
        store.save()
 
3168
        self.assertFileEqual('foo = " a b c "' + os.linesep, 'foo.conf')
 
3169
 
 
3170
 
 
3171
class TestTransportIniFileStore(TestStore):
2567
3172
 
2568
3173
    def test_loading_unknown_file_fails(self):
2569
 
        store = config.IniFileStore(self.get_transport(), 'I-do-not-exist')
 
3174
        store = config.TransportIniFileStore(self.get_transport(),
 
3175
            'I-do-not-exist')
2570
3176
        self.assertRaises(errors.NoSuchFile, store.load)
2571
3177
 
2572
3178
    def test_invalid_content(self):
2573
 
        store = config.IniFileStore(self.get_transport(), 'foo.conf', )
 
3179
        store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
2574
3180
        self.assertEquals(False, store.is_loaded())
2575
3181
        exc = self.assertRaises(
2576
3182
            errors.ParseConfigError, store._load_from_string,
2584
3190
        # option names share the same name space...)
2585
3191
        # FIXME: This should be fixed by forbidding dicts as values ?
2586
3192
        # -- vila 2011-04-05
2587
 
        store = config.IniFileStore(self.get_transport(), 'foo.conf', )
 
3193
        store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
2588
3194
        store._load_from_string('''
2589
3195
foo=bar
2590
3196
l=1,2
2600
3206
        sections = list(store.get_sections())
2601
3207
        self.assertLength(4, sections)
2602
3208
        # The default section has no name.
2603
 
        # List values are provided as lists
2604
 
        self.assertSectionContent((None, {'foo': 'bar', 'l': ['1', '2']}),
 
3209
        # List values are provided as strings and need to be explicitly
 
3210
        # converted by specifying from_unicode=list_from_store at option
 
3211
        # registration
 
3212
        self.assertSectionContent((None, {'foo': 'bar', 'l': u'1,2'}),
2605
3213
                                  sections[0])
2606
3214
        self.assertSectionContent(
2607
3215
            ('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
2637
3245
 
2638
3246
    def setUp(self):
2639
3247
        super(TestConcurrentStoreUpdates, self).setUp()
2640
 
        self._content = 'one=1\ntwo=2\n'
2641
3248
        self.stack = self.get_stack(self)
2642
3249
        if not isinstance(self.stack, config._CompatibleStack):
2643
3250
            raise tests.TestNotApplicable(
2644
3251
                '%s is not meant to be compatible with the old config design'
2645
3252
                % (self.stack,))
2646
 
        self.stack.store._load_from_string(self._content)
 
3253
        self.stack.set('one', '1')
 
3254
        self.stack.set('two', '2')
2647
3255
        # Flush the store
2648
3256
        self.stack.store.save()
2649
3257
 
2753
3361
    # FIXME: It may be worth looking into removing the lock dir when it's not
2754
3362
    # needed anymore and look at possible fallouts for concurrent lockers. This
2755
3363
    # will matter if/when we use config files outside of bazaar directories
2756
 
    # (.bazaar or .bzr) -- vila 20110-04-11
 
3364
    # (.bazaar or .bzr) -- vila 20110-04-111
2757
3365
 
2758
3366
 
2759
3367
class TestSectionMatcher(TestStore):
2760
3368
 
2761
 
    scenarios = [('location', {'matcher': config.LocationMatcher})]
 
3369
    scenarios = [('location', {'matcher': config.LocationMatcher}),
 
3370
                 ('id', {'matcher': config.NameMatcher}),]
2762
3371
 
2763
 
    def get_store(self, file_name):
2764
 
        return config.IniFileStore(self.get_readonly_transport(), file_name)
 
3372
    def setUp(self):
 
3373
        super(TestSectionMatcher, self).setUp()
 
3374
        # Any simple store is good enough
 
3375
        self.get_store = config.test_store_builder_registry.get('configobj')
2765
3376
 
2766
3377
    def test_no_matches_for_empty_stores(self):
2767
 
        store = self.get_store('foo.conf')
 
3378
        store = self.get_store(self)
2768
3379
        store._load_from_string('')
2769
3380
        matcher = self.matcher(store, '/bar')
2770
3381
        self.assertEquals([], list(matcher.get_sections()))
2771
3382
 
2772
3383
    def test_build_doesnt_load_store(self):
2773
 
        store = self.get_store('foo.conf')
2774
 
        matcher = self.matcher(store, '/bar')
 
3384
        store = self.get_store(self)
 
3385
        self.matcher(store, '/bar')
2775
3386
        self.assertFalse(store.is_loaded())
2776
3387
 
2777
3388
 
2779
3390
 
2780
3391
    def get_section(self, options, extra_path):
2781
3392
        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)
 
3393
        return config.LocationSection(section, extra_path)
2784
3394
 
2785
3395
    def test_simple_option(self):
2786
3396
        section = self.get_section({'foo': 'bar'}, '')
2800
3410
 
2801
3411
class TestLocationMatcher(TestStore):
2802
3412
 
2803
 
    def get_store(self, file_name):
2804
 
        return config.IniFileStore(self.get_readonly_transport(), file_name)
 
3413
    def setUp(self):
 
3414
        super(TestLocationMatcher, self).setUp()
 
3415
        # Any simple store is good enough
 
3416
        self.get_store = config.test_store_builder_registry.get('configobj')
 
3417
 
 
3418
    def test_unrelated_section_excluded(self):
 
3419
        store = self.get_store(self)
 
3420
        store._load_from_string('''
 
3421
[/foo]
 
3422
section=/foo
 
3423
[/foo/baz]
 
3424
section=/foo/baz
 
3425
[/foo/bar]
 
3426
section=/foo/bar
 
3427
[/foo/bar/baz]
 
3428
section=/foo/bar/baz
 
3429
[/quux/quux]
 
3430
section=/quux/quux
 
3431
''')
 
3432
        self.assertEquals(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
 
3433
                           '/quux/quux'],
 
3434
                          [section.id for _, section in store.get_sections()])
 
3435
        matcher = config.LocationMatcher(store, '/foo/bar/quux')
 
3436
        sections = [section for _, section in matcher.get_sections()]
 
3437
        self.assertEquals(['/foo/bar', '/foo'],
 
3438
                          [section.id for section in sections])
 
3439
        self.assertEquals(['quux', 'bar/quux'],
 
3440
                          [section.extra_path for section in sections])
2805
3441
 
2806
3442
    def test_more_specific_sections_first(self):
2807
 
        store = self.get_store('foo.conf')
 
3443
        store = self.get_store(self)
2808
3444
        store._load_from_string('''
2809
3445
[/foo]
2810
3446
section=/foo
2812
3448
section=/foo/bar
2813
3449
''')
2814
3450
        self.assertEquals(['/foo', '/foo/bar'],
2815
 
                          [section.id for section in store.get_sections()])
 
3451
                          [section.id for _, section in store.get_sections()])
2816
3452
        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])
 
3453
        sections = [section for _, section in matcher.get_sections()]
2820
3454
        self.assertEquals(['/foo/bar', '/foo'],
2821
3455
                          [section.id for section in sections])
2822
3456
        self.assertEquals(['baz', 'bar/baz'],
2825
3459
    def test_appendpath_in_no_name_section(self):
2826
3460
        # It's a bit weird to allow appendpath in a no-name section, but
2827
3461
        # someone may found a use for it
2828
 
        store = self.get_store('foo.conf')
 
3462
        store = self.get_store(self)
2829
3463
        store._load_from_string('''
2830
3464
foo=bar
2831
3465
foo:policy = appendpath
2833
3467
        matcher = config.LocationMatcher(store, 'dir/subdir')
2834
3468
        sections = list(matcher.get_sections())
2835
3469
        self.assertLength(1, sections)
2836
 
        self.assertEquals('bar/dir/subdir', sections[0].get('foo'))
 
3470
        self.assertEquals('bar/dir/subdir', sections[0][1].get('foo'))
2837
3471
 
2838
3472
    def test_file_urls_are_normalized(self):
2839
 
        store = self.get_store('foo.conf')
 
3473
        store = self.get_store(self)
2840
3474
        if sys.platform == 'win32':
2841
3475
            expected_url = 'file:///C:/dir/subdir'
2842
3476
            expected_location = 'C:/dir/subdir'
2846
3480
        matcher = config.LocationMatcher(store, expected_url)
2847
3481
        self.assertEquals(expected_location, matcher.location)
2848
3482
 
2849
 
 
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'))
 
3483
    def test_branch_name_colo(self):
 
3484
        store = self.get_store(self)
 
3485
        store._load_from_string(dedent("""\
 
3486
            [/]
 
3487
            push_location=my{branchname}
 
3488
        """))
 
3489
        matcher = config.LocationMatcher(store, 'file:///,branch=example%3c')
 
3490
        self.assertEqual('example<', matcher.branch_name)
 
3491
        ((_, section),) = matcher.get_sections()
 
3492
        self.assertEqual('example<', section.locals['branchname'])
 
3493
 
 
3494
    def test_branch_name_basename(self):
 
3495
        store = self.get_store(self)
 
3496
        store._load_from_string(dedent("""\
 
3497
            [/]
 
3498
            push_location=my{branchname}
 
3499
        """))
 
3500
        matcher = config.LocationMatcher(store, 'file:///parent/example%3c')
 
3501
        self.assertEqual('example<', matcher.branch_name)
 
3502
        ((_, section),) = matcher.get_sections()
 
3503
        self.assertEqual('example<', section.locals['branchname'])
 
3504
 
 
3505
 
 
3506
class TestStartingPathMatcher(TestStore):
 
3507
 
 
3508
    def setUp(self):
 
3509
        super(TestStartingPathMatcher, self).setUp()
 
3510
        # Any simple store is good enough
 
3511
        self.store = config.IniFileStore()
 
3512
 
 
3513
    def assertSectionIDs(self, expected, location, content):
 
3514
        self.store._load_from_string(content)
 
3515
        matcher = config.StartingPathMatcher(self.store, location)
 
3516
        sections = list(matcher.get_sections())
 
3517
        self.assertLength(len(expected), sections)
 
3518
        self.assertEqual(expected, [section.id for _, section in sections])
 
3519
        return sections
 
3520
 
 
3521
    def test_empty(self):
 
3522
        self.assertSectionIDs([], self.get_url(), '')
 
3523
 
 
3524
    def test_url_vs_local_paths(self):
 
3525
        # The matcher location is an url and the section names are local paths
 
3526
        self.assertSectionIDs(['/foo/bar', '/foo'],
 
3527
                              'file:///foo/bar/baz', '''\
 
3528
[/foo]
 
3529
[/foo/bar]
 
3530
''')
 
3531
 
 
3532
    def test_local_path_vs_url(self):
 
3533
        # The matcher location is a local path and the section names are urls
 
3534
        self.assertSectionIDs(['file:///foo/bar', 'file:///foo'],
 
3535
                              '/foo/bar/baz', '''\
 
3536
[file:///foo]
 
3537
[file:///foo/bar]
 
3538
''')
 
3539
 
 
3540
 
 
3541
    def test_no_name_section_included_when_present(self):
 
3542
        # Note that other tests will cover the case where the no-name section
 
3543
        # is empty and as such, not included.
 
3544
        sections = self.assertSectionIDs(['/foo/bar', '/foo', None],
 
3545
                                         '/foo/bar/baz', '''\
 
3546
option = defined so the no-name section exists
 
3547
[/foo]
 
3548
[/foo/bar]
 
3549
''')
 
3550
        self.assertEquals(['baz', 'bar/baz', '/foo/bar/baz'],
 
3551
                          [s.locals['relpath'] for _, s in sections])
 
3552
 
 
3553
    def test_order_reversed(self):
 
3554
        self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
 
3555
[/foo]
 
3556
[/foo/bar]
 
3557
''')
 
3558
 
 
3559
    def test_unrelated_section_excluded(self):
 
3560
        self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
 
3561
[/foo]
 
3562
[/foo/qux]
 
3563
[/foo/bar]
 
3564
''')
 
3565
 
 
3566
    def test_glob_included(self):
 
3567
        sections = self.assertSectionIDs(['/foo/*/baz', '/foo/b*', '/foo'],
 
3568
                                         '/foo/bar/baz', '''\
 
3569
[/foo]
 
3570
[/foo/qux]
 
3571
[/foo/b*]
 
3572
[/foo/*/baz]
 
3573
''')
 
3574
        # Note that 'baz' as a relpath for /foo/b* is not fully correct, but
 
3575
        # nothing really is... as far using {relpath} to append it to something
 
3576
        # else, this seems good enough though.
 
3577
        self.assertEquals(['', 'baz', 'bar/baz'],
 
3578
                          [s.locals['relpath'] for _, s in sections])
 
3579
 
 
3580
    def test_respect_order(self):
 
3581
        self.assertSectionIDs(['/foo', '/foo/b*', '/foo/*/baz'],
 
3582
                              '/foo/bar/baz', '''\
 
3583
[/foo/*/baz]
 
3584
[/foo/qux]
 
3585
[/foo/b*]
 
3586
[/foo]
 
3587
''')
 
3588
 
 
3589
 
 
3590
class TestNameMatcher(TestStore):
 
3591
 
 
3592
    def setUp(self):
 
3593
        super(TestNameMatcher, self).setUp()
 
3594
        self.matcher = config.NameMatcher
 
3595
        # Any simple store is good enough
 
3596
        self.get_store = config.test_store_builder_registry.get('configobj')
 
3597
 
 
3598
    def get_matching_sections(self, name):
 
3599
        store = self.get_store(self)
 
3600
        store._load_from_string('''
 
3601
[foo]
 
3602
option=foo
 
3603
[foo/baz]
 
3604
option=foo/baz
 
3605
[bar]
 
3606
option=bar
 
3607
''')
 
3608
        matcher = self.matcher(store, name)
 
3609
        return list(matcher.get_sections())
 
3610
 
 
3611
    def test_matching(self):
 
3612
        sections = self.get_matching_sections('foo')
 
3613
        self.assertLength(1, sections)
 
3614
        self.assertSectionContent(('foo', {'option': 'foo'}), sections[0])
 
3615
 
 
3616
    def test_not_matching(self):
 
3617
        sections = self.get_matching_sections('baz')
 
3618
        self.assertLength(0, sections)
 
3619
 
 
3620
 
 
3621
class TestBaseStackGet(tests.TestCase):
 
3622
 
 
3623
    def setUp(self):
 
3624
        super(TestBaseStackGet, self).setUp()
 
3625
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
 
3626
 
 
3627
    def test_get_first_definition(self):
 
3628
        store1 = config.IniFileStore()
 
3629
        store1._load_from_string('foo=bar')
 
3630
        store2 = config.IniFileStore()
 
3631
        store2._load_from_string('foo=baz')
 
3632
        conf = config.Stack([store1.get_sections, store2.get_sections])
 
3633
        self.assertEquals('bar', conf.get('foo'))
2859
3634
 
2860
3635
    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)
 
3636
        config.option_registry.register(config.Option('foo', default='bar'))
 
3637
        conf_stack = config.Stack([])
2865
3638
        self.assertEquals('bar', conf_stack.get('foo'))
2866
3639
 
2867
3640
    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)
 
3641
        config.option_registry.register(config.Option('foo'))
 
3642
        conf_stack = config.Stack([])
2872
3643
        self.assertEquals(None, conf_stack.get('foo'))
2873
3644
 
2874
3645
    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())
 
3646
        conf_stack = config.Stack([])
2878
3647
        self.assertEquals(None, conf_stack.get('foo'))
2879
3648
 
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
3649
    def test_get_for_empty_section_callable(self):
2893
3650
        conf_stack = config.Stack([lambda : []])
2894
3651
        self.assertEquals(None, conf_stack.get('foo'))
2895
3652
 
2896
3653
    def test_get_for_broken_callable(self):
2897
3654
        # Trying to use and invalid callable raises an exception on first use
2898
 
        conf_stack = config.Stack([lambda : object()])
 
3655
        conf_stack = config.Stack([object])
2899
3656
        self.assertRaises(TypeError, conf_stack.get, 'foo')
2900
3657
 
2901
3658
 
 
3659
class TestStackWithSimpleStore(tests.TestCase):
 
3660
 
 
3661
    def setUp(self):
 
3662
        super(TestStackWithSimpleStore, self).setUp()
 
3663
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
 
3664
        self.registry = config.option_registry
 
3665
 
 
3666
    def get_conf(self, content=None):
 
3667
        return config.MemoryStack(content)
 
3668
 
 
3669
    def test_override_value_from_env(self):
 
3670
        self.overrideEnv('FOO', None)
 
3671
        self.registry.register(
 
3672
            config.Option('foo', default='bar', override_from_env=['FOO']))
 
3673
        self.overrideEnv('FOO', 'quux')
 
3674
        # Env variable provides a default taking over the option one
 
3675
        conf = self.get_conf('foo=store')
 
3676
        self.assertEquals('quux', conf.get('foo'))
 
3677
 
 
3678
    def test_first_override_value_from_env_wins(self):
 
3679
        self.overrideEnv('NO_VALUE', None)
 
3680
        self.overrideEnv('FOO', None)
 
3681
        self.overrideEnv('BAZ', None)
 
3682
        self.registry.register(
 
3683
            config.Option('foo', default='bar',
 
3684
                          override_from_env=['NO_VALUE', 'FOO', 'BAZ']))
 
3685
        self.overrideEnv('FOO', 'foo')
 
3686
        self.overrideEnv('BAZ', 'baz')
 
3687
        # The first env var set wins
 
3688
        conf = self.get_conf('foo=store')
 
3689
        self.assertEquals('foo', conf.get('foo'))
 
3690
 
 
3691
 
 
3692
class TestMemoryStack(tests.TestCase):
 
3693
 
 
3694
    def test_get(self):
 
3695
        conf = config.MemoryStack('foo=bar')
 
3696
        self.assertEquals('bar', conf.get('foo'))
 
3697
 
 
3698
    def test_set(self):
 
3699
        conf = config.MemoryStack('foo=bar')
 
3700
        conf.set('foo', 'baz')
 
3701
        self.assertEquals('baz', conf.get('foo'))
 
3702
 
 
3703
    def test_no_content(self):
 
3704
        conf = config.MemoryStack()
 
3705
        # No content means no loading
 
3706
        self.assertFalse(conf.store.is_loaded())
 
3707
        self.assertRaises(NotImplementedError, conf.get, 'foo')
 
3708
        # But a content can still be provided
 
3709
        conf.store._load_from_string('foo=bar')
 
3710
        self.assertEquals('bar', conf.get('foo'))
 
3711
 
 
3712
 
 
3713
class TestStackIterSections(tests.TestCase):
 
3714
 
 
3715
    def test_empty_stack(self):
 
3716
        conf = config.Stack([])
 
3717
        sections = list(conf.iter_sections())
 
3718
        self.assertLength(0, sections)
 
3719
 
 
3720
    def test_empty_store(self):
 
3721
        store = config.IniFileStore()
 
3722
        store._load_from_string('')
 
3723
        conf = config.Stack([store.get_sections])
 
3724
        sections = list(conf.iter_sections())
 
3725
        self.assertLength(0, sections)
 
3726
 
 
3727
    def test_simple_store(self):
 
3728
        store = config.IniFileStore()
 
3729
        store._load_from_string('foo=bar')
 
3730
        conf = config.Stack([store.get_sections])
 
3731
        tuples = list(conf.iter_sections())
 
3732
        self.assertLength(1, tuples)
 
3733
        (found_store, found_section) = tuples[0]
 
3734
        self.assertIs(store, found_store)
 
3735
 
 
3736
    def test_two_stores(self):
 
3737
        store1 = config.IniFileStore()
 
3738
        store1._load_from_string('foo=bar')
 
3739
        store2 = config.IniFileStore()
 
3740
        store2._load_from_string('bar=qux')
 
3741
        conf = config.Stack([store1.get_sections, store2.get_sections])
 
3742
        tuples = list(conf.iter_sections())
 
3743
        self.assertLength(2, tuples)
 
3744
        self.assertIs(store1, tuples[0][0])
 
3745
        self.assertIs(store2, tuples[1][0])
 
3746
 
 
3747
 
2902
3748
class TestStackWithTransport(tests.TestCaseWithTransport):
2903
3749
 
2904
3750
    scenarios = [(key, {'get_stack': builder}) for key, builder
2909
3755
 
2910
3756
    def test_build_stack(self):
2911
3757
        # Just a smoke test to help debug builders
2912
 
        stack = self.get_stack(self)
 
3758
        self.get_stack(self)
2913
3759
 
2914
3760
 
2915
3761
class TestStackGet(TestStackWithTransport):
2916
3762
 
 
3763
    def setUp(self):
 
3764
        super(TestStackGet, self).setUp()
 
3765
        self.conf = self.get_stack(self)
 
3766
 
2917
3767
    def test_get_for_empty_stack(self):
2918
 
        conf = self.get_stack(self)
2919
 
        self.assertEquals(None, conf.get('foo'))
 
3768
        self.assertEquals(None, self.conf.get('foo'))
2920
3769
 
2921
3770
    def test_get_hook(self):
2922
 
        conf = self.get_stack(self)
2923
 
        conf.store._load_from_string('foo=bar')
 
3771
        self.conf.set('foo', 'bar')
2924
3772
        calls = []
2925
3773
        def hook(*args):
2926
3774
            calls.append(args)
2927
3775
        config.ConfigHooks.install_named_hook('get', hook, None)
2928
3776
        self.assertLength(0, calls)
2929
 
        value = conf.get('foo')
 
3777
        value = self.conf.get('foo')
2930
3778
        self.assertEquals('bar', value)
2931
3779
        self.assertLength(1, calls)
2932
 
        self.assertEquals((conf, 'foo', 'bar'), calls[0])
 
3780
        self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
 
3781
 
 
3782
 
 
3783
class TestStackGetWithConverter(tests.TestCase):
 
3784
 
 
3785
    def setUp(self):
 
3786
        super(TestStackGetWithConverter, self).setUp()
 
3787
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
 
3788
        self.registry = config.option_registry
 
3789
 
 
3790
    def get_conf(self, content=None):
 
3791
        return config.MemoryStack(content)
 
3792
 
 
3793
    def register_bool_option(self, name, default=None, default_from_env=None):
 
3794
        b = config.Option(name, help='A boolean.',
 
3795
                          default=default, default_from_env=default_from_env,
 
3796
                          from_unicode=config.bool_from_store)
 
3797
        self.registry.register(b)
 
3798
 
 
3799
    def test_get_default_bool_None(self):
 
3800
        self.register_bool_option('foo')
 
3801
        conf = self.get_conf('')
 
3802
        self.assertEquals(None, conf.get('foo'))
 
3803
 
 
3804
    def test_get_default_bool_True(self):
 
3805
        self.register_bool_option('foo', u'True')
 
3806
        conf = self.get_conf('')
 
3807
        self.assertEquals(True, conf.get('foo'))
 
3808
 
 
3809
    def test_get_default_bool_False(self):
 
3810
        self.register_bool_option('foo', False)
 
3811
        conf = self.get_conf('')
 
3812
        self.assertEquals(False, conf.get('foo'))
 
3813
 
 
3814
    def test_get_default_bool_False_as_string(self):
 
3815
        self.register_bool_option('foo', u'False')
 
3816
        conf = self.get_conf('')
 
3817
        self.assertEquals(False, conf.get('foo'))
 
3818
 
 
3819
    def test_get_default_bool_from_env_converted(self):
 
3820
        self.register_bool_option('foo', u'True', default_from_env=['FOO'])
 
3821
        self.overrideEnv('FOO', 'False')
 
3822
        conf = self.get_conf('')
 
3823
        self.assertEquals(False, conf.get('foo'))
 
3824
 
 
3825
    def test_get_default_bool_when_conversion_fails(self):
 
3826
        self.register_bool_option('foo', default='True')
 
3827
        conf = self.get_conf('foo=invalid boolean')
 
3828
        self.assertEquals(True, conf.get('foo'))
 
3829
 
 
3830
    def register_integer_option(self, name,
 
3831
                                default=None, default_from_env=None):
 
3832
        i = config.Option(name, help='An integer.',
 
3833
                          default=default, default_from_env=default_from_env,
 
3834
                          from_unicode=config.int_from_store)
 
3835
        self.registry.register(i)
 
3836
 
 
3837
    def test_get_default_integer_None(self):
 
3838
        self.register_integer_option('foo')
 
3839
        conf = self.get_conf('')
 
3840
        self.assertEquals(None, conf.get('foo'))
 
3841
 
 
3842
    def test_get_default_integer(self):
 
3843
        self.register_integer_option('foo', 42)
 
3844
        conf = self.get_conf('')
 
3845
        self.assertEquals(42, conf.get('foo'))
 
3846
 
 
3847
    def test_get_default_integer_as_string(self):
 
3848
        self.register_integer_option('foo', u'42')
 
3849
        conf = self.get_conf('')
 
3850
        self.assertEquals(42, conf.get('foo'))
 
3851
 
 
3852
    def test_get_default_integer_from_env(self):
 
3853
        self.register_integer_option('foo', default_from_env=['FOO'])
 
3854
        self.overrideEnv('FOO', '18')
 
3855
        conf = self.get_conf('')
 
3856
        self.assertEquals(18, conf.get('foo'))
 
3857
 
 
3858
    def test_get_default_integer_when_conversion_fails(self):
 
3859
        self.register_integer_option('foo', default='12')
 
3860
        conf = self.get_conf('foo=invalid integer')
 
3861
        self.assertEquals(12, conf.get('foo'))
 
3862
 
 
3863
    def register_list_option(self, name, default=None, default_from_env=None):
 
3864
        l = config.ListOption(name, help='A list.', default=default,
 
3865
                              default_from_env=default_from_env)
 
3866
        self.registry.register(l)
 
3867
 
 
3868
    def test_get_default_list_None(self):
 
3869
        self.register_list_option('foo')
 
3870
        conf = self.get_conf('')
 
3871
        self.assertEquals(None, conf.get('foo'))
 
3872
 
 
3873
    def test_get_default_list_empty(self):
 
3874
        self.register_list_option('foo', '')
 
3875
        conf = self.get_conf('')
 
3876
        self.assertEquals([], conf.get('foo'))
 
3877
 
 
3878
    def test_get_default_list_from_env(self):
 
3879
        self.register_list_option('foo', default_from_env=['FOO'])
 
3880
        self.overrideEnv('FOO', '')
 
3881
        conf = self.get_conf('')
 
3882
        self.assertEquals([], conf.get('foo'))
 
3883
 
 
3884
    def test_get_with_list_converter_no_item(self):
 
3885
        self.register_list_option('foo', None)
 
3886
        conf = self.get_conf('foo=,')
 
3887
        self.assertEquals([], conf.get('foo'))
 
3888
 
 
3889
    def test_get_with_list_converter_many_items(self):
 
3890
        self.register_list_option('foo', None)
 
3891
        conf = self.get_conf('foo=m,o,r,e')
 
3892
        self.assertEquals(['m', 'o', 'r', 'e'], conf.get('foo'))
 
3893
 
 
3894
    def test_get_with_list_converter_embedded_spaces_many_items(self):
 
3895
        self.register_list_option('foo', None)
 
3896
        conf = self.get_conf('foo=" bar", "baz "')
 
3897
        self.assertEquals([' bar', 'baz '], conf.get('foo'))
 
3898
 
 
3899
    def test_get_with_list_converter_stripped_spaces_many_items(self):
 
3900
        self.register_list_option('foo', None)
 
3901
        conf = self.get_conf('foo= bar ,  baz ')
 
3902
        self.assertEquals(['bar', 'baz'], conf.get('foo'))
 
3903
 
 
3904
 
 
3905
class TestIterOptionRefs(tests.TestCase):
 
3906
    """iter_option_refs is a bit unusual, document some cases."""
 
3907
 
 
3908
    def assertRefs(self, expected, string):
 
3909
        self.assertEquals(expected, list(config.iter_option_refs(string)))
 
3910
 
 
3911
    def test_empty(self):
 
3912
        self.assertRefs([(False, '')], '')
 
3913
 
 
3914
    def test_no_refs(self):
 
3915
        self.assertRefs([(False, 'foo bar')], 'foo bar')
 
3916
 
 
3917
    def test_single_ref(self):
 
3918
        self.assertRefs([(False, ''), (True, '{foo}'), (False, '')], '{foo}')
 
3919
 
 
3920
    def test_broken_ref(self):
 
3921
        self.assertRefs([(False, '{foo')], '{foo')
 
3922
 
 
3923
    def test_embedded_ref(self):
 
3924
        self.assertRefs([(False, '{'), (True, '{foo}'), (False, '}')],
 
3925
                        '{{foo}}')
 
3926
 
 
3927
    def test_two_refs(self):
 
3928
        self.assertRefs([(False, ''), (True, '{foo}'),
 
3929
                         (False, ''), (True, '{bar}'),
 
3930
                         (False, ''),],
 
3931
                        '{foo}{bar}')
 
3932
 
 
3933
    def test_newline_in_refs_are_not_matched(self):
 
3934
        self.assertRefs([(False, '{\nxx}{xx\n}{{\n}}')], '{\nxx}{xx\n}{{\n}}')
 
3935
 
 
3936
 
 
3937
class TestStackExpandOptions(tests.TestCaseWithTransport):
 
3938
 
 
3939
    def setUp(self):
 
3940
        super(TestStackExpandOptions, self).setUp()
 
3941
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
 
3942
        self.registry = config.option_registry
 
3943
        store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
 
3944
        self.conf = config.Stack([store.get_sections], store)
 
3945
 
 
3946
    def assertExpansion(self, expected, string, env=None):
 
3947
        self.assertEquals(expected, self.conf.expand_options(string, env))
 
3948
 
 
3949
    def test_no_expansion(self):
 
3950
        self.assertExpansion('foo', 'foo')
 
3951
 
 
3952
    def test_expand_default_value(self):
 
3953
        self.conf.store._load_from_string('bar=baz')
 
3954
        self.registry.register(config.Option('foo', default=u'{bar}'))
 
3955
        self.assertEquals('baz', self.conf.get('foo', expand=True))
 
3956
 
 
3957
    def test_expand_default_from_env(self):
 
3958
        self.conf.store._load_from_string('bar=baz')
 
3959
        self.registry.register(config.Option('foo', default_from_env=['FOO']))
 
3960
        self.overrideEnv('FOO', '{bar}')
 
3961
        self.assertEquals('baz', self.conf.get('foo', expand=True))
 
3962
 
 
3963
    def test_expand_default_on_failed_conversion(self):
 
3964
        self.conf.store._load_from_string('baz=bogus\nbar=42\nfoo={baz}')
 
3965
        self.registry.register(
 
3966
            config.Option('foo', default=u'{bar}',
 
3967
                          from_unicode=config.int_from_store))
 
3968
        self.assertEquals(42, self.conf.get('foo', expand=True))
 
3969
 
 
3970
    def test_env_adding_options(self):
 
3971
        self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
 
3972
 
 
3973
    def test_env_overriding_options(self):
 
3974
        self.conf.store._load_from_string('foo=baz')
 
3975
        self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
 
3976
 
 
3977
    def test_simple_ref(self):
 
3978
        self.conf.store._load_from_string('foo=xxx')
 
3979
        self.assertExpansion('xxx', '{foo}')
 
3980
 
 
3981
    def test_unknown_ref(self):
 
3982
        self.assertRaises(errors.ExpandingUnknownOption,
 
3983
                          self.conf.expand_options, '{foo}')
 
3984
 
 
3985
    def test_illegal_def_is_ignored(self):
 
3986
        self.assertExpansion('{1,2}', '{1,2}')
 
3987
        self.assertExpansion('{ }', '{ }')
 
3988
        self.assertExpansion('${Foo,f}', '${Foo,f}')
 
3989
 
 
3990
    def test_indirect_ref(self):
 
3991
        self.conf.store._load_from_string('''
 
3992
foo=xxx
 
3993
bar={foo}
 
3994
''')
 
3995
        self.assertExpansion('xxx', '{bar}')
 
3996
 
 
3997
    def test_embedded_ref(self):
 
3998
        self.conf.store._load_from_string('''
 
3999
foo=xxx
 
4000
bar=foo
 
4001
''')
 
4002
        self.assertExpansion('xxx', '{{bar}}')
 
4003
 
 
4004
    def test_simple_loop(self):
 
4005
        self.conf.store._load_from_string('foo={foo}')
 
4006
        self.assertRaises(errors.OptionExpansionLoop,
 
4007
                          self.conf.expand_options, '{foo}')
 
4008
 
 
4009
    def test_indirect_loop(self):
 
4010
        self.conf.store._load_from_string('''
 
4011
foo={bar}
 
4012
bar={baz}
 
4013
baz={foo}''')
 
4014
        e = self.assertRaises(errors.OptionExpansionLoop,
 
4015
                              self.conf.expand_options, '{foo}')
 
4016
        self.assertEquals('foo->bar->baz', e.refs)
 
4017
        self.assertEquals('{foo}', e.string)
 
4018
 
 
4019
    def test_list(self):
 
4020
        self.conf.store._load_from_string('''
 
4021
foo=start
 
4022
bar=middle
 
4023
baz=end
 
4024
list={foo},{bar},{baz}
 
4025
''')
 
4026
        self.registry.register(
 
4027
            config.ListOption('list'))
 
4028
        self.assertEquals(['start', 'middle', 'end'],
 
4029
                           self.conf.get('list', expand=True))
 
4030
 
 
4031
    def test_cascading_list(self):
 
4032
        self.conf.store._load_from_string('''
 
4033
foo=start,{bar}
 
4034
bar=middle,{baz}
 
4035
baz=end
 
4036
list={foo}
 
4037
''')
 
4038
        self.registry.register(config.ListOption('list'))
 
4039
        # Register an intermediate option as a list to ensure no conversion
 
4040
        # happen while expanding. Conversion should only occur for the original
 
4041
        # option ('list' here).
 
4042
        self.registry.register(config.ListOption('baz'))
 
4043
        self.assertEquals(['start', 'middle', 'end'],
 
4044
                           self.conf.get('list', expand=True))
 
4045
 
 
4046
    def test_pathologically_hidden_list(self):
 
4047
        self.conf.store._load_from_string('''
 
4048
foo=bin
 
4049
bar=go
 
4050
start={foo
 
4051
middle=},{
 
4052
end=bar}
 
4053
hidden={start}{middle}{end}
 
4054
''')
 
4055
        # What matters is what the registration says, the conversion happens
 
4056
        # only after all expansions have been performed
 
4057
        self.registry.register(config.ListOption('hidden'))
 
4058
        self.assertEquals(['bin', 'go'],
 
4059
                          self.conf.get('hidden', expand=True))
 
4060
 
 
4061
 
 
4062
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
 
4063
 
 
4064
    def setUp(self):
 
4065
        super(TestStackCrossSectionsExpand, self).setUp()
 
4066
 
 
4067
    def get_config(self, location, string):
 
4068
        if string is None:
 
4069
            string = ''
 
4070
        # Since we don't save the config we won't strictly require to inherit
 
4071
        # from TestCaseInTempDir, but an error occurs so quickly...
 
4072
        c = config.LocationStack(location)
 
4073
        c.store._load_from_string(string)
 
4074
        return c
 
4075
 
 
4076
    def test_dont_cross_unrelated_section(self):
 
4077
        c = self.get_config('/another/branch/path','''
 
4078
[/one/branch/path]
 
4079
foo = hello
 
4080
bar = {foo}/2
 
4081
 
 
4082
[/another/branch/path]
 
4083
bar = {foo}/2
 
4084
''')
 
4085
        self.assertRaises(errors.ExpandingUnknownOption,
 
4086
                          c.get, 'bar', expand=True)
 
4087
 
 
4088
    def test_cross_related_sections(self):
 
4089
        c = self.get_config('/project/branch/path','''
 
4090
[/project]
 
4091
foo = qu
 
4092
 
 
4093
[/project/branch/path]
 
4094
bar = {foo}ux
 
4095
''')
 
4096
        self.assertEquals('quux', c.get('bar', expand=True))
 
4097
 
 
4098
 
 
4099
class TestStackCrossStoresExpand(tests.TestCaseWithTransport):
 
4100
 
 
4101
    def test_cross_global_locations(self):
 
4102
        l_store = config.LocationStore()
 
4103
        l_store._load_from_string('''
 
4104
[/branch]
 
4105
lfoo = loc-foo
 
4106
lbar = {gbar}
 
4107
''')
 
4108
        l_store.save()
 
4109
        g_store = config.GlobalStore()
 
4110
        g_store._load_from_string('''
 
4111
[DEFAULT]
 
4112
gfoo = {lfoo}
 
4113
gbar = glob-bar
 
4114
''')
 
4115
        g_store.save()
 
4116
        stack = config.LocationStack('/branch')
 
4117
        self.assertEquals('glob-bar', stack.get('lbar', expand=True))
 
4118
        self.assertEquals('loc-foo', stack.get('gfoo', expand=True))
 
4119
 
 
4120
 
 
4121
class TestStackExpandSectionLocals(tests.TestCaseWithTransport):
 
4122
 
 
4123
    def test_expand_locals_empty(self):
 
4124
        l_store = config.LocationStore()
 
4125
        l_store._load_from_string('''
 
4126
[/home/user/project]
 
4127
base = {basename}
 
4128
rel = {relpath}
 
4129
''')
 
4130
        l_store.save()
 
4131
        stack = config.LocationStack('/home/user/project/')
 
4132
        self.assertEquals('', stack.get('base', expand=True))
 
4133
        self.assertEquals('', stack.get('rel', expand=True))
 
4134
 
 
4135
    def test_expand_basename_locally(self):
 
4136
        l_store = config.LocationStore()
 
4137
        l_store._load_from_string('''
 
4138
[/home/user/project]
 
4139
bfoo = {basename}
 
4140
''')
 
4141
        l_store.save()
 
4142
        stack = config.LocationStack('/home/user/project/branch')
 
4143
        self.assertEquals('branch', stack.get('bfoo', expand=True))
 
4144
 
 
4145
    def test_expand_basename_locally_longer_path(self):
 
4146
        l_store = config.LocationStore()
 
4147
        l_store._load_from_string('''
 
4148
[/home/user]
 
4149
bfoo = {basename}
 
4150
''')
 
4151
        l_store.save()
 
4152
        stack = config.LocationStack('/home/user/project/dir/branch')
 
4153
        self.assertEquals('branch', stack.get('bfoo', expand=True))
 
4154
 
 
4155
    def test_expand_relpath_locally(self):
 
4156
        l_store = config.LocationStore()
 
4157
        l_store._load_from_string('''
 
4158
[/home/user/project]
 
4159
lfoo = loc-foo/{relpath}
 
4160
''')
 
4161
        l_store.save()
 
4162
        stack = config.LocationStack('/home/user/project/branch')
 
4163
        self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
 
4164
 
 
4165
    def test_expand_relpath_unknonw_in_global(self):
 
4166
        g_store = config.GlobalStore()
 
4167
        g_store._load_from_string('''
 
4168
[DEFAULT]
 
4169
gfoo = {relpath}
 
4170
''')
 
4171
        g_store.save()
 
4172
        stack = config.LocationStack('/home/user/project/branch')
 
4173
        self.assertRaises(errors.ExpandingUnknownOption,
 
4174
                          stack.get, 'gfoo', expand=True)
 
4175
 
 
4176
    def test_expand_local_option_locally(self):
 
4177
        l_store = config.LocationStore()
 
4178
        l_store._load_from_string('''
 
4179
[/home/user/project]
 
4180
lfoo = loc-foo/{relpath}
 
4181
lbar = {gbar}
 
4182
''')
 
4183
        l_store.save()
 
4184
        g_store = config.GlobalStore()
 
4185
        g_store._load_from_string('''
 
4186
[DEFAULT]
 
4187
gfoo = {lfoo}
 
4188
gbar = glob-bar
 
4189
''')
 
4190
        g_store.save()
 
4191
        stack = config.LocationStack('/home/user/project/branch')
 
4192
        self.assertEquals('glob-bar', stack.get('lbar', expand=True))
 
4193
        self.assertEquals('loc-foo/branch', stack.get('gfoo', expand=True))
 
4194
 
 
4195
    def test_locals_dont_leak(self):
 
4196
        """Make sure we chose the right local in presence of several sections.
 
4197
        """
 
4198
        l_store = config.LocationStore()
 
4199
        l_store._load_from_string('''
 
4200
[/home/user]
 
4201
lfoo = loc-foo/{relpath}
 
4202
[/home/user/project]
 
4203
lfoo = loc-foo/{relpath}
 
4204
''')
 
4205
        l_store.save()
 
4206
        stack = config.LocationStack('/home/user/project/branch')
 
4207
        self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
 
4208
        stack = config.LocationStack('/home/user/bar/baz')
 
4209
        self.assertEquals('loc-foo/bar/baz', stack.get('lfoo', expand=True))
 
4210
 
2933
4211
 
2934
4212
 
2935
4213
class TestStackSet(TestStackWithTransport):
2936
4214
 
2937
4215
    def test_simple_set(self):
2938
4216
        conf = self.get_stack(self)
2939
 
        conf.store._load_from_string('foo=bar')
2940
 
        self.assertEquals('bar', conf.get('foo'))
 
4217
        self.assertEquals(None, conf.get('foo'))
2941
4218
        conf.set('foo', 'baz')
2942
4219
        # Did we get it back ?
2943
4220
        self.assertEquals('baz', conf.get('foo'))
2963
4240
 
2964
4241
    def test_remove_existing(self):
2965
4242
        conf = self.get_stack(self)
2966
 
        conf.store._load_from_string('foo=bar')
 
4243
        conf.set('foo', 'bar')
2967
4244
        self.assertEquals('bar', conf.get('foo'))
2968
4245
        conf.remove('foo')
2969
4246
        # Did we get it back ?
2980
4257
        config.ConfigHooks.install_named_hook('remove', hook, None)
2981
4258
        self.assertLength(0, calls)
2982
4259
        conf = self.get_stack(self)
2983
 
        conf.store._load_from_string('foo=bar')
 
4260
        conf.set('foo', 'bar')
2984
4261
        conf.remove('foo')
2985
4262
        self.assertLength(1, calls)
2986
4263
        self.assertEquals((conf, 'foo'), calls[0])
3087
4364
        """
3088
4365
        sections = list(conf._get_sections(name))
3089
4366
        self.assertLength(len(expected), sections)
3090
 
        self.assertEqual(expected, [name for name, _, _ in sections])
 
4367
        self.assertEqual(expected, [n for n, _, _ in sections])
3091
4368
 
3092
4369
    def test_bazaar_default_section(self):
3093
4370
        self.assertSectionNames(['DEFAULT'], self.bazaar_config)
3137
4414
        self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
3138
4415
 
3139
4416
 
 
4417
class TestSharedStores(tests.TestCaseInTempDir):
 
4418
 
 
4419
    def test_bazaar_conf_shared(self):
 
4420
        g1 = config.GlobalStack()
 
4421
        g2 = config.GlobalStack()
 
4422
        # The two stacks share the same store
 
4423
        self.assertIs(g1.store, g2.store)
 
4424
 
 
4425
 
3140
4426
class TestAuthenticationConfigFile(tests.TestCase):
3141
4427
    """Test the authentication.conf file matching"""
3142
4428
 
3161
4447
        conf = config.AuthenticationConfig(_file=StringIO(
3162
4448
                'foo = bar\xff'))
3163
4449
        self.assertRaises(errors.ConfigContentError, conf._get_config)
3164
 
        
 
4450
 
3165
4451
    def test_missing_auth_section_header(self):
3166
4452
        conf = config.AuthenticationConfig(_file=StringIO('foo = bar'))
3167
4453
        self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
3357
4643
                                           port=99, path='/foo',
3358
4644
                                           realm='realm')
3359
4645
        CREDENTIALS = {'name': 'name', 'user': 'user', 'password': 'password',
3360
 
                       'verify_certificates': False, 'scheme': 'scheme', 
3361
 
                       'host': 'host', 'port': 99, 'path': '/foo', 
 
4646
                       'verify_certificates': False, 'scheme': 'scheme',
 
4647
                       'host': 'host', 'port': 99, 'path': '/foo',
3362
4648
                       'realm': 'realm'}
3363
4649
        self.assertEqual(CREDENTIALS, credentials)
3364
4650
        credentials_from_disk = config.AuthenticationConfig().get_credentials(
3372
4658
        self.assertIs(None, conf._get_config().get('name'))
3373
4659
        credentials = conf.get_credentials(host='host', scheme='scheme')
3374
4660
        CREDENTIALS = {'name': 'name2', 'user': 'user2', 'password':
3375
 
                       'password', 'verify_certificates': True, 
3376
 
                       'scheme': 'scheme', 'host': 'host', 'port': None, 
 
4661
                       'password', 'verify_certificates': True,
 
4662
                       'scheme': 'scheme', 'host': 'host', 'port': None,
3377
4663
                       'path': None, 'realm': None}
3378
4664
        self.assertEqual(CREDENTIALS, credentials)
3379
4665
 
3631
4917
        self.assertEquals('secret', decoded)
3632
4918
 
3633
4919
 
 
4920
class TestBase64CredentialStore(tests.TestCase):
 
4921
 
 
4922
    def test_decode_password(self):
 
4923
        r = config.credential_store_registry
 
4924
        plain_text = r.get_credential_store('base64')
 
4925
        decoded = plain_text.decode_password(dict(password='c2VjcmV0'))
 
4926
        self.assertEquals('secret', decoded)
 
4927
 
 
4928
 
3634
4929
# FIXME: Once we have a way to declare authentication to all test servers, we
3635
4930
# can implement generic tests.
3636
4931
# test_user_password_in_url
3648
4943
 
3649
4944
    def test_auto_user_id(self):
3650
4945
        """Automatic inference of user name.
3651
 
        
 
4946
 
3652
4947
        This is a bit hard to test in an isolated way, because it depends on
3653
4948
        system functions that go direct to /etc or perhaps somewhere else.
3654
4949
        But it's reasonable to say that on Unix, with an /etc/mailname, we ought
3664
4959
        else:
3665
4960
            self.assertEquals((None, None), (realname, address))
3666
4961
 
 
4962
 
 
4963
class TestDefaultMailDomain(tests.TestCaseInTempDir):
 
4964
    """Test retrieving default domain from mailname file"""
 
4965
 
 
4966
    def test_default_mail_domain_simple(self):
 
4967
        f = file('simple', 'w')
 
4968
        try:
 
4969
            f.write("domainname.com\n")
 
4970
        finally:
 
4971
            f.close()
 
4972
        r = config._get_default_mail_domain('simple')
 
4973
        self.assertEquals('domainname.com', r)
 
4974
 
 
4975
    def test_default_mail_domain_no_eol(self):
 
4976
        f = file('no_eol', 'w')
 
4977
        try:
 
4978
            f.write("domainname.com")
 
4979
        finally:
 
4980
            f.close()
 
4981
        r = config._get_default_mail_domain('no_eol')
 
4982
        self.assertEquals('domainname.com', r)
 
4983
 
 
4984
    def test_default_mail_domain_multiple_lines(self):
 
4985
        f = file('multiple_lines', 'w')
 
4986
        try:
 
4987
            f.write("domainname.com\nsome other text\n")
 
4988
        finally:
 
4989
            f.close()
 
4990
        r = config._get_default_mail_domain('multiple_lines')
 
4991
        self.assertEquals('domainname.com', r)
 
4992
 
 
4993
 
 
4994
class EmailOptionTests(tests.TestCase):
 
4995
 
 
4996
    def test_default_email_uses_BZR_EMAIL(self):
 
4997
        conf = config.MemoryStack('email=jelmer@debian.org')
 
4998
        # BZR_EMAIL takes precedence over EMAIL
 
4999
        self.overrideEnv('BZR_EMAIL', 'jelmer@samba.org')
 
5000
        self.overrideEnv('EMAIL', 'jelmer@apache.org')
 
5001
        self.assertEquals('jelmer@samba.org', conf.get('email'))
 
5002
 
 
5003
    def test_default_email_uses_EMAIL(self):
 
5004
        conf = config.MemoryStack('')
 
5005
        self.overrideEnv('BZR_EMAIL', None)
 
5006
        self.overrideEnv('EMAIL', 'jelmer@apache.org')
 
5007
        self.assertEquals('jelmer@apache.org', conf.get('email'))
 
5008
 
 
5009
    def test_BZR_EMAIL_overrides(self):
 
5010
        conf = config.MemoryStack('email=jelmer@debian.org')
 
5011
        self.overrideEnv('BZR_EMAIL', 'jelmer@apache.org')
 
5012
        self.assertEquals('jelmer@apache.org', conf.get('email'))
 
5013
        self.overrideEnv('BZR_EMAIL', None)
 
5014
        self.overrideEnv('EMAIL', 'jelmer@samba.org')
 
5015
        self.assertEquals('jelmer@debian.org', conf.get('email'))
 
5016
 
 
5017
 
 
5018
class MailClientOptionTests(tests.TestCase):
 
5019
 
 
5020
    def test_default(self):
 
5021
        conf = config.MemoryStack('')
 
5022
        client = conf.get('mail_client')
 
5023
        self.assertIs(client, mail_client.DefaultMail)
 
5024
 
 
5025
    def test_evolution(self):
 
5026
        conf = config.MemoryStack('mail_client=evolution')
 
5027
        client = conf.get('mail_client')
 
5028
        self.assertIs(client, mail_client.Evolution)
 
5029
 
 
5030
    def test_kmail(self):
 
5031
        conf = config.MemoryStack('mail_client=kmail')
 
5032
        client = conf.get('mail_client')
 
5033
        self.assertIs(client, mail_client.KMail)
 
5034
 
 
5035
    def test_mutt(self):
 
5036
        conf = config.MemoryStack('mail_client=mutt')
 
5037
        client = conf.get('mail_client')
 
5038
        self.assertIs(client, mail_client.Mutt)
 
5039
 
 
5040
    def test_thunderbird(self):
 
5041
        conf = config.MemoryStack('mail_client=thunderbird')
 
5042
        client = conf.get('mail_client')
 
5043
        self.assertIs(client, mail_client.Thunderbird)
 
5044
 
 
5045
    def test_explicit_default(self):
 
5046
        conf = config.MemoryStack('mail_client=default')
 
5047
        client = conf.get('mail_client')
 
5048
        self.assertIs(client, mail_client.DefaultMail)
 
5049
 
 
5050
    def test_editor(self):
 
5051
        conf = config.MemoryStack('mail_client=editor')
 
5052
        client = conf.get('mail_client')
 
5053
        self.assertIs(client, mail_client.Editor)
 
5054
 
 
5055
    def test_mapi(self):
 
5056
        conf = config.MemoryStack('mail_client=mapi')
 
5057
        client = conf.get('mail_client')
 
5058
        self.assertIs(client, mail_client.MAPIClient)
 
5059
 
 
5060
    def test_xdg_email(self):
 
5061
        conf = config.MemoryStack('mail_client=xdg-email')
 
5062
        client = conf.get('mail_client')
 
5063
        self.assertIs(client, mail_client.XDGEmail)
 
5064
 
 
5065
    def test_unknown(self):
 
5066
        conf = config.MemoryStack('mail_client=firebird')
 
5067
        self.assertRaises(errors.ConfigOptionValueError, conf.get,
 
5068
                'mail_client')