/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to breezy/tests/test_config.py

  • Committer: Jelmer Vernooij
  • Date: 2017-06-08 23:30:31 UTC
  • mto: This revision was merged to the branch mainline in revision 6690.
  • Revision ID: jelmer@jelmer.uk-20170608233031-3qavls2o7a1pqllj
Update imports.

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
"""Tests for finding and reading the bzr config file[s]."""
18
18
 
19
19
from textwrap import dedent
20
 
from io import BytesIO
21
20
import os
22
21
import sys
23
22
import threading
28
27
from .. import (
29
28
    branch,
30
29
    config,
31
 
    bedding,
32
30
    controldir,
33
31
    diff,
34
32
    errors,
35
 
    lock,
36
33
    osutils,
37
34
    mail_client,
38
35
    ui,
39
36
    urlutils,
40
37
    registry as _mod_registry,
 
38
    remote,
41
39
    tests,
42
40
    trace,
43
41
    )
44
 
from ..bzr import (
45
 
    remote,
 
42
from ..sixish import (
 
43
    BytesIO,
46
44
    )
47
45
from ..transport import remote as transport_remote
48
46
from . import (
61
59
        ('locations',
62
60
         {'config_class': config.LocationConfig,
63
61
          'config_args': ['.'],
64
 
          'config_section': '.'}), ]
 
62
          'config_section': '.'}),]
65
63
 
66
64
 
67
65
load_tests = scenarios.load_tests_apply_scenarios
71
69
    'configobj', lambda test: config.TransportIniFileStore(
72
70
        test.get_transport(), 'configobj.conf'))
73
71
config.test_store_builder_registry.register(
74
 
    'breezy', lambda test: config.GlobalStore())
 
72
    'bazaar', lambda test: config.GlobalStore())
75
73
config.test_store_builder_registry.register(
76
74
    'location', lambda test: config.LocationStore())
77
75
 
110
108
    build_backing_branch(test, 'branch')
111
109
    b = branch.Branch.open('branch')
112
110
    return config.BranchStore(b)
113
 
 
114
 
 
115
111
config.test_store_builder_registry.register('branch', build_branch_store)
116
112
 
117
113
 
119
115
    build_backing_branch(test, 'branch')
120
116
    b = controldir.ControlDir.open('branch')
121
117
    return config.ControlStore(b)
122
 
 
123
 
 
124
118
config.test_store_builder_registry.register('control', build_control_store)
125
119
 
126
120
 
132
126
    build_backing_branch(test, 'branch', transport_class, server_class)
133
127
    b = branch.Branch.open(test.get_url('branch'))
134
128
    return config.BranchStore(b)
135
 
 
136
 
 
137
129
config.test_store_builder_registry.register('remote_branch',
138
130
                                            build_remote_branch_store)
139
131
 
140
132
 
141
133
config.test_stack_builder_registry.register(
142
 
    'breezy', lambda test: config.GlobalStack())
 
134
    'bazaar', lambda test: config.GlobalStack())
143
135
config.test_stack_builder_registry.register(
144
136
    'location', lambda test: config.LocationStack('.'))
145
137
 
148
140
    build_backing_branch(test, 'branch')
149
141
    b = branch.Branch.open('branch')
150
142
    return config.BranchStack(b)
151
 
 
152
 
 
153
143
config.test_stack_builder_registry.register('branch', build_branch_stack)
154
144
 
155
145
 
161
151
    build_backing_branch(test, 'branch', transport_class, server_class)
162
152
    b = branch.Branch.open(test.get_url('branch'))
163
153
    return config.BranchOnlyStack(b)
164
 
 
165
 
 
166
154
config.test_stack_builder_registry.register('branch_only',
167
155
                                            build_branch_only_stack)
168
156
 
169
 
 
170
157
def build_remote_control_stack(test):
171
158
    # There is only one permutation (but we won't be able to handle more with
172
159
    # this design anyway)
176
163
    # creating a dedicated helper to create only the bzrdir
177
164
    build_backing_branch(test, 'branch', transport_class, server_class)
178
165
    b = branch.Branch.open(test.get_url('branch'))
179
 
    return config.RemoteControlStack(b.controldir)
180
 
 
181
 
 
 
166
    return config.RemoteControlStack(b.bzrdir)
182
167
config.test_stack_builder_registry.register('remote_control',
183
168
                                            build_remote_control_stack)
184
169
 
185
170
 
186
 
sample_long_alias = "log -r-15..-1 --line"
 
171
sample_long_alias="log -r-15..-1 --line"
187
172
sample_config_text = u"""
188
173
[DEFAULT]
189
174
email=Erik B\u00e5gfors <erik@bagfors.nu>
190
175
editor=vim
191
 
change_editor=vimdiff -of {new_path} {old_path}
 
176
change_editor=vimdiff -of @new_path @old_path
 
177
gpg_signing_command=gnome-gpg
192
178
gpg_signing_key=DD4D5088
193
179
log_format=short
194
180
validate_signatures_in_log=true
200
186
bzr.default_mergetool=sometool
201
187
[ALIASES]
202
188
h=help
203
 
ll=""".encode('utf-8') + sample_long_alias.encode('utf-8') + b"\n"
204
 
 
205
 
 
206
 
sample_always_signatures = b"""
 
189
ll=""" + sample_long_alias + "\n"
 
190
 
 
191
 
 
192
sample_always_signatures = """
207
193
[DEFAULT]
208
194
check_signatures=ignore
209
195
create_signatures=always
210
196
"""
211
197
 
212
 
sample_ignore_signatures = b"""
 
198
sample_ignore_signatures = """
213
199
[DEFAULT]
214
200
check_signatures=require
215
201
create_signatures=never
216
202
"""
217
203
 
218
 
sample_maybe_signatures = b"""
 
204
sample_maybe_signatures = """
219
205
[DEFAULT]
220
206
check_signatures=ignore
221
207
create_signatures=when-required
222
208
"""
223
209
 
224
 
sample_branches_text = b"""
 
210
sample_branches_text = """
225
211
[http://www.example.com]
226
212
# Top level policy
227
213
email=Robert Collins <robertc@example.org>
244
230
# test trailing / matching with no children
245
231
[/a/]
246
232
check_signatures=check-available
 
233
gpg_signing_command=false
247
234
gpg_signing_key=default
248
235
user_local_option=local
249
236
# test trailing / matching
266
253
 
267
254
    - locations_config : A LocationConfig for the associated branch
268
255
 
269
 
    - breezy_config: A GlobalConfig.
 
256
    - bazaar_config: A GlobalConfig.
270
257
 
271
258
    The tree and branch are created in a 'tree' subdirectory so the tests can
272
259
    still use the test directory to stay outside of the branch.
275
262
    test.tree = tree
276
263
    test.branch_config = config.BranchConfig(tree.branch)
277
264
    test.locations_config = config.LocationConfig(tree.basedir)
278
 
    test.breezy_config = config.GlobalConfig()
 
265
    test.bazaar_config = config.GlobalConfig()
279
266
 
280
267
 
281
268
def create_configs_with_file_option(test):
285
272
    configuration with a value which allows identifying the configuration file.
286
273
    """
287
274
    create_configs(test)
288
 
    test.breezy_config.set_user_option('file', 'breezy')
 
275
    test.bazaar_config.set_user_option('file', 'bazaar')
289
276
    test.locations_config.set_user_option('file', 'locations')
290
277
    test.branch_config.set_user_option('file', 'branch')
291
278
 
352
339
        return config.TransportConfig(self._transport, 'branch.conf')
353
340
 
354
341
    def lock_write(self):
355
 
        return lock.LogicalLockResult(self.unlock)
 
342
        pass
356
343
 
357
344
    def unlock(self):
358
345
        pass
392
379
        super(InstrumentedConfig, self).__init__()
393
380
        self._calls = []
394
381
        self._signatures = config.CHECK_NEVER
395
 
        self._change_editor = 'vimdiff -fo {new_path} {old_path}'
396
382
 
397
383
    def _get_user_id(self):
398
384
        self._calls.append('_get_user_id')
404
390
 
405
391
    def _get_change_editor(self):
406
392
        self._calls.append('_get_change_editor')
407
 
        return self._change_editor
408
 
 
409
 
 
410
 
bool_config = b"""[DEFAULT]
 
393
        return 'vimdiff -fo @new_path @old_path'
 
394
 
 
395
 
 
396
bool_config = """[DEFAULT]
411
397
active = true
412
398
inactive = false
413
399
[UPPERCASE]
435
421
        outfile = BytesIO()
436
422
        co.write(outfile=outfile)
437
423
        lines = outfile.getvalue().splitlines()
438
 
        self.assertEqual(lines, [b'test = "foo#bar"'])
 
424
        self.assertEqual(lines, ['test = "foo#bar"'])
439
425
        co2 = config.ConfigObj(lines)
440
426
        self.assertEqual(co2['test'], 'foo#bar')
441
427
 
464
450
        self.assertEqual(triple_quotes_value, co2['test'])
465
451
 
466
452
 
467
 
erroneous_config = b"""[section] # line 1
 
453
erroneous_config = """[section] # line 1
468
454
good=good # line 2
469
455
[section] # line 3
470
456
whocares=notme # line 4
512
498
        change_editor = my_config.get_change_editor('old_tree', 'new_tree')
513
499
        self.assertEqual(['_get_change_editor'], my_config._calls)
514
500
        self.assertIs(diff.DiffFromTool, change_editor.__class__)
515
 
        self.assertEqual(['vimdiff', '-fo', '{new_path}', '{old_path}'],
516
 
                         change_editor.command_template)
517
 
 
518
 
    def test_get_change_editor_implicit_args(self):
519
 
        # If there are no substitution variables, then assume the
520
 
        # old and new path are the last arguments.
521
 
        my_config = InstrumentedConfig()
522
 
        my_config._change_editor = 'vimdiff -o'
523
 
        change_editor = my_config.get_change_editor('old_tree', 'new_tree')
524
 
        self.assertEqual(['_get_change_editor'], my_config._calls)
525
 
        self.assertIs(diff.DiffFromTool, change_editor.__class__)
526
 
        self.assertEqual(['vimdiff', '-o', '{old_path}', '{new_path}'],
527
 
                         change_editor.command_template)
528
 
 
529
 
    def test_get_change_editor_old_style(self):
530
 
        # Test the old style format for the change_editor setting.
531
 
        my_config = InstrumentedConfig()
532
 
        my_config._change_editor = 'vimdiff -o @old_path @new_path'
533
 
        change_editor = my_config.get_change_editor('old_tree', 'new_tree')
534
 
        self.assertEqual(['_get_change_editor'], my_config._calls)
535
 
        self.assertIs(diff.DiffFromTool, change_editor.__class__)
536
 
        self.assertEqual(['vimdiff', '-o', '{old_path}', '{new_path}'],
537
 
                         change_editor.command_template)
 
501
        self.assertEqual(['vimdiff', '-fo', '@new_path', '@old_path'],
 
502
                         change_editor.command_template)
 
503
 
 
504
 
 
505
class TestConfigPath(tests.TestCase):
 
506
 
 
507
    def setUp(self):
 
508
        super(TestConfigPath, self).setUp()
 
509
        self.overrideEnv('HOME', '/home/bogus')
 
510
        self.overrideEnv('XDG_CACHE_HOME', '')
 
511
        if sys.platform == 'win32':
 
512
            self.overrideEnv(
 
513
                'BRZ_HOME',
 
514
                r'C:\Documents and Settings\bogus\Application Data')
 
515
            self.brz_home = \
 
516
                'C:/Documents and Settings/bogus/Application Data/breezy'
 
517
        else:
 
518
            self.brz_home = '/home/bogus/.config/breezy'
 
519
 
 
520
    def test_config_dir(self):
 
521
        self.assertEqual(config.config_dir(), self.brz_home)
 
522
 
 
523
    def test_config_dir_is_unicode(self):
 
524
        self.assertIsInstance(config.config_dir(), unicode)
 
525
 
 
526
    def test_config_filename(self):
 
527
        self.assertEqual(config.config_filename(),
 
528
                         self.brz_home + '/bazaar.conf')
 
529
 
 
530
    def test_locations_config_filename(self):
 
531
        self.assertEqual(config.locations_config_filename(),
 
532
                         self.brz_home + '/locations.conf')
 
533
 
 
534
    def test_authentication_config_filename(self):
 
535
        self.assertEqual(config.authentication_config_filename(),
 
536
                         self.brz_home + '/authentication.conf')
 
537
 
 
538
    def test_xdg_cache_dir(self):
 
539
        self.assertEqual(config.xdg_cache_dir(),
 
540
            '/home/bogus/.cache')
 
541
 
 
542
 
 
543
class TestXDGConfigDir(tests.TestCaseInTempDir):
 
544
    # must be in temp dir because config tests for the existence of the bazaar
 
545
    # subdirectory of $XDG_CONFIG_HOME
 
546
 
 
547
    def setUp(self):
 
548
        if sys.platform == 'win32':
 
549
            raise tests.TestNotApplicable(
 
550
                'XDG config dir not used on this platform')
 
551
        super(TestXDGConfigDir, self).setUp()
 
552
        self.overrideEnv('HOME', self.test_home_dir)
 
553
        # BRZ_HOME overrides everything we want to test so unset it.
 
554
        self.overrideEnv('BRZ_HOME', None)
 
555
 
 
556
    def test_xdg_config_dir_exists(self):
 
557
        """When ~/.config/bazaar exists, use it as the config dir."""
 
558
        newdir = osutils.pathjoin(self.test_home_dir, '.config', 'bazaar')
 
559
        os.makedirs(newdir)
 
560
        self.assertEqual(config.config_dir(), newdir)
 
561
 
 
562
    def test_xdg_config_home(self):
 
563
        """When XDG_CONFIG_HOME is set, use it."""
 
564
        xdgconfigdir = osutils.pathjoin(self.test_home_dir, 'xdgconfig')
 
565
        self.overrideEnv('XDG_CONFIG_HOME', xdgconfigdir)
 
566
        newdir = osutils.pathjoin(xdgconfigdir, 'bazaar')
 
567
        os.makedirs(newdir)
 
568
        self.assertEqual(config.config_dir(), newdir)
538
569
 
539
570
 
540
571
class TestIniConfig(tests.TestCaseInTempDir):
580
611
        self.assertRaises(AssertionError, conf._write_config_file)
581
612
 
582
613
    def test_saved_with_content(self):
583
 
        content = b'foo = bar\n'
 
614
        content = 'foo = bar\n'
584
615
        config.IniBasedConfig.from_string(content, file_name='./test.conf',
585
616
                                          save=True)
586
617
        self.assertFileEqual(content, 'test.conf')
598
629
 
599
630
    def get_config(self, string=None):
600
631
        if string is None:
601
 
            string = b''
 
632
            string = ''
602
633
        c = config.IniBasedConfig.from_string(string)
603
634
        return c
604
635
 
623
654
 
624
655
    def test_unknown_ref(self):
625
656
        c = self.get_config('')
626
 
        self.assertRaises(config.ExpandingUnknownOption,
 
657
        self.assertRaises(errors.ExpandingUnknownOption,
627
658
                          c.expand_options, '{foo}')
628
659
 
629
660
    def test_indirect_ref(self):
642
673
 
643
674
    def test_simple_loop(self):
644
675
        c = self.get_config('foo={foo}')
645
 
        self.assertRaises(config.OptionExpansionLoop, c.expand_options,
646
 
                          '{foo}')
 
676
        self.assertRaises(errors.OptionExpansionLoop, c.expand_options, '{foo}')
647
677
 
648
678
    def test_indirect_loop(self):
649
679
        c = self.get_config('''
650
680
foo={bar}
651
681
bar={baz}
652
682
baz={foo}''')
653
 
        e = self.assertRaises(config.OptionExpansionLoop,
 
683
        e = self.assertRaises(errors.OptionExpansionLoop,
654
684
                              c.expand_options, '{foo}')
655
685
        self.assertEqual('foo->bar->baz', e.refs)
656
686
        self.assertEqual('{foo}', e.string)
663
693
list={foo},{bar},{baz}
664
694
''')
665
695
        self.assertEqual(['start', 'middle', 'end'],
666
 
                         conf.get_user_option('list', expand=True))
 
696
                           conf.get_user_option('list', expand=True))
667
697
 
668
698
    def test_cascading_list(self):
669
699
        conf = self.get_config('''
673
703
list={foo}
674
704
''')
675
705
        self.assertEqual(['start', 'middle', 'end'],
676
 
                         conf.get_user_option('list', expand=True))
 
706
                           conf.get_user_option('list', expand=True))
677
707
 
678
708
    def test_pathological_hidden_list(self):
679
709
        conf = self.get_config('''
687
717
        # Nope, it's either a string or a list, and the list wins as soon as a
688
718
        # ',' appears, so the string concatenation never occur.
689
719
        self.assertEqual(['{foo', '}', '{', 'bar}'],
690
 
                         conf.get_user_option('hidden', expand=True))
 
720
                          conf.get_user_option('hidden', expand=True))
691
721
 
692
722
 
693
723
class TestLocationConfigOptionExpansion(tests.TestCaseInTempDir):
701
731
        return c
702
732
 
703
733
    def test_dont_cross_unrelated_section(self):
704
 
        c = self.get_config('/another/branch/path', '''
 
734
        c = self.get_config('/another/branch/path','''
705
735
[/one/branch/path]
706
736
foo = hello
707
737
bar = {foo}/2
709
739
[/another/branch/path]
710
740
bar = {foo}/2
711
741
''')
712
 
        self.assertRaises(config.ExpandingUnknownOption,
 
742
        self.assertRaises(errors.ExpandingUnknownOption,
713
743
                          c.get_user_option, 'bar', expand=True)
714
744
 
715
745
    def test_cross_related_sections(self):
716
 
        c = self.get_config('/project/branch/path', '''
 
746
        c = self.get_config('/project/branch/path','''
717
747
[/project]
718
748
foo = qu
719
749
 
805
835
        after_writing = threading.Event()
806
836
        writing_done = threading.Event()
807
837
        c1_orig = c1._write_config_file
808
 
 
809
838
        def c1_write_config_file():
810
839
            before_writing.set()
811
840
            c1_orig()
813
842
            # continue
814
843
            after_writing.wait()
815
844
        c1._write_config_file = c1_write_config_file
816
 
 
817
845
        def c1_set_option():
818
846
            c1.set_user_option('one', 'c1')
819
847
            writing_done.set()
835
863
        self.assertEqual('c2', c2.get_user_option('one'))
836
864
 
837
865
    def test_read_while_writing(self):
838
 
        c1 = self.config
839
 
        # We spawn a thread that will pause *during* the write
840
 
        ready_to_write = threading.Event()
841
 
        do_writing = threading.Event()
842
 
        writing_done = threading.Event()
843
 
        c1_orig = c1._write_config_file
844
 
 
845
 
        def c1_write_config_file():
846
 
            ready_to_write.set()
847
 
            # The lock is held. We wait for the main thread to decide when to
848
 
            # continue
849
 
            do_writing.wait()
850
 
            c1_orig()
851
 
            writing_done.set()
852
 
        c1._write_config_file = c1_write_config_file
853
 
 
854
 
        def c1_set_option():
855
 
            c1.set_user_option('one', 'c1')
856
 
        t1 = threading.Thread(target=c1_set_option)
857
 
        # Collect the thread after the test
858
 
        self.addCleanup(t1.join)
859
 
        # Be ready to unblock the thread if the test goes wrong
860
 
        self.addCleanup(do_writing.set)
861
 
        t1.start()
862
 
        # Ensure the thread is ready to write
863
 
        ready_to_write.wait()
864
 
        self.assertTrue(c1._lock.is_held)
865
 
        self.assertEqual('c1', c1.get_user_option('one'))
866
 
        # If we read during the write, we get the old value
867
 
        c2 = self.get_existing_config()
868
 
        self.assertEqual('1', c2.get_user_option('one'))
869
 
        # Let the writing occur and ensure it occurred
870
 
        do_writing.set()
871
 
        writing_done.wait()
872
 
        # Now we get the updated value
873
 
        c3 = self.get_existing_config()
874
 
        self.assertEqual('c1', c3.get_user_option('one'))
 
866
       c1 = self.config
 
867
       # We spawn a thread that will pause *during* the write
 
868
       ready_to_write = threading.Event()
 
869
       do_writing = threading.Event()
 
870
       writing_done = threading.Event()
 
871
       c1_orig = c1._write_config_file
 
872
       def c1_write_config_file():
 
873
           ready_to_write.set()
 
874
           # The lock is held. We wait for the main thread to decide when to
 
875
           # continue
 
876
           do_writing.wait()
 
877
           c1_orig()
 
878
           writing_done.set()
 
879
       c1._write_config_file = c1_write_config_file
 
880
       def c1_set_option():
 
881
           c1.set_user_option('one', 'c1')
 
882
       t1 = threading.Thread(target=c1_set_option)
 
883
       # Collect the thread after the test
 
884
       self.addCleanup(t1.join)
 
885
       # Be ready to unblock the thread if the test goes wrong
 
886
       self.addCleanup(do_writing.set)
 
887
       t1.start()
 
888
       # Ensure the thread is ready to write
 
889
       ready_to_write.wait()
 
890
       self.assertTrue(c1._lock.is_held)
 
891
       self.assertEqual('c1', c1.get_user_option('one'))
 
892
       # If we read during the write, we get the old value
 
893
       c2 = self.get_existing_config()
 
894
       self.assertEqual('1', c2.get_user_option('one'))
 
895
       # Let the writing occur and ensure it occurred
 
896
       do_writing.set()
 
897
       writing_done.wait()
 
898
       # Now we get the updated value
 
899
       c3 = self.get_existing_config()
 
900
       self.assertEqual('c1', c3.get_user_option('one'))
875
901
 
876
902
 
877
903
class TestGetUserOptionAs(TestIniConfig):
887
913
        self.assertEqual(True, get_bool('a_true_bool'))
888
914
        self.assertEqual(False, get_bool('a_false_bool'))
889
915
        warnings = []
890
 
 
891
916
        def warning(*args):
892
917
            warnings.append(args[0] % args[1:])
893
918
        self.overrideAttr(trace, 'warning', warning)
929
954
        self.assertEqual(True, suppress_warning('b'))
930
955
 
931
956
 
932
 
class TestGetConfig(tests.TestCaseInTempDir):
 
957
class TestGetConfig(tests.TestCase):
933
958
 
934
959
    def test_constructs(self):
935
960
        config.GlobalConfig()
944
969
        finally:
945
970
            config.ConfigObj = oldparserclass
946
971
        self.assertIsInstance(parser, InstrumentedConfigObj)
947
 
        self.assertEqual(parser._calls, [('__init__', bedding.config_path(),
 
972
        self.assertEqual(parser._calls, [('__init__', config.config_filename(),
948
973
                                          'utf-8')])
949
974
 
950
975
 
1003
1028
 
1004
1029
        local_path = osutils.getcwd().encode('utf8')
1005
1030
        config.LocationConfig.from_string(
1006
 
            b'[%s/branch]\nnickname = barry' % (local_path,),
1007
 
            'branch', save=True)
 
1031
            '[%s/branch]\nnickname = barry' % (local_path,),
 
1032
            'branch',  save=True)
1008
1033
        # Now the branch will find its nick via the location config
1009
1034
        self.assertEqual('barry', branch.nick)
1010
1035
 
1014
1039
        branch.set_push_location('http://foobar')
1015
1040
        local_path = osutils.getcwd().encode('utf8')
1016
1041
        # Surprisingly ConfigObj doesn't create a trailing newline
1017
 
        self.check_file_contents(bedding.locations_config_path(),
1018
 
                                 b'[%s/branch]\n'
1019
 
                                 b'push_location = http://foobar\n'
1020
 
                                 b'push_location:policy = norecurse\n'
 
1042
        self.check_file_contents(config.locations_config_filename(),
 
1043
                                 '[%s/branch]\n'
 
1044
                                 'push_location = http://foobar\n'
 
1045
                                 'push_location:policy = norecurse\n'
1021
1046
                                 % (local_path,))
1022
1047
 
1023
1048
    def test_autonick_urlencoded(self):
1030
1055
 
1031
1056
    def test_warn_if_masked(self):
1032
1057
        warnings = []
1033
 
 
1034
1058
        def warning(*args):
1035
1059
            warnings.append(args[0] % args[1:])
1036
1060
        self.overrideAttr(trace, 'warning', warning)
1039
1063
            warnings[:] = []
1040
1064
            conf.set_user_option('example_option', repr(store), store=store,
1041
1065
                                 warn_masked=warn_masked)
1042
 
 
1043
1066
        def assertWarning(warning):
1044
1067
            if warning is None:
1045
1068
                self.assertEqual(0, len(warnings))
1120
1143
        my_config = self._get_sample_config()
1121
1144
        change_editor = my_config.get_change_editor('old', 'new')
1122
1145
        self.assertIs(diff.DiffFromTool, change_editor.__class__)
1123
 
        self.assertEqual('vimdiff -of {new_path} {old_path}',
 
1146
        self.assertEqual('vimdiff -of @new_path @old_path',
1124
1147
                         ' '.join(change_editor.command_template))
1125
1148
 
1126
1149
    def test_get_no_change_editor(self):
1133
1156
        tools = conf.get_merge_tools()
1134
1157
        self.log(repr(tools))
1135
1158
        self.assertEqual(
1136
 
            {u'funkytool': u'funkytool "arg with spaces" {this_temp}',
1137
 
             u'sometool': u'sometool {base} {this} {other} -o {result}',
1138
 
             u'newtool': u'"newtool with spaces" {this_temp}'},
 
1159
            {u'funkytool' : u'funkytool "arg with spaces" {this_temp}',
 
1160
            u'sometool' : u'sometool {base} {this} {other} -o {result}',
 
1161
            u'newtool' : u'"newtool with spaces" {this_temp}'},
1139
1162
            tools)
1140
1163
 
1141
1164
    def test_get_merge_tools_empty(self):
1209
1232
            config.ConfigObj = oldparserclass
1210
1233
        self.assertIsInstance(parser, InstrumentedConfigObj)
1211
1234
        self.assertEqual(parser._calls,
1212
 
                         [('__init__', bedding.locations_config_path(),
 
1235
                         [('__init__', config.locations_config_filename(),
1213
1236
                           'utf-8')])
1214
1237
 
1215
1238
    def test_get_global_config(self):
1277
1300
        self.get_branch_config('http://www.example.com')
1278
1301
        self.assertEqual(
1279
1302
            self.my_location_config._get_config_policy(
1280
 
                'http://www.example.com', 'normal_option'),
 
1303
            'http://www.example.com', 'normal_option'),
1281
1304
            config.POLICY_NONE)
1282
1305
 
1283
1306
    def test__get_option_policy_norecurse(self):
1284
1307
        self.get_branch_config('http://www.example.com')
1285
1308
        self.assertEqual(
1286
1309
            self.my_location_config._get_option_policy(
1287
 
                'http://www.example.com', 'norecurse_option'),
 
1310
            'http://www.example.com', 'norecurse_option'),
1288
1311
            config.POLICY_NORECURSE)
1289
1312
        # Test old recurse=False setting:
1290
1313
        self.assertEqual(
1291
1314
            self.my_location_config._get_option_policy(
1292
 
                'http://www.example.com/norecurse', 'normal_option'),
 
1315
            'http://www.example.com/norecurse', 'normal_option'),
1293
1316
            config.POLICY_NORECURSE)
1294
1317
 
1295
1318
    def test__get_option_policy_normal(self):
1296
1319
        self.get_branch_config('http://www.example.com')
1297
1320
        self.assertEqual(
1298
1321
            self.my_location_config._get_option_policy(
1299
 
                'http://www.example.com', 'appendpath_option'),
 
1322
            'http://www.example.com', 'appendpath_option'),
1300
1323
            config.POLICY_APPENDPATH)
1301
1324
 
1302
1325
    def test__get_options_with_policy(self):
1378
1401
                                       store=config.STORE_LOCATION_NORECURSE)
1379
1402
        self.assertEqual(
1380
1403
            self.my_location_config._get_option_policy(
1381
 
                'http://www.example.com', 'foo'),
 
1404
            'http://www.example.com', 'foo'),
1382
1405
            config.POLICY_NORECURSE)
1383
1406
 
1384
1407
    def test_set_user_option_appendpath(self):
1387
1410
                                       store=config.STORE_LOCATION_APPENDPATH)
1388
1411
        self.assertEqual(
1389
1412
            self.my_location_config._get_option_policy(
1390
 
                'http://www.example.com', 'foo'),
 
1413
            'http://www.example.com', 'foo'),
1391
1414
            config.POLICY_APPENDPATH)
1392
1415
 
1393
1416
    def test_set_user_option_change_policy(self):
1396
1419
                                       store=config.STORE_LOCATION)
1397
1420
        self.assertEqual(
1398
1421
            self.my_location_config._get_option_policy(
1399
 
                'http://www.example.com', 'norecurse_option'),
 
1422
            'http://www.example.com', 'norecurse_option'),
1400
1423
            config.POLICY_NONE)
1401
1424
 
1402
1425
    def get_branch_config(self, location, global_config=None,
1420
1443
        self.my_config.set_user_option('foo', 'bar')
1421
1444
        self.assertEqual(
1422
1445
            self.my_config.branch.control_files.files['branch.conf'].strip(),
1423
 
            b'foo = bar')
 
1446
            'foo = bar')
1424
1447
        self.assertEqual(self.my_config.get_user_option('foo'), 'bar')
1425
1448
        self.my_config.set_user_option('foo', 'baz',
1426
1449
                                       store=config.STORE_LOCATION)
1437
1460
        self.assertEqual('/environ-bzr', my_config.get_bzr_remote_path())
1438
1461
 
1439
1462
 
1440
 
precedence_global = b'option = global'
1441
 
precedence_branch = b'option = branch'
1442
 
precedence_location = b"""
 
1463
precedence_global = 'option = global'
 
1464
precedence_branch = 'option = branch'
 
1465
precedence_location = """
1443
1466
[http://]
1444
1467
recurse = true
1445
1468
option = recurse
1447
1470
option = exact
1448
1471
"""
1449
1472
 
1450
 
 
1451
1473
class TestBranchConfigItems(tests.TestCaseInTempDir):
1452
1474
 
1453
1475
    def get_branch_config(self, global_config=None, location=None,
1472
1494
        my_config.set_user_option('email',
1473
1495
                                  "Robert Collins <robertc@example.org>")
1474
1496
        self.assertEqual("Robert Collins <robertc@example.org>",
1475
 
                         my_config.username())
 
1497
                        my_config.username())
1476
1498
 
1477
1499
    def test_BRZ_EMAIL_OVERRIDES(self):
1478
1500
        self.overrideEnv('BRZ_EMAIL', "Robert Collins <robertc@example.org>")
1481
1503
        self.assertEqual("Robert Collins <robertc@example.org>",
1482
1504
                         my_config.username())
1483
1505
 
1484
 
    def test_BRZ_EMAIL_OVERRIDES(self):
1485
 
        self.overrideEnv('BZR_EMAIL', "Robert Collins <robertc@example.org>")
1486
 
        branch = FakeBranch()
1487
 
        my_config = config.BranchConfig(branch)
1488
 
        self.assertEqual("Robert Collins <robertc@example.org>",
1489
 
                         my_config.username())
1490
 
 
1491
1506
    def test_get_user_option_global(self):
1492
1507
        my_config = self.get_branch_config(global_config=sample_config_text)
1493
1508
        self.assertEqual('something',
1519
1534
    def test_extract_email_address(self):
1520
1535
        self.assertEqual('jane@test.com',
1521
1536
                         config.extract_email_address('Jane <jane@test.com>'))
1522
 
        self.assertRaises(config.NoEmailInUsername,
 
1537
        self.assertRaises(errors.NoEmailInUsername,
1523
1538
                          config.extract_email_address, 'Jane Tester')
1524
1539
 
1525
1540
    def test_parse_username(self):
1534
1549
        self.assertEqual(('John Doe', 'jdoe@example.com'),
1535
1550
                         config.parse_username('John Doe jdoe@example.com'))
1536
1551
 
1537
 
 
1538
1552
class TestTreeConfig(tests.TestCaseWithTransport):
1539
1553
 
1540
1554
    def test_get_value(self):
1579
1593
    def test_load_non_ascii(self):
1580
1594
        """Ensure we display a proper error on non-ascii, non utf-8 content."""
1581
1595
        t = self.get_transport()
1582
 
        t.put_bytes('foo.conf', b'user=foo\n#\xff\n')
 
1596
        t.put_bytes('foo.conf', 'user=foo\n#\xff\n')
1583
1597
        conf = config.TransportConfig(t, 'foo.conf')
1584
 
        self.assertRaises(config.ConfigContentError, conf._get_configobj)
 
1598
        self.assertRaises(errors.ConfigContentError, conf._get_configobj)
1585
1599
 
1586
1600
    def test_load_erroneous_content(self):
1587
1601
        """Ensure we display a proper error on content that can't be parsed."""
1588
1602
        t = self.get_transport()
1589
 
        t.put_bytes('foo.conf', b'[open_section\n')
 
1603
        t.put_bytes('foo.conf', '[open_section\n')
1590
1604
        conf = config.TransportConfig(t, 'foo.conf')
1591
 
        self.assertRaises(config.ParseConfigError, conf._get_configobj)
 
1605
        self.assertRaises(errors.ParseConfigError, conf._get_configobj)
1592
1606
 
1593
1607
    def test_load_permission_denied(self):
1594
1608
        """Ensure we get an empty config file if the file is inaccessible."""
1595
1609
        warnings = []
1596
 
 
1597
1610
        def warning(*args):
1598
1611
            warnings.append(args[0] % args[1:])
1599
1612
        self.overrideAttr(trace, 'warning', warning)
1641
1654
        self.assertEqual(value, 'value3-section')
1642
1655
 
1643
1656
    def test_set_unset_default_stack_on(self):
1644
 
        my_dir = self.make_controldir('.')
 
1657
        my_dir = self.make_bzrdir('.')
1645
1658
        bzrdir_config = config.BzrDirConfig(my_dir)
1646
1659
        self.assertIs(None, bzrdir_config.get_default_stack_on())
1647
1660
        bzrdir_config.set_default_stack_on('Foo')
1660
1673
 
1661
1674
    def assertGetHook(self, conf, name, value):
1662
1675
        calls = []
1663
 
 
1664
1676
        def hook(*args):
1665
1677
            calls.append(args)
1666
1678
        config.OldConfigHooks.install_named_hook('get', hook, None)
1672
1684
        self.assertLength(1, calls)
1673
1685
        self.assertEqual((conf, name, value), calls[0])
1674
1686
 
1675
 
    def test_get_hook_breezy(self):
1676
 
        self.assertGetHook(self.breezy_config, 'file', 'breezy')
 
1687
    def test_get_hook_bazaar(self):
 
1688
        self.assertGetHook(self.bazaar_config, 'file', 'bazaar')
1677
1689
 
1678
1690
    def test_get_hook_locations(self):
1679
1691
        self.assertGetHook(self.locations_config, 'file', 'locations')
1685
1697
 
1686
1698
    def assertSetHook(self, conf, name, value):
1687
1699
        calls = []
1688
 
 
1689
1700
        def hook(*args):
1690
1701
            calls.append(args)
1691
1702
        config.OldConfigHooks.install_named_hook('set', hook, None)
1699
1710
        # coverage here.
1700
1711
        self.assertEqual((name, value), calls[0][1:])
1701
1712
 
1702
 
    def test_set_hook_breezy(self):
1703
 
        self.assertSetHook(self.breezy_config, 'foo', 'breezy')
 
1713
    def test_set_hook_bazaar(self):
 
1714
        self.assertSetHook(self.bazaar_config, 'foo', 'bazaar')
1704
1715
 
1705
1716
    def test_set_hook_locations(self):
1706
1717
        self.assertSetHook(self.locations_config, 'foo', 'locations')
1710
1721
 
1711
1722
    def assertRemoveHook(self, conf, name, section_name=None):
1712
1723
        calls = []
1713
 
 
1714
1724
        def hook(*args):
1715
1725
            calls.append(args)
1716
1726
        config.OldConfigHooks.install_named_hook('remove', hook, None)
1724
1734
        # coverage here.
1725
1735
        self.assertEqual((name,), calls[0][1:])
1726
1736
 
1727
 
    def test_remove_hook_breezy(self):
1728
 
        self.assertRemoveHook(self.breezy_config, 'file')
 
1737
    def test_remove_hook_bazaar(self):
 
1738
        self.assertRemoveHook(self.bazaar_config, 'file')
1729
1739
 
1730
1740
    def test_remove_hook_locations(self):
1731
1741
        self.assertRemoveHook(self.locations_config, 'file',
1736
1746
 
1737
1747
    def assertLoadHook(self, name, conf_class, *conf_args):
1738
1748
        calls = []
1739
 
 
1740
1749
        def hook(*args):
1741
1750
            calls.append(args)
1742
1751
        config.OldConfigHooks.install_named_hook('load', hook, None)
1750
1759
        self.assertLength(1, calls)
1751
1760
        # Since we can't assert about conf, we just use the number of calls ;-/
1752
1761
 
1753
 
    def test_load_hook_breezy(self):
 
1762
    def test_load_hook_bazaar(self):
1754
1763
        self.assertLoadHook('file', config.GlobalConfig)
1755
1764
 
1756
1765
    def test_load_hook_locations(self):
1761
1770
 
1762
1771
    def assertSaveHook(self, conf):
1763
1772
        calls = []
1764
 
 
1765
1773
        def hook(*args):
1766
1774
            calls.append(args)
1767
1775
        config.OldConfigHooks.install_named_hook('save', hook, None)
1773
1781
        self.assertLength(1, calls)
1774
1782
        # Since we can't assert about conf, we just use the number of calls ;-/
1775
1783
 
1776
 
    def test_save_hook_breezy(self):
1777
 
        self.assertSaveHook(self.breezy_config)
 
1784
    def test_save_hook_bazaar(self):
 
1785
        self.assertSaveHook(self.bazaar_config)
1778
1786
 
1779
1787
    def test_save_hook_locations(self):
1780
1788
        self.assertSaveHook(self.locations_config)
1796
1804
 
1797
1805
    def assertGetHook(self, conf, name, value):
1798
1806
        calls = []
1799
 
 
1800
1807
        def hook(*args):
1801
1808
            calls.append(args)
1802
1809
        config.OldConfigHooks.install_named_hook('get', hook, None)
1820
1827
 
1821
1828
    def assertSetHook(self, conf, name, value):
1822
1829
        calls = []
1823
 
 
1824
1830
        def hook(*args):
1825
1831
            calls.append(args)
1826
1832
        config.OldConfigHooks.install_named_hook('set', hook, None)
1847
1853
 
1848
1854
    def assertLoadHook(self, expected_nb_calls, name, conf_class, *conf_args):
1849
1855
        calls = []
1850
 
 
1851
1856
        def hook(*args):
1852
1857
            calls.append(args)
1853
1858
        config.OldConfigHooks.install_named_hook('load', hook, None)
1863
1868
 
1864
1869
    def test_load_hook_remote_branch(self):
1865
1870
        remote_branch = branch.Branch.open(self.get_url('tree'))
1866
 
        self.assertLoadHook(
1867
 
            1, 'file', remote.RemoteBranchConfig, remote_branch)
 
1871
        self.assertLoadHook(1, 'file', remote.RemoteBranchConfig, remote_branch)
1868
1872
 
1869
1873
    def test_load_hook_remote_bzrdir(self):
1870
1874
        remote_bzrdir = controldir.ControlDir.open(self.get_url('tree'))
1875
1879
        # caused by the differences in implementations betwen
1876
1880
        # SmartServerBzrDirRequestConfigFile (in smart/bzrdir.py) and
1877
1881
        # SmartServerBranchGetConfigFile (in smart/branch.py)
1878
 
        self.assertLoadHook(
1879
 
            2, 'file', remote.RemoteBzrDirConfig, remote_bzrdir)
 
1882
        self.assertLoadHook(2 ,'file', remote.RemoteBzrDirConfig, remote_bzrdir)
1880
1883
 
1881
1884
    def assertSaveHook(self, conf):
1882
1885
        calls = []
1883
 
 
1884
1886
        def hook(*args):
1885
1887
            calls.append(args)
1886
1888
        config.OldConfigHooks.install_named_hook('save', hook, None)
1983
1985
 
1984
1986
    def test_not_supported_callable_default_value_not_unicode(self):
1985
1987
        def bar_not_unicode():
1986
 
            return b'bar'
 
1988
            return 'bar'
1987
1989
        opt = config.Option('foo', default=bar_not_unicode)
1988
1990
        self.assertRaises(AssertionError, opt.get_default)
1989
1991
 
2010
2012
            warnings[0])
2011
2013
 
2012
2014
    def assertCallsError(self, opt, value):
2013
 
        self.assertRaises(config.ConfigOptionValueError,
 
2015
        self.assertRaises(errors.ConfigOptionValueError,
2014
2016
                          opt.convert_from_unicode, None, value)
2015
2017
 
2016
2018
    def assertConvertInvalid(self, opt, invalid_value):
2177
2179
        self.assertEqual('A simple option', self.registry.get_help('foo'))
2178
2180
 
2179
2181
    def test_dont_register_illegal_name(self):
2180
 
        self.assertRaises(config.IllegalOptionName,
 
2182
        self.assertRaises(errors.IllegalOptionName,
2181
2183
                          self.registry.register, config.Option(' foo'))
2182
 
        self.assertRaises(config.IllegalOptionName,
 
2184
        self.assertRaises(errors.IllegalOptionName,
2183
2185
                          self.registry.register, config.Option('bar,'))
2184
2186
 
2185
2187
    lazy_option = config.Option('lazy_foo', help='Lazy help')
2200
2202
        # the option name which indirectly requires that the option name is a
2201
2203
        # valid python identifier. We violate that rule here (using a key that
2202
2204
        # doesn't match the option name) to test the option name checking.
2203
 
        self.assertRaises(config.IllegalOptionName,
 
2205
        self.assertRaises(errors.IllegalOptionName,
2204
2206
                          self.registry.register_lazy, ' foo', self.__module__,
2205
2207
                          'TestOptionRegistry.lazy_option')
2206
 
        self.assertRaises(config.IllegalOptionName,
 
2208
        self.assertRaises(errors.IllegalOptionName,
2207
2209
                          self.registry.register_lazy, '1,2', self.__module__,
2208
2210
                          'TestOptionRegistry.lazy_option')
2209
2211
 
2245
2247
        a_dict = dict()
2246
2248
        section = config.Section(None, a_dict)
2247
2249
        self.assertEqual('out of thin air',
2248
 
                         section.get('foo', 'out of thin air'))
 
2250
                          section.get('foo', 'out of thin air'))
2249
2251
 
2250
2252
    def test_options_is_shared(self):
2251
2253
        a_dict = dict()
2257
2259
 
2258
2260
    scenarios = [('mutable',
2259
2261
                  {'get_section':
2260
 
                   lambda opts: config.MutableSection('myID', opts)},),
2261
 
                 ]
 
2262
                       lambda opts: config.MutableSection('myID', opts)},),
 
2263
        ]
2262
2264
 
2263
2265
    def test_set(self):
2264
2266
        a_dict = dict(foo='bar')
2348
2350
        self.assertEqual('y', section.get('x'))
2349
2351
 
2350
2352
    def test_wrong_syntax(self):
2351
 
        self.assertRaises(errors.CommandError,
 
2353
        self.assertRaises(errors.BzrCommandError,
2352
2354
                          self.store._from_cmdline, ['a=b', 'c'])
2353
2355
 
2354
 
 
2355
2356
class TestStoreMinimalAPI(tests.TestCaseWithTransport):
2356
2357
 
2357
2358
    scenarios = [(key, {'get_store': builder}) for key, builder
2387
2388
    def test_building_delays_load(self):
2388
2389
        store = self.get_store(self)
2389
2390
        self.assertEqual(False, store.is_loaded())
2390
 
        store._load_from_string(b'')
 
2391
        store._load_from_string('')
2391
2392
        self.assertEqual(True, store.is_loaded())
2392
2393
 
2393
2394
    def test_get_no_sections_for_empty(self):
2394
2395
        store = self.get_store(self)
2395
 
        store._load_from_string(b'')
 
2396
        store._load_from_string('')
2396
2397
        self.assertEqual([], list(store.get_sections()))
2397
2398
 
2398
2399
    def test_get_default_section(self):
2399
2400
        store = self.get_store(self)
2400
 
        store._load_from_string(b'foo=bar')
 
2401
        store._load_from_string('foo=bar')
2401
2402
        sections = list(store.get_sections())
2402
2403
        self.assertLength(1, sections)
2403
2404
        self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2404
2405
 
2405
2406
    def test_get_named_section(self):
2406
2407
        store = self.get_store(self)
2407
 
        store._load_from_string(b'[baz]\nfoo=bar')
 
2408
        store._load_from_string('[baz]\nfoo=bar')
2408
2409
        sections = list(store.get_sections())
2409
2410
        self.assertLength(1, sections)
2410
2411
        self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2411
2412
 
2412
2413
    def test_load_from_string_fails_for_non_empty_store(self):
2413
2414
        store = self.get_store(self)
2414
 
        store._load_from_string(b'foo=bar')
2415
 
        self.assertRaises(AssertionError, store._load_from_string, b'bar=baz')
 
2415
        store._load_from_string('foo=bar')
 
2416
        self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2416
2417
 
2417
2418
 
2418
2419
class TestStoreQuoting(TestStore):
2424
2425
        super(TestStoreQuoting, self).setUp()
2425
2426
        self.store = self.get_store(self)
2426
2427
        # We need a loaded store but any content will do
2427
 
        self.store._load_from_string(b'')
 
2428
        self.store._load_from_string('')
2428
2429
 
2429
2430
    def assertIdempotent(self, s):
2430
2431
        """Assert that quoting an unquoted string is a no-op and vice-versa.
2456
2457
    def test_simple_comma(self):
2457
2458
        if isinstance(self.store, config.IniFileStore):
2458
2459
            # configobj requires that lists are special-cased
2459
 
            self.assertRaises(AssertionError,
2460
 
                              self.assertIdempotent, ',')
 
2460
           self.assertRaises(AssertionError,
 
2461
                             self.assertIdempotent, ',')
2461
2462
        else:
2462
2463
            self.assertIdempotent(',')
2463
2464
        # When a single comma is required, quoting is also required
2475
2476
class TestDictFromStore(tests.TestCase):
2476
2477
 
2477
2478
    def test_unquote_not_string(self):
2478
 
        conf = config.MemoryStack(b'x=2\n[a_section]\na=1\n')
 
2479
        conf = config.MemoryStack('x=2\n[a_section]\na=1\n')
2479
2480
        value = conf.get('a_section')
2480
2481
        # Urgh, despite 'conf' asking for the no-name section, we get the
2481
2482
        # content of another section as a dict o_O
2485
2486
        # are getting the value or displaying it. In the later case, '%s' will
2486
2487
        # do).
2487
2488
        self.assertEqual({'a': '1'}, unquoted)
2488
 
        self.assertIn('%s' % (unquoted,), ("{u'a': u'1'}", "{'a': '1'}"))
 
2489
        self.assertEqual("{u'a': u'1'}", '%s' % (unquoted,))
2489
2490
 
2490
2491
 
2491
2492
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2497
2498
    loaded. We need to issue proper error messages in this case.
2498
2499
    """
2499
2500
 
2500
 
    invalid_utf8_char = b'\xff'
 
2501
    invalid_utf8_char = '\xff'
2501
2502
 
2502
2503
    def test_load_utf8(self):
2503
2504
        """Ensure we can load an utf8-encoded file."""
2516
2517
    def test_load_non_ascii(self):
2517
2518
        """Ensure we display a proper error on non-ascii, non utf-8 content."""
2518
2519
        t = self.get_transport()
2519
 
        t.put_bytes('foo.conf', b'user=foo\n#%s\n' % (self.invalid_utf8_char,))
 
2520
        t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2520
2521
        store = config.TransportIniFileStore(t, 'foo.conf')
2521
 
        self.assertRaises(config.ConfigContentError, store.load)
 
2522
        self.assertRaises(errors.ConfigContentError, store.load)
2522
2523
 
2523
2524
    def test_load_erroneous_content(self):
2524
2525
        """Ensure we display a proper error on content that can't be parsed."""
2525
2526
        t = self.get_transport()
2526
 
        t.put_bytes('foo.conf', b'[open_section\n')
 
2527
        t.put_bytes('foo.conf', '[open_section\n')
2527
2528
        store = config.TransportIniFileStore(t, 'foo.conf')
2528
 
        self.assertRaises(config.ParseConfigError, store.load)
 
2529
        self.assertRaises(errors.ParseConfigError, store.load)
2529
2530
 
2530
2531
    def test_load_permission_denied(self):
2531
2532
        """Ensure we get warned when trying to load an inaccessible file."""
2532
2533
        warnings = []
2533
 
 
2534
2534
        def warning(*args):
2535
2535
            warnings.append(args[0] % args[1:])
2536
2536
        self.overrideAttr(trace, 'warning', warning)
2557
2557
    loaded. We need to issue proper error messages in this case.
2558
2558
    """
2559
2559
 
2560
 
    invalid_utf8_char = b'\xff'
 
2560
    invalid_utf8_char = '\xff'
2561
2561
 
2562
2562
    def test_load_utf8(self):
2563
2563
        """Ensure we can load an utf8-encoded file."""
2574
2574
    def test_load_badly_encoded_content(self):
2575
2575
        """Ensure we display a proper error on non-ascii, non utf-8 content."""
2576
2576
        with open('foo.conf', 'wb') as f:
2577
 
            f.write(b'user=foo\n#%s\n' % (self.invalid_utf8_char,))
 
2577
            f.write('user=foo\n#%s\n' % (self.invalid_utf8_char,))
2578
2578
        conf = config.IniBasedConfig(file_name='foo.conf')
2579
 
        self.assertRaises(config.ConfigContentError, conf._get_parser)
 
2579
        self.assertRaises(errors.ConfigContentError, conf._get_parser)
2580
2580
 
2581
2581
    def test_load_erroneous_content(self):
2582
2582
        """Ensure we display a proper error on content that can't be parsed."""
2583
2583
        with open('foo.conf', 'wb') as f:
2584
 
            f.write(b'[open_section\n')
 
2584
            f.write('[open_section\n')
2585
2585
        conf = config.IniBasedConfig(file_name='foo.conf')
2586
 
        self.assertRaises(config.ParseConfigError, conf._get_parser)
 
2586
        self.assertRaises(errors.ParseConfigError, conf._get_parser)
2587
2587
 
2588
2588
 
2589
2589
class TestMutableStore(TestStore):
2612
2612
 
2613
2613
    def test_mutable_section_shared(self):
2614
2614
        store = self.get_store(self)
2615
 
        store._load_from_string(b'foo=bar\n')
 
2615
        store._load_from_string('foo=bar\n')
2616
2616
        # FIXME: There should be a better way than relying on the test
2617
2617
        # parametrization to identify branch.conf -- vila 2011-0526
2618
2618
        if self.store_id in ('branch', 'remote_branch'):
2626
2626
 
2627
2627
    def test_save_emptied_succeeds(self):
2628
2628
        store = self.get_store(self)
2629
 
        store._load_from_string(b'foo=bar\n')
 
2629
        store._load_from_string('foo=bar\n')
2630
2630
        # FIXME: There should be a better way than relying on the test
2631
2631
        # parametrization to identify branch.conf -- vila 2011-0526
2632
2632
        if self.store_id in ('branch', 'remote_branch'):
2647
2647
            raise tests.TestNotApplicable(
2648
2648
                'branch.conf is *always* created when a branch is initialized')
2649
2649
        store = self.get_store(self)
2650
 
        store._load_from_string(b'foo=bar\n')
 
2650
        store._load_from_string('foo=bar\n')
2651
2651
        self.assertEqual(False, self.has_store(store))
2652
2652
        store.save()
2653
2653
        self.assertEqual(True, self.has_store(store))
2673
2673
 
2674
2674
    def test_set_option_in_default_section(self):
2675
2675
        store = self.get_store(self)
2676
 
        store._load_from_string(b'')
 
2676
        store._load_from_string('')
2677
2677
        # FIXME: There should be a better way than relying on the test
2678
2678
        # parametrization to identify branch.conf -- vila 2011-0526
2679
2679
        if self.store_id in ('branch', 'remote_branch'):
2689
2689
 
2690
2690
    def test_set_option_in_named_section(self):
2691
2691
        store = self.get_store(self)
2692
 
        store._load_from_string(b'')
 
2692
        store._load_from_string('')
2693
2693
        # FIXME: There should be a better way than relying on the test
2694
2694
        # parametrization to identify branch.conf -- vila 2011-0526
2695
2695
        if self.store_id in ('branch', 'remote_branch'):
2717
2717
        # Now we can try to load it
2718
2718
        store = self.get_store(self)
2719
2719
        calls = []
2720
 
 
2721
2720
        def hook(*args):
2722
2721
            calls.append(args)
2723
2722
        config.ConfigHooks.install_named_hook('load', hook, None)
2728
2727
 
2729
2728
    def test_save_hook(self):
2730
2729
        calls = []
2731
 
 
2732
2730
        def hook(*args):
2733
2731
            calls.append(args)
2734
2732
        config.ConfigHooks.install_named_hook('save', hook, None)
2746
2744
        self.assertEqual((store,), calls[0])
2747
2745
 
2748
2746
    def test_set_mark_dirty(self):
2749
 
        stack = config.MemoryStack(b'')
 
2747
        stack = config.MemoryStack('')
2750
2748
        self.assertLength(0, stack.store.dirty_sections)
2751
2749
        stack.set('foo', 'baz')
2752
2750
        self.assertLength(1, stack.store.dirty_sections)
2753
2751
        self.assertTrue(stack.store._need_saving())
2754
2752
 
2755
2753
    def test_remove_mark_dirty(self):
2756
 
        stack = config.MemoryStack(b'foo=bar')
 
2754
        stack = config.MemoryStack('foo=bar')
2757
2755
        self.assertLength(0, stack.store.dirty_sections)
2758
2756
        stack.remove('foo')
2759
2757
        self.assertLength(1, stack.store.dirty_sections)
2771
2769
        self.st1 = config.TransportIniFileStore(self.transport, 'foo.conf')
2772
2770
        self.st2 = config.TransportIniFileStore(self.transport, 'foo.conf')
2773
2771
        self.warnings = []
2774
 
 
2775
2772
        def warning(*args):
2776
2773
            self.warnings.append(args[0] % args[1:])
2777
2774
        self.overrideAttr(trace, 'warning', warning)
2823
2820
                            ' The baz value will be saved.')
2824
2821
 
2825
2822
    def test_concurrent_deletion(self):
2826
 
        self.st1._load_from_string(b'foo=bar')
 
2823
        self.st1._load_from_string('foo=bar')
2827
2824
        self.st1.save()
2828
2825
        s1 = self.get_stack(self.st1)
2829
2826
        s2 = self.get_stack(self.st2)
2848
2845
 
2849
2846
    def test_get_quoted_string(self):
2850
2847
        store = self.get_store()
2851
 
        store._load_from_string(b'foo= " abc "')
 
2848
        store._load_from_string('foo= " abc "')
2852
2849
        stack = config.Stack([store.get_sections])
2853
2850
        self.assertEqual(' abc ', stack.get('foo'))
2854
2851
 
2857
2854
        stack = config.Stack([store.get_sections], store)
2858
2855
        stack.set('foo', ' a b c ')
2859
2856
        store.save()
2860
 
        self.assertFileEqual(b'foo = " a b c "' +
2861
 
                             os.linesep.encode('ascii'), 'foo.conf')
 
2857
        self.assertFileEqual('foo = " a b c "' + os.linesep, 'foo.conf')
2862
2858
 
2863
2859
 
2864
2860
class TestTransportIniFileStore(TestStore):
2865
2861
 
2866
2862
    def test_loading_unknown_file_fails(self):
2867
2863
        store = config.TransportIniFileStore(self.get_transport(),
2868
 
                                             'I-do-not-exist')
 
2864
            'I-do-not-exist')
2869
2865
        self.assertRaises(errors.NoSuchFile, store.load)
2870
2866
 
2871
2867
    def test_invalid_content(self):
2872
2868
        store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
2873
2869
        self.assertEqual(False, store.is_loaded())
2874
2870
        exc = self.assertRaises(
2875
 
            config.ParseConfigError, store._load_from_string,
2876
 
            b'this is invalid !')
 
2871
            errors.ParseConfigError, store._load_from_string,
 
2872
            'this is invalid !')
2877
2873
        self.assertEndsWith(exc.filename, 'foo.conf')
2878
2874
        # And the load failed
2879
2875
        self.assertEqual(False, store.is_loaded())
2884
2880
        # FIXME: This should be fixed by forbidding dicts as values ?
2885
2881
        # -- vila 2011-04-05
2886
2882
        store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
2887
 
        store._load_from_string(b'''
 
2883
        store._load_from_string('''
2888
2884
foo=bar
2889
2885
l=1,2
2890
2886
[DEFAULT]
2988
2984
        after_writing = threading.Event()
2989
2985
        writing_done = threading.Event()
2990
2986
        c1_save_without_locking_orig = c1.store.save_without_locking
2991
 
 
2992
2987
        def c1_save_without_locking():
2993
2988
            before_writing.set()
2994
2989
            c1_save_without_locking_orig()
2996
2991
            # continue
2997
2992
            after_writing.wait()
2998
2993
        c1.store.save_without_locking = c1_save_without_locking
2999
 
 
3000
2994
        def c1_set():
3001
2995
            c1.set('one', 'c1')
3002
2996
            writing_done.set()
3017
3011
        self.assertEqual('c2', c2.get('one'))
3018
3012
 
3019
3013
    def test_read_while_writing(self):
3020
 
        c1 = self.stack
3021
 
        # We spawn a thread that will pause *during* the write
3022
 
        ready_to_write = threading.Event()
3023
 
        do_writing = threading.Event()
3024
 
        writing_done = threading.Event()
3025
 
        # We override the _save implementation so we know the store is locked
3026
 
        c1_save_without_locking_orig = c1.store.save_without_locking
3027
 
 
3028
 
        def c1_save_without_locking():
3029
 
            ready_to_write.set()
3030
 
            # The lock is held. We wait for the main thread to decide when to
3031
 
            # continue
3032
 
            do_writing.wait()
3033
 
            c1_save_without_locking_orig()
3034
 
            writing_done.set()
3035
 
        c1.store.save_without_locking = c1_save_without_locking
3036
 
 
3037
 
        def c1_set():
3038
 
            c1.set('one', 'c1')
3039
 
        t1 = threading.Thread(target=c1_set)
3040
 
        # Collect the thread after the test
3041
 
        self.addCleanup(t1.join)
3042
 
        # Be ready to unblock the thread if the test goes wrong
3043
 
        self.addCleanup(do_writing.set)
3044
 
        t1.start()
3045
 
        # Ensure the thread is ready to write
3046
 
        ready_to_write.wait()
3047
 
        self.assertEqual('c1', c1.get('one'))
3048
 
        # If we read during the write, we get the old value
3049
 
        c2 = self.get_stack(self)
3050
 
        self.assertEqual('1', c2.get('one'))
3051
 
        # Let the writing occur and ensure it occurred
3052
 
        do_writing.set()
3053
 
        writing_done.wait()
3054
 
        # Now we get the updated value
3055
 
        c3 = self.get_stack(self)
3056
 
        self.assertEqual('c1', c3.get('one'))
 
3014
       c1 = self.stack
 
3015
       # We spawn a thread that will pause *during* the write
 
3016
       ready_to_write = threading.Event()
 
3017
       do_writing = threading.Event()
 
3018
       writing_done = threading.Event()
 
3019
       # We override the _save implementation so we know the store is locked
 
3020
       c1_save_without_locking_orig = c1.store.save_without_locking
 
3021
       def c1_save_without_locking():
 
3022
           ready_to_write.set()
 
3023
           # The lock is held. We wait for the main thread to decide when to
 
3024
           # continue
 
3025
           do_writing.wait()
 
3026
           c1_save_without_locking_orig()
 
3027
           writing_done.set()
 
3028
       c1.store.save_without_locking = c1_save_without_locking
 
3029
       def c1_set():
 
3030
           c1.set('one', 'c1')
 
3031
       t1 = threading.Thread(target=c1_set)
 
3032
       # Collect the thread after the test
 
3033
       self.addCleanup(t1.join)
 
3034
       # Be ready to unblock the thread if the test goes wrong
 
3035
       self.addCleanup(do_writing.set)
 
3036
       t1.start()
 
3037
       # Ensure the thread is ready to write
 
3038
       ready_to_write.wait()
 
3039
       self.assertEqual('c1', c1.get('one'))
 
3040
       # If we read during the write, we get the old value
 
3041
       c2 = self.get_stack(self)
 
3042
       self.assertEqual('1', c2.get('one'))
 
3043
       # Let the writing occur and ensure it occurred
 
3044
       do_writing.set()
 
3045
       writing_done.wait()
 
3046
       # Now we get the updated value
 
3047
       c3 = self.get_stack(self)
 
3048
       self.assertEqual('c1', c3.get('one'))
3057
3049
 
3058
3050
    # FIXME: It may be worth looking into removing the lock dir when it's not
3059
3051
    # needed anymore and look at possible fallouts for concurrent lockers. This
3060
 
    # will matter if/when we use config files outside of breezy directories
3061
 
    # (.config/breezy or .bzr) -- vila 20110-04-111
 
3052
    # will matter if/when we use config files outside of bazaar directories
 
3053
    # (.bazaar or .bzr) -- vila 20110-04-111
3062
3054
 
3063
3055
 
3064
3056
class TestSectionMatcher(TestStore):
3065
3057
 
3066
3058
    scenarios = [('location', {'matcher': config.LocationMatcher}),
3067
 
                 ('id', {'matcher': config.NameMatcher}), ]
 
3059
                 ('id', {'matcher': config.NameMatcher}),]
3068
3060
 
3069
3061
    def setUp(self):
3070
3062
        super(TestSectionMatcher, self).setUp()
3073
3065
 
3074
3066
    def test_no_matches_for_empty_stores(self):
3075
3067
        store = self.get_store(self)
3076
 
        store._load_from_string(b'')
 
3068
        store._load_from_string('')
3077
3069
        matcher = self.matcher(store, '/bar')
3078
3070
        self.assertEqual([], list(matcher.get_sections()))
3079
3071
 
3114
3106
 
3115
3107
    def test_unrelated_section_excluded(self):
3116
3108
        store = self.get_store(self)
3117
 
        store._load_from_string(b'''
 
3109
        store._load_from_string('''
3118
3110
[/foo]
3119
3111
section=/foo
3120
3112
[/foo/baz]
3127
3119
section=/quux/quux
3128
3120
''')
3129
3121
        self.assertEqual(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
3130
 
                          '/quux/quux'],
3131
 
                         [section.id for _, section in store.get_sections()])
 
3122
                           '/quux/quux'],
 
3123
                          [section.id for _, section in store.get_sections()])
3132
3124
        matcher = config.LocationMatcher(store, '/foo/bar/quux')
3133
3125
        sections = [section for _, section in matcher.get_sections()]
3134
3126
        self.assertEqual(['/foo/bar', '/foo'],
3135
 
                         [section.id for section in sections])
 
3127
                          [section.id for section in sections])
3136
3128
        self.assertEqual(['quux', 'bar/quux'],
3137
 
                         [section.extra_path for section in sections])
 
3129
                          [section.extra_path for section in sections])
3138
3130
 
3139
3131
    def test_more_specific_sections_first(self):
3140
3132
        store = self.get_store(self)
3141
 
        store._load_from_string(b'''
 
3133
        store._load_from_string('''
3142
3134
[/foo]
3143
3135
section=/foo
3144
3136
[/foo/bar]
3145
3137
section=/foo/bar
3146
3138
''')
3147
3139
        self.assertEqual(['/foo', '/foo/bar'],
3148
 
                         [section.id for _, section in store.get_sections()])
 
3140
                          [section.id for _, section in store.get_sections()])
3149
3141
        matcher = config.LocationMatcher(store, '/foo/bar/baz')
3150
3142
        sections = [section for _, section in matcher.get_sections()]
3151
3143
        self.assertEqual(['/foo/bar', '/foo'],
3152
 
                         [section.id for section in sections])
 
3144
                          [section.id for section in sections])
3153
3145
        self.assertEqual(['baz', 'bar/baz'],
3154
 
                         [section.extra_path for section in sections])
 
3146
                          [section.extra_path for section in sections])
3155
3147
 
3156
3148
    def test_appendpath_in_no_name_section(self):
3157
3149
        # It's a bit weird to allow appendpath in a no-name section, but
3158
3150
        # someone may found a use for it
3159
3151
        store = self.get_store(self)
3160
 
        store._load_from_string(b'''
 
3152
        store._load_from_string('''
3161
3153
foo=bar
3162
3154
foo:policy = appendpath
3163
3155
''')
3182
3174
        store._load_from_string(dedent("""\
3183
3175
            [/]
3184
3176
            push_location=my{branchname}
3185
 
        """).encode('ascii'))
 
3177
        """))
3186
3178
        matcher = config.LocationMatcher(store, 'file:///,branch=example%3c')
3187
3179
        self.assertEqual('example<', matcher.branch_name)
3188
3180
        ((_, section),) = matcher.get_sections()
3193
3185
        store._load_from_string(dedent("""\
3194
3186
            [/]
3195
3187
            push_location=my{branchname}
3196
 
        """).encode('ascii'))
 
3188
        """))
3197
3189
        matcher = config.LocationMatcher(store, 'file:///parent/example%3c')
3198
3190
        self.assertEqual('example<', matcher.branch_name)
3199
3191
        ((_, section),) = matcher.get_sections()
3216
3208
        return sections
3217
3209
 
3218
3210
    def test_empty(self):
3219
 
        self.assertSectionIDs([], self.get_url(), b'')
 
3211
        self.assertSectionIDs([], self.get_url(), '')
3220
3212
 
3221
3213
    def test_url_vs_local_paths(self):
3222
3214
        # The matcher location is an url and the section names are local paths
3223
3215
        self.assertSectionIDs(['/foo/bar', '/foo'],
3224
 
                              'file:///foo/bar/baz', b'''\
 
3216
                              'file:///foo/bar/baz', '''\
3225
3217
[/foo]
3226
3218
[/foo/bar]
3227
3219
''')
3229
3221
    def test_local_path_vs_url(self):
3230
3222
        # The matcher location is a local path and the section names are urls
3231
3223
        self.assertSectionIDs(['file:///foo/bar', 'file:///foo'],
3232
 
                              '/foo/bar/baz', b'''\
 
3224
                              '/foo/bar/baz', '''\
3233
3225
[file:///foo]
3234
3226
[file:///foo/bar]
3235
3227
''')
3236
3228
 
 
3229
 
3237
3230
    def test_no_name_section_included_when_present(self):
3238
3231
        # Note that other tests will cover the case where the no-name section
3239
3232
        # is empty and as such, not included.
3240
3233
        sections = self.assertSectionIDs(['/foo/bar', '/foo', None],
3241
 
                                         '/foo/bar/baz', b'''\
 
3234
                                         '/foo/bar/baz', '''\
3242
3235
option = defined so the no-name section exists
3243
3236
[/foo]
3244
3237
[/foo/bar]
3245
3238
''')
3246
3239
        self.assertEqual(['baz', 'bar/baz', '/foo/bar/baz'],
3247
 
                         [s.locals['relpath'] for _, s in sections])
 
3240
                          [s.locals['relpath'] for _, s in sections])
3248
3241
 
3249
3242
    def test_order_reversed(self):
3250
 
        self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', b'''\
 
3243
        self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
3251
3244
[/foo]
3252
3245
[/foo/bar]
3253
3246
''')
3254
3247
 
3255
3248
    def test_unrelated_section_excluded(self):
3256
 
        self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', b'''\
 
3249
        self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
3257
3250
[/foo]
3258
3251
[/foo/qux]
3259
3252
[/foo/bar]
3261
3254
 
3262
3255
    def test_glob_included(self):
3263
3256
        sections = self.assertSectionIDs(['/foo/*/baz', '/foo/b*', '/foo'],
3264
 
                                         '/foo/bar/baz', b'''\
 
3257
                                         '/foo/bar/baz', '''\
3265
3258
[/foo]
3266
3259
[/foo/qux]
3267
3260
[/foo/b*]
3271
3264
        # nothing really is... as far using {relpath} to append it to something
3272
3265
        # else, this seems good enough though.
3273
3266
        self.assertEqual(['', 'baz', 'bar/baz'],
3274
 
                         [s.locals['relpath'] for _, s in sections])
 
3267
                          [s.locals['relpath'] for _, s in sections])
3275
3268
 
3276
3269
    def test_respect_order(self):
3277
3270
        self.assertSectionIDs(['/foo', '/foo/b*', '/foo/*/baz'],
3278
 
                              '/foo/bar/baz', b'''\
 
3271
                              '/foo/bar/baz', '''\
3279
3272
[/foo/*/baz]
3280
3273
[/foo/qux]
3281
3274
[/foo/b*]
3293
3286
 
3294
3287
    def get_matching_sections(self, name):
3295
3288
        store = self.get_store(self)
3296
 
        store._load_from_string(b'''
 
3289
        store._load_from_string('''
3297
3290
[foo]
3298
3291
option=foo
3299
3292
[foo/baz]
3322
3315
 
3323
3316
    def test_get_first_definition(self):
3324
3317
        store1 = config.IniFileStore()
3325
 
        store1._load_from_string(b'foo=bar')
 
3318
        store1._load_from_string('foo=bar')
3326
3319
        store2 = config.IniFileStore()
3327
 
        store2._load_from_string(b'foo=baz')
 
3320
        store2._load_from_string('foo=baz')
3328
3321
        conf = config.Stack([store1.get_sections, store2.get_sections])
3329
3322
        self.assertEqual('bar', conf.get('foo'))
3330
3323
 
3343
3336
        self.assertEqual(None, conf_stack.get('foo'))
3344
3337
 
3345
3338
    def test_get_for_empty_section_callable(self):
3346
 
        conf_stack = config.Stack([lambda: []])
 
3339
        conf_stack = config.Stack([lambda : []])
3347
3340
        self.assertEqual(None, conf_stack.get('foo'))
3348
3341
 
3349
3342
    def test_get_for_broken_callable(self):
3368
3361
            config.Option('foo', default='bar', override_from_env=['FOO']))
3369
3362
        self.overrideEnv('FOO', 'quux')
3370
3363
        # Env variable provides a default taking over the option one
3371
 
        conf = self.get_conf(b'foo=store')
 
3364
        conf = self.get_conf('foo=store')
3372
3365
        self.assertEqual('quux', conf.get('foo'))
3373
3366
 
3374
3367
    def test_first_override_value_from_env_wins(self):
3381
3374
        self.overrideEnv('FOO', 'foo')
3382
3375
        self.overrideEnv('BAZ', 'baz')
3383
3376
        # The first env var set wins
3384
 
        conf = self.get_conf(b'foo=store')
 
3377
        conf = self.get_conf('foo=store')
3385
3378
        self.assertEqual('foo', conf.get('foo'))
3386
3379
 
3387
3380
 
3388
3381
class TestMemoryStack(tests.TestCase):
3389
3382
 
3390
3383
    def test_get(self):
3391
 
        conf = config.MemoryStack(b'foo=bar')
 
3384
        conf = config.MemoryStack('foo=bar')
3392
3385
        self.assertEqual('bar', conf.get('foo'))
3393
3386
 
3394
3387
    def test_set(self):
3395
 
        conf = config.MemoryStack(b'foo=bar')
 
3388
        conf = config.MemoryStack('foo=bar')
3396
3389
        conf.set('foo', 'baz')
3397
3390
        self.assertEqual('baz', conf.get('foo'))
3398
3391
 
3402
3395
        self.assertFalse(conf.store.is_loaded())
3403
3396
        self.assertRaises(NotImplementedError, conf.get, 'foo')
3404
3397
        # But a content can still be provided
3405
 
        conf.store._load_from_string(b'foo=bar')
 
3398
        conf.store._load_from_string('foo=bar')
3406
3399
        self.assertEqual('bar', conf.get('foo'))
3407
3400
 
3408
3401
 
3415
3408
 
3416
3409
    def test_empty_store(self):
3417
3410
        store = config.IniFileStore()
3418
 
        store._load_from_string(b'')
 
3411
        store._load_from_string('')
3419
3412
        conf = config.Stack([store.get_sections])
3420
3413
        sections = list(conf.iter_sections())
3421
3414
        self.assertLength(0, sections)
3422
3415
 
3423
3416
    def test_simple_store(self):
3424
3417
        store = config.IniFileStore()
3425
 
        store._load_from_string(b'foo=bar')
 
3418
        store._load_from_string('foo=bar')
3426
3419
        conf = config.Stack([store.get_sections])
3427
3420
        tuples = list(conf.iter_sections())
3428
3421
        self.assertLength(1, tuples)
3431
3424
 
3432
3425
    def test_two_stores(self):
3433
3426
        store1 = config.IniFileStore()
3434
 
        store1._load_from_string(b'foo=bar')
 
3427
        store1._load_from_string('foo=bar')
3435
3428
        store2 = config.IniFileStore()
3436
 
        store2._load_from_string(b'bar=qux')
 
3429
        store2._load_from_string('bar=qux')
3437
3430
        conf = config.Stack([store1.get_sections, store2.get_sections])
3438
3431
        tuples = list(conf.iter_sections())
3439
3432
        self.assertLength(2, tuples)
3466
3459
    def test_get_hook(self):
3467
3460
        self.conf.set('foo', 'bar')
3468
3461
        calls = []
3469
 
 
3470
3462
        def hook(*args):
3471
3463
            calls.append(args)
3472
3464
        config.ConfigHooks.install_named_hook('get', hook, None)
3495
3487
 
3496
3488
    def test_get_default_bool_None(self):
3497
3489
        self.register_bool_option('foo')
3498
 
        conf = self.get_conf(b'')
 
3490
        conf = self.get_conf('')
3499
3491
        self.assertEqual(None, conf.get('foo'))
3500
3492
 
3501
3493
    def test_get_default_bool_True(self):
3502
3494
        self.register_bool_option('foo', u'True')
3503
 
        conf = self.get_conf(b'')
 
3495
        conf = self.get_conf('')
3504
3496
        self.assertEqual(True, conf.get('foo'))
3505
3497
 
3506
3498
    def test_get_default_bool_False(self):
3507
3499
        self.register_bool_option('foo', False)
3508
 
        conf = self.get_conf(b'')
 
3500
        conf = self.get_conf('')
3509
3501
        self.assertEqual(False, conf.get('foo'))
3510
3502
 
3511
3503
    def test_get_default_bool_False_as_string(self):
3512
3504
        self.register_bool_option('foo', u'False')
3513
 
        conf = self.get_conf(b'')
 
3505
        conf = self.get_conf('')
3514
3506
        self.assertEqual(False, conf.get('foo'))
3515
3507
 
3516
3508
    def test_get_default_bool_from_env_converted(self):
3517
3509
        self.register_bool_option('foo', u'True', default_from_env=['FOO'])
3518
3510
        self.overrideEnv('FOO', 'False')
3519
 
        conf = self.get_conf(b'')
 
3511
        conf = self.get_conf('')
3520
3512
        self.assertEqual(False, conf.get('foo'))
3521
3513
 
3522
3514
    def test_get_default_bool_when_conversion_fails(self):
3523
3515
        self.register_bool_option('foo', default='True')
3524
 
        conf = self.get_conf(b'foo=invalid boolean')
 
3516
        conf = self.get_conf('foo=invalid boolean')
3525
3517
        self.assertEqual(True, conf.get('foo'))
3526
3518
 
3527
3519
    def register_integer_option(self, name,
3533
3525
 
3534
3526
    def test_get_default_integer_None(self):
3535
3527
        self.register_integer_option('foo')
3536
 
        conf = self.get_conf(b'')
 
3528
        conf = self.get_conf('')
3537
3529
        self.assertEqual(None, conf.get('foo'))
3538
3530
 
3539
3531
    def test_get_default_integer(self):
3540
3532
        self.register_integer_option('foo', 42)
3541
 
        conf = self.get_conf(b'')
 
3533
        conf = self.get_conf('')
3542
3534
        self.assertEqual(42, conf.get('foo'))
3543
3535
 
3544
3536
    def test_get_default_integer_as_string(self):
3545
3537
        self.register_integer_option('foo', u'42')
3546
 
        conf = self.get_conf(b'')
 
3538
        conf = self.get_conf('')
3547
3539
        self.assertEqual(42, conf.get('foo'))
3548
3540
 
3549
3541
    def test_get_default_integer_from_env(self):
3550
3542
        self.register_integer_option('foo', default_from_env=['FOO'])
3551
3543
        self.overrideEnv('FOO', '18')
3552
 
        conf = self.get_conf(b'')
 
3544
        conf = self.get_conf('')
3553
3545
        self.assertEqual(18, conf.get('foo'))
3554
3546
 
3555
3547
    def test_get_default_integer_when_conversion_fails(self):
3556
3548
        self.register_integer_option('foo', default='12')
3557
 
        conf = self.get_conf(b'foo=invalid integer')
 
3549
        conf = self.get_conf('foo=invalid integer')
3558
3550
        self.assertEqual(12, conf.get('foo'))
3559
3551
 
3560
3552
    def register_list_option(self, name, default=None, default_from_env=None):
3564
3556
 
3565
3557
    def test_get_default_list_None(self):
3566
3558
        self.register_list_option('foo')
3567
 
        conf = self.get_conf(b'')
 
3559
        conf = self.get_conf('')
3568
3560
        self.assertEqual(None, conf.get('foo'))
3569
3561
 
3570
3562
    def test_get_default_list_empty(self):
3571
3563
        self.register_list_option('foo', '')
3572
 
        conf = self.get_conf(b'')
 
3564
        conf = self.get_conf('')
3573
3565
        self.assertEqual([], conf.get('foo'))
3574
3566
 
3575
3567
    def test_get_default_list_from_env(self):
3576
3568
        self.register_list_option('foo', default_from_env=['FOO'])
3577
3569
        self.overrideEnv('FOO', '')
3578
 
        conf = self.get_conf(b'')
 
3570
        conf = self.get_conf('')
3579
3571
        self.assertEqual([], conf.get('foo'))
3580
3572
 
3581
3573
    def test_get_with_list_converter_no_item(self):
3582
3574
        self.register_list_option('foo', None)
3583
 
        conf = self.get_conf(b'foo=,')
 
3575
        conf = self.get_conf('foo=,')
3584
3576
        self.assertEqual([], conf.get('foo'))
3585
3577
 
3586
3578
    def test_get_with_list_converter_many_items(self):
3587
3579
        self.register_list_option('foo', None)
3588
 
        conf = self.get_conf(b'foo=m,o,r,e')
 
3580
        conf = self.get_conf('foo=m,o,r,e')
3589
3581
        self.assertEqual(['m', 'o', 'r', 'e'], conf.get('foo'))
3590
3582
 
3591
3583
    def test_get_with_list_converter_embedded_spaces_many_items(self):
3592
3584
        self.register_list_option('foo', None)
3593
 
        conf = self.get_conf(b'foo=" bar", "baz "')
 
3585
        conf = self.get_conf('foo=" bar", "baz "')
3594
3586
        self.assertEqual([' bar', 'baz '], conf.get('foo'))
3595
3587
 
3596
3588
    def test_get_with_list_converter_stripped_spaces_many_items(self):
3597
3589
        self.register_list_option('foo', None)
3598
 
        conf = self.get_conf(b'foo= bar ,  baz ')
 
3590
        conf = self.get_conf('foo= bar ,  baz ')
3599
3591
        self.assertEqual(['bar', 'baz'], conf.get('foo'))
3600
3592
 
3601
3593
 
3624
3616
    def test_two_refs(self):
3625
3617
        self.assertRefs([(False, ''), (True, '{foo}'),
3626
3618
                         (False, ''), (True, '{bar}'),
3627
 
                         (False, ''), ],
 
3619
                         (False, ''),],
3628
3620
                        '{foo}{bar}')
3629
3621
 
3630
3622
    def test_newline_in_refs_are_not_matched(self):
3647
3639
        self.assertExpansion('foo', 'foo')
3648
3640
 
3649
3641
    def test_expand_default_value(self):
3650
 
        self.conf.store._load_from_string(b'bar=baz')
 
3642
        self.conf.store._load_from_string('bar=baz')
3651
3643
        self.registry.register(config.Option('foo', default=u'{bar}'))
3652
3644
        self.assertEqual('baz', self.conf.get('foo', expand=True))
3653
3645
 
3654
3646
    def test_expand_default_from_env(self):
3655
 
        self.conf.store._load_from_string(b'bar=baz')
 
3647
        self.conf.store._load_from_string('bar=baz')
3656
3648
        self.registry.register(config.Option('foo', default_from_env=['FOO']))
3657
3649
        self.overrideEnv('FOO', '{bar}')
3658
3650
        self.assertEqual('baz', self.conf.get('foo', expand=True))
3659
3651
 
3660
3652
    def test_expand_default_on_failed_conversion(self):
3661
 
        self.conf.store._load_from_string(b'baz=bogus\nbar=42\nfoo={baz}')
 
3653
        self.conf.store._load_from_string('baz=bogus\nbar=42\nfoo={baz}')
3662
3654
        self.registry.register(
3663
3655
            config.Option('foo', default=u'{bar}',
3664
3656
                          from_unicode=config.int_from_store))
3668
3660
        self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3669
3661
 
3670
3662
    def test_env_overriding_options(self):
3671
 
        self.conf.store._load_from_string(b'foo=baz')
 
3663
        self.conf.store._load_from_string('foo=baz')
3672
3664
        self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3673
3665
 
3674
3666
    def test_simple_ref(self):
3675
 
        self.conf.store._load_from_string(b'foo=xxx')
 
3667
        self.conf.store._load_from_string('foo=xxx')
3676
3668
        self.assertExpansion('xxx', '{foo}')
3677
3669
 
3678
3670
    def test_unknown_ref(self):
3679
 
        self.assertRaises(config.ExpandingUnknownOption,
 
3671
        self.assertRaises(errors.ExpandingUnknownOption,
3680
3672
                          self.conf.expand_options, '{foo}')
3681
3673
 
3682
3674
    def test_illegal_def_is_ignored(self):
3685
3677
        self.assertExpansion('${Foo,f}', '${Foo,f}')
3686
3678
 
3687
3679
    def test_indirect_ref(self):
3688
 
        self.conf.store._load_from_string(b'''
 
3680
        self.conf.store._load_from_string('''
3689
3681
foo=xxx
3690
3682
bar={foo}
3691
3683
''')
3692
3684
        self.assertExpansion('xxx', '{bar}')
3693
3685
 
3694
3686
    def test_embedded_ref(self):
3695
 
        self.conf.store._load_from_string(b'''
 
3687
        self.conf.store._load_from_string('''
3696
3688
foo=xxx
3697
3689
bar=foo
3698
3690
''')
3699
3691
        self.assertExpansion('xxx', '{{bar}}')
3700
3692
 
3701
3693
    def test_simple_loop(self):
3702
 
        self.conf.store._load_from_string(b'foo={foo}')
3703
 
        self.assertRaises(config.OptionExpansionLoop,
 
3694
        self.conf.store._load_from_string('foo={foo}')
 
3695
        self.assertRaises(errors.OptionExpansionLoop,
3704
3696
                          self.conf.expand_options, '{foo}')
3705
3697
 
3706
3698
    def test_indirect_loop(self):
3707
 
        self.conf.store._load_from_string(b'''
 
3699
        self.conf.store._load_from_string('''
3708
3700
foo={bar}
3709
3701
bar={baz}
3710
3702
baz={foo}''')
3711
 
        e = self.assertRaises(config.OptionExpansionLoop,
 
3703
        e = self.assertRaises(errors.OptionExpansionLoop,
3712
3704
                              self.conf.expand_options, '{foo}')
3713
3705
        self.assertEqual('foo->bar->baz', e.refs)
3714
3706
        self.assertEqual('{foo}', e.string)
3715
3707
 
3716
3708
    def test_list(self):
3717
 
        self.conf.store._load_from_string(b'''
 
3709
        self.conf.store._load_from_string('''
3718
3710
foo=start
3719
3711
bar=middle
3720
3712
baz=end
3723
3715
        self.registry.register(
3724
3716
            config.ListOption('list'))
3725
3717
        self.assertEqual(['start', 'middle', 'end'],
3726
 
                         self.conf.get('list', expand=True))
 
3718
                           self.conf.get('list', expand=True))
3727
3719
 
3728
3720
    def test_cascading_list(self):
3729
 
        self.conf.store._load_from_string(b'''
 
3721
        self.conf.store._load_from_string('''
3730
3722
foo=start,{bar}
3731
3723
bar=middle,{baz}
3732
3724
baz=end
3738
3730
        # option ('list' here).
3739
3731
        self.registry.register(config.ListOption('baz'))
3740
3732
        self.assertEqual(['start', 'middle', 'end'],
3741
 
                         self.conf.get('list', expand=True))
 
3733
                           self.conf.get('list', expand=True))
3742
3734
 
3743
3735
    def test_pathologically_hidden_list(self):
3744
 
        self.conf.store._load_from_string(b'''
 
3736
        self.conf.store._load_from_string('''
3745
3737
foo=bin
3746
3738
bar=go
3747
3739
start={foo
3753
3745
        # only after all expansions have been performed
3754
3746
        self.registry.register(config.ListOption('hidden'))
3755
3747
        self.assertEqual(['bin', 'go'],
3756
 
                         self.conf.get('hidden', expand=True))
 
3748
                          self.conf.get('hidden', expand=True))
3757
3749
 
3758
3750
 
3759
3751
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
3763
3755
 
3764
3756
    def get_config(self, location, string):
3765
3757
        if string is None:
3766
 
            string = b''
 
3758
            string = ''
3767
3759
        # Since we don't save the config we won't strictly require to inherit
3768
3760
        # from TestCaseInTempDir, but an error occurs so quickly...
3769
3761
        c = config.LocationStack(location)
3771
3763
        return c
3772
3764
 
3773
3765
    def test_dont_cross_unrelated_section(self):
3774
 
        c = self.get_config('/another/branch/path', b'''
 
3766
        c = self.get_config('/another/branch/path','''
3775
3767
[/one/branch/path]
3776
3768
foo = hello
3777
3769
bar = {foo}/2
3779
3771
[/another/branch/path]
3780
3772
bar = {foo}/2
3781
3773
''')
3782
 
        self.assertRaises(config.ExpandingUnknownOption,
 
3774
        self.assertRaises(errors.ExpandingUnknownOption,
3783
3775
                          c.get, 'bar', expand=True)
3784
3776
 
3785
3777
    def test_cross_related_sections(self):
3786
 
        c = self.get_config('/project/branch/path', b'''
 
3778
        c = self.get_config('/project/branch/path','''
3787
3779
[/project]
3788
3780
foo = qu
3789
3781
 
3797
3789
 
3798
3790
    def test_cross_global_locations(self):
3799
3791
        l_store = config.LocationStore()
3800
 
        l_store._load_from_string(b'''
 
3792
        l_store._load_from_string('''
3801
3793
[/branch]
3802
3794
lfoo = loc-foo
3803
3795
lbar = {gbar}
3804
3796
''')
3805
3797
        l_store.save()
3806
3798
        g_store = config.GlobalStore()
3807
 
        g_store._load_from_string(b'''
 
3799
        g_store._load_from_string('''
3808
3800
[DEFAULT]
3809
3801
gfoo = {lfoo}
3810
3802
gbar = glob-bar
3819
3811
 
3820
3812
    def test_expand_locals_empty(self):
3821
3813
        l_store = config.LocationStore()
3822
 
        l_store._load_from_string(b'''
 
3814
        l_store._load_from_string('''
3823
3815
[/home/user/project]
3824
3816
base = {basename}
3825
3817
rel = {relpath}
3831
3823
 
3832
3824
    def test_expand_basename_locally(self):
3833
3825
        l_store = config.LocationStore()
3834
 
        l_store._load_from_string(b'''
 
3826
        l_store._load_from_string('''
3835
3827
[/home/user/project]
3836
3828
bfoo = {basename}
3837
3829
''')
3841
3833
 
3842
3834
    def test_expand_basename_locally_longer_path(self):
3843
3835
        l_store = config.LocationStore()
3844
 
        l_store._load_from_string(b'''
 
3836
        l_store._load_from_string('''
3845
3837
[/home/user]
3846
3838
bfoo = {basename}
3847
3839
''')
3851
3843
 
3852
3844
    def test_expand_relpath_locally(self):
3853
3845
        l_store = config.LocationStore()
3854
 
        l_store._load_from_string(b'''
 
3846
        l_store._load_from_string('''
3855
3847
[/home/user/project]
3856
3848
lfoo = loc-foo/{relpath}
3857
3849
''')
3861
3853
 
3862
3854
    def test_expand_relpath_unknonw_in_global(self):
3863
3855
        g_store = config.GlobalStore()
3864
 
        g_store._load_from_string(b'''
 
3856
        g_store._load_from_string('''
3865
3857
[DEFAULT]
3866
3858
gfoo = {relpath}
3867
3859
''')
3868
3860
        g_store.save()
3869
3861
        stack = config.LocationStack('/home/user/project/branch')
3870
 
        self.assertRaises(config.ExpandingUnknownOption,
 
3862
        self.assertRaises(errors.ExpandingUnknownOption,
3871
3863
                          stack.get, 'gfoo', expand=True)
3872
3864
 
3873
3865
    def test_expand_local_option_locally(self):
3874
3866
        l_store = config.LocationStore()
3875
 
        l_store._load_from_string(b'''
 
3867
        l_store._load_from_string('''
3876
3868
[/home/user/project]
3877
3869
lfoo = loc-foo/{relpath}
3878
3870
lbar = {gbar}
3879
3871
''')
3880
3872
        l_store.save()
3881
3873
        g_store = config.GlobalStore()
3882
 
        g_store._load_from_string(b'''
 
3874
        g_store._load_from_string('''
3883
3875
[DEFAULT]
3884
3876
gfoo = {lfoo}
3885
3877
gbar = glob-bar
3893
3885
        """Make sure we chose the right local in presence of several sections.
3894
3886
        """
3895
3887
        l_store = config.LocationStore()
3896
 
        l_store._load_from_string(b'''
 
3888
        l_store._load_from_string('''
3897
3889
[/home/user]
3898
3890
lfoo = loc-foo/{relpath}
3899
3891
[/home/user/project]
3906
3898
        self.assertEqual('loc-foo/bar/baz', stack.get('lfoo', expand=True))
3907
3899
 
3908
3900
 
 
3901
 
3909
3902
class TestStackSet(TestStackWithTransport):
3910
3903
 
3911
3904
    def test_simple_set(self):
3922
3915
 
3923
3916
    def test_set_hook(self):
3924
3917
        calls = []
3925
 
 
3926
3918
        def hook(*args):
3927
3919
            calls.append(args)
3928
3920
        config.ConfigHooks.install_named_hook('set', hook, None)
3949
3941
 
3950
3942
    def test_remove_hook(self):
3951
3943
        calls = []
3952
 
 
3953
3944
        def hook(*args):
3954
3945
            calls.append(args)
3955
3946
        config.ConfigHooks.install_named_hook('remove', hook, None)
3968
3959
        create_configs(self)
3969
3960
 
3970
3961
    def test_no_variable(self):
3971
 
        # Using branch should query branch, locations and breezy
 
3962
        # Using branch should query branch, locations and bazaar
3972
3963
        self.assertOptions([], self.branch_config)
3973
3964
 
3974
 
    def test_option_in_breezy(self):
3975
 
        self.breezy_config.set_user_option('file', 'breezy')
3976
 
        self.assertOptions([('file', 'breezy', 'DEFAULT', 'breezy')],
3977
 
                           self.breezy_config)
 
3965
    def test_option_in_bazaar(self):
 
3966
        self.bazaar_config.set_user_option('file', 'bazaar')
 
3967
        self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
 
3968
                           self.bazaar_config)
3978
3969
 
3979
3970
    def test_option_in_locations(self):
3980
3971
        self.locations_config.set_user_option('file', 'locations')
3987
3978
        self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
3988
3979
                           self.branch_config)
3989
3980
 
3990
 
    def test_option_in_breezy_and_branch(self):
3991
 
        self.breezy_config.set_user_option('file', 'breezy')
 
3981
    def test_option_in_bazaar_and_branch(self):
 
3982
        self.bazaar_config.set_user_option('file', 'bazaar')
3992
3983
        self.branch_config.set_user_option('file', 'branch')
3993
3984
        self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
3994
 
                            ('file', 'breezy', 'DEFAULT', 'breezy'), ],
 
3985
                            ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3995
3986
                           self.branch_config)
3996
3987
 
3997
3988
    def test_option_in_branch_and_locations(self):
4000
3991
        self.branch_config.set_user_option('file', 'branch')
4001
3992
        self.assertOptions(
4002
3993
            [('file', 'locations', self.tree.basedir, 'locations'),
4003
 
             ('file', 'branch', 'DEFAULT', 'branch'), ],
 
3994
             ('file', 'branch', 'DEFAULT', 'branch'),],
4004
3995
            self.branch_config)
4005
3996
 
4006
 
    def test_option_in_breezy_locations_and_branch(self):
4007
 
        self.breezy_config.set_user_option('file', 'breezy')
 
3997
    def test_option_in_bazaar_locations_and_branch(self):
 
3998
        self.bazaar_config.set_user_option('file', 'bazaar')
4008
3999
        self.locations_config.set_user_option('file', 'locations')
4009
4000
        self.branch_config.set_user_option('file', 'branch')
4010
4001
        self.assertOptions(
4011
4002
            [('file', 'locations', self.tree.basedir, 'locations'),
4012
4003
             ('file', 'branch', 'DEFAULT', 'branch'),
4013
 
             ('file', 'breezy', 'DEFAULT', 'breezy'), ],
 
4004
             ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4014
4005
            self.branch_config)
4015
4006
 
4016
4007
 
4024
4015
        self.locations_config.remove_user_option('file', self.tree.basedir)
4025
4016
        self.assertOptions(
4026
4017
            [('file', 'branch', 'DEFAULT', 'branch'),
4027
 
             ('file', 'breezy', 'DEFAULT', 'breezy'), ],
 
4018
             ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4028
4019
            self.branch_config)
4029
4020
 
4030
4021
    def test_remove_in_branch(self):
4031
4022
        self.branch_config.remove_user_option('file')
4032
4023
        self.assertOptions(
4033
4024
            [('file', 'locations', self.tree.basedir, 'locations'),
4034
 
             ('file', 'breezy', 'DEFAULT', 'breezy'), ],
 
4025
             ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4035
4026
            self.branch_config)
4036
4027
 
4037
 
    def test_remove_in_breezy(self):
4038
 
        self.breezy_config.remove_user_option('file')
 
4028
    def test_remove_in_bazaar(self):
 
4029
        self.bazaar_config.remove_user_option('file')
4039
4030
        self.assertOptions(
4040
4031
            [('file', 'locations', self.tree.basedir, 'locations'),
4041
 
             ('file', 'branch', 'DEFAULT', 'branch'), ],
 
4032
             ('file', 'branch', 'DEFAULT', 'branch'),],
4042
4033
            self.branch_config)
4043
4034
 
4044
4035
 
4064
4055
        self.assertLength(len(expected), sections)
4065
4056
        self.assertEqual(expected, [n for n, _, _ in sections])
4066
4057
 
4067
 
    def test_breezy_default_section(self):
4068
 
        self.assertSectionNames(['DEFAULT'], self.breezy_config)
 
4058
    def test_bazaar_default_section(self):
 
4059
        self.assertSectionNames(['DEFAULT'], self.bazaar_config)
4069
4060
 
4070
4061
    def test_locations_default_section(self):
4071
4062
        # No sections are defined in an empty file
4105
4096
        self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
4106
4097
                                self.branch_config)
4107
4098
 
4108
 
    def test_breezy_named_section(self):
 
4099
    def test_bazaar_named_section(self):
4109
4100
        # We need to cheat as the API doesn't give direct access to sections
4110
4101
        # other than DEFAULT.
4111
 
        self.breezy_config.set_alias('breezy', 'bzr')
4112
 
        self.assertSectionNames(['ALIASES'], self.breezy_config, 'ALIASES')
 
4102
        self.bazaar_config.set_alias('bazaar', 'bzr')
 
4103
        self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
4113
4104
 
4114
4105
 
4115
4106
class TestSharedStores(tests.TestCaseInTempDir):
4116
4107
 
4117
 
    def test_breezy_conf_shared(self):
 
4108
    def test_bazaar_conf_shared(self):
4118
4109
        g1 = config.GlobalStack()
4119
4110
        g2 = config.GlobalStack()
4120
4111
        # The two stacks share the same store
4121
4112
        self.assertIs(g1.store, g2.store)
4122
4113
 
4123
4114
 
4124
 
class TestAuthenticationConfigFilePermissions(tests.TestCaseInTempDir):
4125
 
    """Test warning for permissions of authentication.conf."""
4126
 
 
4127
 
    def setUp(self):
4128
 
        super(TestAuthenticationConfigFilePermissions, self).setUp()
4129
 
        self.path = osutils.pathjoin(self.test_dir, 'authentication.conf')
4130
 
        with open(self.path, 'wb') as f:
4131
 
            f.write(b"""[broken]
4132
 
scheme=ftp
4133
 
user=joe
4134
 
port=port # Error: Not an int
4135
 
""")
4136
 
        self.overrideAttr(bedding, 'authentication_config_path',
4137
 
                          lambda: self.path)
4138
 
        osutils.chmod_if_possible(self.path, 0o755)
4139
 
 
4140
 
    def test_check_warning(self):
4141
 
        conf = config.AuthenticationConfig()
4142
 
        self.assertEqual(conf._filename, self.path)
4143
 
        self.assertContainsRe(self.get_log(),
4144
 
                              'Saved passwords may be accessible by other users.')
4145
 
 
4146
 
    def test_check_suppressed_warning(self):
4147
 
        global_config = config.GlobalConfig()
4148
 
        global_config.set_user_option('suppress_warnings',
4149
 
                                      'insecure_permissions')
4150
 
        conf = config.AuthenticationConfig()
4151
 
        self.assertEqual(conf._filename, self.path)
4152
 
        self.assertNotContainsRe(self.get_log(),
4153
 
                                 'Saved passwords may be accessible by other users.')
4154
 
 
4155
 
 
4156
4115
class TestAuthenticationConfigFile(tests.TestCase):
4157
4116
    """Test the authentication.conf file matching"""
4158
4117
 
4175
4134
 
4176
4135
    def test_non_utf8_config(self):
4177
4136
        conf = config.AuthenticationConfig(_file=BytesIO(b'foo = bar\xff'))
4178
 
        self.assertRaises(config.ConfigContentError, conf._get_config)
 
4137
        self.assertRaises(errors.ConfigContentError, conf._get_config)
4179
4138
 
4180
4139
    def test_missing_auth_section_header(self):
4181
4140
        conf = config.AuthenticationConfig(_file=BytesIO(b'foo = bar'))
4183
4142
 
4184
4143
    def test_auth_section_header_not_closed(self):
4185
4144
        conf = config.AuthenticationConfig(_file=BytesIO(b'[DEF'))
4186
 
        self.assertRaises(config.ParseConfigError, conf._get_config)
 
4145
        self.assertRaises(errors.ParseConfigError, conf._get_config)
4187
4146
 
4188
4147
    def test_auth_value_not_boolean(self):
4189
4148
        conf = config.AuthenticationConfig(_file=BytesIO(b"""\
4307
4266
                              conf, 'http', host='bar.org', path='/dir2')
4308
4267
        # matching subdir
4309
4268
        self._got_user_passwd('jim', 'jimpass',
4310
 
                              conf, 'http', host='bar.org', path='/dir1/subdir')
 
4269
                              conf, 'http', host='bar.org',path='/dir1/subdir')
4311
4270
 
4312
4271
    def test_credentials_for_user(self):
4313
4272
        conf = config.AuthenticationConfig(_file=BytesIO(b"""
4363
4322
    def test_set_credentials(self):
4364
4323
        conf = config.AuthenticationConfig()
4365
4324
        conf.set_credentials('name', 'host', 'user', 'scheme', 'password',
4366
 
                             99, path='/foo', verify_certificates=False, realm='realm')
 
4325
        99, path='/foo', verify_certificates=False, realm='realm')
4367
4326
        credentials = conf.get_credentials(host='host', scheme='scheme',
4368
4327
                                           port=99, path='/foo',
4369
4328
                                           realm='realm')
4389
4348
        self.assertEqual(CREDENTIALS, credentials)
4390
4349
 
4391
4350
 
4392
 
class TestAuthenticationConfig(tests.TestCaseInTempDir):
 
4351
class TestAuthenticationConfig(tests.TestCase):
4393
4352
    """Test AuthenticationConfig behaviour"""
4394
4353
 
4395
4354
    def _check_default_password_prompt(self, expected_prompt_format, scheme,
4406
4365
        # We use an empty conf so that the user is always prompted
4407
4366
        conf = config.AuthenticationConfig()
4408
4367
        self.assertEqual(password,
4409
 
                         conf.get_password(scheme, host, user, port=port,
4410
 
                                           realm=realm, path=path))
 
4368
                          conf.get_password(scheme, host, user, port=port,
 
4369
                                            realm=realm, path=path))
4411
4370
        self.assertEqual(expected_prompt, ui.ui_factory.stderr.getvalue())
4412
4371
        self.assertEqual('', ui.ui_factory.stdout.getvalue())
4413
4372
 
4420
4379
        expected_prompt = expected_prompt_format % {
4421
4380
            'scheme': scheme, 'host': host, 'port': port,
4422
4381
            'realm': realm}
4423
 
        ui.ui_factory = tests.TestUIFactory(stdin=username + '\n')
 
4382
        ui.ui_factory = tests.TestUIFactory(stdin=username+ '\n')
4424
4383
        # We use an empty conf so that the user is always prompted
4425
4384
        conf = config.AuthenticationConfig()
4426
4385
        self.assertEqual(username, conf.get_user(scheme, host, port=port,
4427
 
                                                 realm=realm, path=path, ask=True))
 
4386
                          realm=realm, path=path, ask=True))
4428
4387
        self.assertEqual(expected_prompt, ui.ui_factory.stderr.getvalue())
4429
4388
        self.assertEqual('', ui.ui_factory.stdout.getvalue())
4430
4389
 
4439
4398
    def test_username_default_no_prompt(self):
4440
4399
        conf = config.AuthenticationConfig()
4441
4400
        self.assertEqual(None,
4442
 
                         conf.get_user('ftp', 'example.com'))
 
4401
            conf.get_user('ftp', 'example.com'))
4443
4402
        self.assertEqual("explicitdefault",
4444
 
                         conf.get_user('ftp', 'example.com', default="explicitdefault"))
 
4403
            conf.get_user('ftp', 'example.com', default="explicitdefault"))
4445
4404
 
4446
4405
    def test_password_default_prompts(self):
4447
4406
        # HTTP prompts can't be tested here, see test_http.py
4476
4435
        # Since the password defined in the authentication config is ignored,
4477
4436
        # the user is prompted
4478
4437
        self.assertEqual(entered_password,
4479
 
                         conf.get_password('ssh', 'bar.org', user='jim'))
 
4438
                          conf.get_password('ssh', 'bar.org', user='jim'))
4480
4439
        self.assertContainsRe(
4481
4440
            self.get_log(),
4482
 
            'password ignored in section \\[ssh with password\\]')
 
4441
            'password ignored in section \[ssh with password\]')
4483
4442
 
4484
4443
    def test_ssh_without_password_doesnt_emit_warning(self):
4485
4444
        conf = config.AuthenticationConfig(_file=BytesIO(b"""
4494
4453
        # Since the password defined in the authentication config is ignored,
4495
4454
        # the user is prompted
4496
4455
        self.assertEqual(entered_password,
4497
 
                         conf.get_password('ssh', 'bar.org', user='jim'))
 
4456
                          conf.get_password('ssh', 'bar.org', user='jim'))
4498
4457
        # No warning shoud be emitted since there is no password. We are only
4499
4458
        # providing "user".
4500
4459
        self.assertNotContainsRe(
4501
4460
            self.get_log(),
4502
 
            'password ignored in section \\[ssh with password\\]')
 
4461
            'password ignored in section \[ssh with password\]')
4503
4462
 
4504
4463
    def test_uses_fallback_stores(self):
4505
4464
        self.overrideAttr(config, 'credential_store_registry',
4524
4483
        self._password[(scheme, host)] = password
4525
4484
 
4526
4485
    def get_credentials(self, scheme, host, port=None, user=None,
4527
 
                        path=None, realm=None):
 
4486
        path=None, realm=None):
4528
4487
        key = (scheme, host)
4529
 
        if key not in self._username:
 
4488
        if not key in self._username:
4530
4489
            return None
4531
 
        return {"scheme": scheme, "host": host, "port": port,
 
4490
        return { "scheme": scheme, "host": host, "port": port,
4532
4491
                "user": self._username[key], "password": self._password[key]}
4533
4492
 
4534
4493
 
4538
4497
        self._calls = 0
4539
4498
 
4540
4499
    def get_credentials(self, scheme, host, port=None, user=None,
4541
 
                        path=None, realm=None):
 
4500
        path=None, realm=None):
4542
4501
        self._calls += 1
4543
4502
        return None
4544
4503
 
4562
4521
    def test_fallback_none_registered(self):
4563
4522
        r = config.CredentialStoreRegistry()
4564
4523
        self.assertEqual(None,
4565
 
                         r.get_fallback_credentials("http", "example.com"))
 
4524
                          r.get_fallback_credentials("http", "example.com"))
4566
4525
 
4567
4526
    def test_register(self):
4568
4527
        r = config.CredentialStoreRegistry()
4590
4549
        store = CountingCredentialStore()
4591
4550
        r.register("count", store, fallback=False)
4592
4551
        self.assertEqual(None,
4593
 
                         r.get_fallback_credentials("http", "example.com"))
 
4552
                          r.get_fallback_credentials("http", "example.com"))
4594
4553
        self.assertEqual(0, store._calls)
4595
4554
 
4596
4555
    def test_fallback_credentials(self):
4633
4592
        r = config.credential_store_registry
4634
4593
        plain_text = r.get_credential_store('base64')
4635
4594
        decoded = plain_text.decode_password(dict(password='c2VjcmV0'))
4636
 
        self.assertEqual(b'secret', decoded)
 
4595
        self.assertEqual('secret', decoded)
4637
4596
 
4638
4597
 
4639
4598
# FIXME: Once we have a way to declare authentication to all test servers, we
4648
4607
    pass
4649
4608
 
4650
4609
 
 
4610
class TestAutoUserId(tests.TestCase):
 
4611
    """Test inferring an automatic user name."""
 
4612
 
 
4613
    def test_auto_user_id(self):
 
4614
        """Automatic inference of user name.
 
4615
 
 
4616
        This is a bit hard to test in an isolated way, because it depends on
 
4617
        system functions that go direct to /etc or perhaps somewhere else.
 
4618
        But it's reasonable to say that on Unix, with an /etc/mailname, we ought
 
4619
        to be able to choose a user name with no configuration.
 
4620
        """
 
4621
        if sys.platform == 'win32':
 
4622
            raise tests.TestSkipped(
 
4623
                "User name inference not implemented on win32")
 
4624
        realname, address = config._auto_user_id()
 
4625
        if os.path.exists('/etc/mailname'):
 
4626
            self.assertIsNot(None, realname)
 
4627
            self.assertIsNot(None, address)
 
4628
        else:
 
4629
            self.assertEqual((None, None), (realname, address))
 
4630
 
 
4631
 
 
4632
class TestDefaultMailDomain(tests.TestCaseInTempDir):
 
4633
    """Test retrieving default domain from mailname file"""
 
4634
 
 
4635
    def test_default_mail_domain_simple(self):
 
4636
        f = file('simple', 'w')
 
4637
        try:
 
4638
            f.write("domainname.com\n")
 
4639
        finally:
 
4640
            f.close()
 
4641
        r = config._get_default_mail_domain('simple')
 
4642
        self.assertEqual('domainname.com', r)
 
4643
 
 
4644
    def test_default_mail_domain_no_eol(self):
 
4645
        f = file('no_eol', 'w')
 
4646
        try:
 
4647
            f.write("domainname.com")
 
4648
        finally:
 
4649
            f.close()
 
4650
        r = config._get_default_mail_domain('no_eol')
 
4651
        self.assertEqual('domainname.com', r)
 
4652
 
 
4653
    def test_default_mail_domain_multiple_lines(self):
 
4654
        f = file('multiple_lines', 'w')
 
4655
        try:
 
4656
            f.write("domainname.com\nsome other text\n")
 
4657
        finally:
 
4658
            f.close()
 
4659
        r = config._get_default_mail_domain('multiple_lines')
 
4660
        self.assertEqual('domainname.com', r)
 
4661
 
 
4662
 
4651
4663
class EmailOptionTests(tests.TestCase):
4652
4664
 
4653
4665
    def test_default_email_uses_BRZ_EMAIL(self):
4654
 
        conf = config.MemoryStack(b'email=jelmer@debian.org')
4655
 
        # BRZ_EMAIL takes precedence over BZR_EMAIL and EMAIL
 
4666
        conf = config.MemoryStack('email=jelmer@debian.org')
 
4667
        # BRZ_EMAIL takes precedence over EMAIL
4656
4668
        self.overrideEnv('BRZ_EMAIL', 'jelmer@samba.org')
4657
 
        self.overrideEnv('BZR_EMAIL', 'jelmer@jelmer.uk')
4658
 
        self.overrideEnv('EMAIL', 'jelmer@apache.org')
4659
 
        self.assertEqual('jelmer@samba.org', conf.get('email'))
4660
 
 
4661
 
    def test_default_email_uses_BZR_EMAIL(self):
4662
 
        conf = config.MemoryStack(b'email=jelmer@debian.org')
4663
 
        # BZR_EMAIL takes precedence over EMAIL
4664
 
        self.overrideEnv('BZR_EMAIL', 'jelmer@samba.org')
4665
4669
        self.overrideEnv('EMAIL', 'jelmer@apache.org')
4666
4670
        self.assertEqual('jelmer@samba.org', conf.get('email'))
4667
4671
 
4668
4672
    def test_default_email_uses_EMAIL(self):
4669
 
        conf = config.MemoryStack(b'')
 
4673
        conf = config.MemoryStack('')
4670
4674
        self.overrideEnv('BRZ_EMAIL', None)
4671
4675
        self.overrideEnv('EMAIL', 'jelmer@apache.org')
4672
4676
        self.assertEqual('jelmer@apache.org', conf.get('email'))
4673
4677
 
4674
4678
    def test_BRZ_EMAIL_overrides(self):
4675
 
        conf = config.MemoryStack(b'email=jelmer@debian.org')
 
4679
        conf = config.MemoryStack('email=jelmer@debian.org')
4676
4680
        self.overrideEnv('BRZ_EMAIL', 'jelmer@apache.org')
4677
4681
        self.assertEqual('jelmer@apache.org', conf.get('email'))
4678
4682
        self.overrideEnv('BRZ_EMAIL', None)
4683
4687
class MailClientOptionTests(tests.TestCase):
4684
4688
 
4685
4689
    def test_default(self):
4686
 
        conf = config.MemoryStack(b'')
 
4690
        conf = config.MemoryStack('')
4687
4691
        client = conf.get('mail_client')
4688
4692
        self.assertIs(client, mail_client.DefaultMail)
4689
4693
 
4690
4694
    def test_evolution(self):
4691
 
        conf = config.MemoryStack(b'mail_client=evolution')
 
4695
        conf = config.MemoryStack('mail_client=evolution')
4692
4696
        client = conf.get('mail_client')
4693
4697
        self.assertIs(client, mail_client.Evolution)
4694
4698
 
4695
4699
    def test_kmail(self):
4696
 
        conf = config.MemoryStack(b'mail_client=kmail')
 
4700
        conf = config.MemoryStack('mail_client=kmail')
4697
4701
        client = conf.get('mail_client')
4698
4702
        self.assertIs(client, mail_client.KMail)
4699
4703
 
4700
4704
    def test_mutt(self):
4701
 
        conf = config.MemoryStack(b'mail_client=mutt')
 
4705
        conf = config.MemoryStack('mail_client=mutt')
4702
4706
        client = conf.get('mail_client')
4703
4707
        self.assertIs(client, mail_client.Mutt)
4704
4708
 
4705
4709
    def test_thunderbird(self):
4706
 
        conf = config.MemoryStack(b'mail_client=thunderbird')
 
4710
        conf = config.MemoryStack('mail_client=thunderbird')
4707
4711
        client = conf.get('mail_client')
4708
4712
        self.assertIs(client, mail_client.Thunderbird)
4709
4713
 
4710
4714
    def test_explicit_default(self):
4711
 
        conf = config.MemoryStack(b'mail_client=default')
 
4715
        conf = config.MemoryStack('mail_client=default')
4712
4716
        client = conf.get('mail_client')
4713
4717
        self.assertIs(client, mail_client.DefaultMail)
4714
4718
 
4715
4719
    def test_editor(self):
4716
 
        conf = config.MemoryStack(b'mail_client=editor')
 
4720
        conf = config.MemoryStack('mail_client=editor')
4717
4721
        client = conf.get('mail_client')
4718
4722
        self.assertIs(client, mail_client.Editor)
4719
4723
 
4720
4724
    def test_mapi(self):
4721
 
        conf = config.MemoryStack(b'mail_client=mapi')
 
4725
        conf = config.MemoryStack('mail_client=mapi')
4722
4726
        client = conf.get('mail_client')
4723
4727
        self.assertIs(client, mail_client.MAPIClient)
4724
4728
 
4725
4729
    def test_xdg_email(self):
4726
 
        conf = config.MemoryStack(b'mail_client=xdg-email')
 
4730
        conf = config.MemoryStack('mail_client=xdg-email')
4727
4731
        client = conf.get('mail_client')
4728
4732
        self.assertIs(client, mail_client.XDGEmail)
4729
4733
 
4730
4734
    def test_unknown(self):
4731
 
        conf = config.MemoryStack(b'mail_client=firebird')
4732
 
        self.assertRaises(config.ConfigOptionValueError, conf.get,
4733
 
                          'mail_client')
 
4735
        conf = config.MemoryStack('mail_client=firebird')
 
4736
        self.assertRaises(errors.ConfigOptionValueError, conf.get,
 
4737
                'mail_client')