/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 brzlib/tests/test_config.py

  • Committer: Jelmer Vernooij
  • Date: 2017-05-21 12:41:27 UTC
  • mto: This revision was merged to the branch mainline in revision 6623.
  • Revision ID: jelmer@jelmer.uk-20170521124127-iv8etg0vwymyai6y
s/bzr/brz/ in apport config.

Show diffs side-by-side

added added

removed removed

Lines of Context:
16
16
 
17
17
"""Tests for finding and reading the bzr config file[s]."""
18
18
 
 
19
from cStringIO import StringIO
19
20
from textwrap import dedent
20
 
from io import BytesIO
21
21
import os
22
22
import sys
23
23
import threading
24
24
 
25
 
import configobj
26
25
from testtools import matchers
27
26
 
28
 
from .. import (
 
27
from brzlib 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 brzlib.symbol_versioning import (
 
43
    deprecated_in,
46
44
    )
47
 
from ..transport import remote as transport_remote
48
 
from . import (
 
45
from brzlib.transport import remote as transport_remote
 
46
from brzlib.tests import (
49
47
    features,
50
48
    scenarios,
51
49
    test_server,
52
50
    )
 
51
from brzlib.util.configobj import configobj
53
52
 
54
53
 
55
54
def lockable_config_scenarios():
61
60
        ('locations',
62
61
         {'config_class': config.LocationConfig,
63
62
          'config_args': ['.'],
64
 
          'config_section': '.'}), ]
 
63
          'config_section': '.'}),]
65
64
 
66
65
 
67
66
load_tests = scenarios.load_tests_apply_scenarios
71
70
    'configobj', lambda test: config.TransportIniFileStore(
72
71
        test.get_transport(), 'configobj.conf'))
73
72
config.test_store_builder_registry.register(
74
 
    'breezy', lambda test: config.GlobalStore())
 
73
    'bazaar', lambda test: config.GlobalStore())
75
74
config.test_store_builder_registry.register(
76
75
    'location', lambda test: config.LocationStore())
77
76
 
110
109
    build_backing_branch(test, 'branch')
111
110
    b = branch.Branch.open('branch')
112
111
    return config.BranchStore(b)
113
 
 
114
 
 
115
112
config.test_store_builder_registry.register('branch', build_branch_store)
116
113
 
117
114
 
119
116
    build_backing_branch(test, 'branch')
120
117
    b = controldir.ControlDir.open('branch')
121
118
    return config.ControlStore(b)
122
 
 
123
 
 
124
119
config.test_store_builder_registry.register('control', build_control_store)
125
120
 
126
121
 
132
127
    build_backing_branch(test, 'branch', transport_class, server_class)
133
128
    b = branch.Branch.open(test.get_url('branch'))
134
129
    return config.BranchStore(b)
135
 
 
136
 
 
137
130
config.test_store_builder_registry.register('remote_branch',
138
131
                                            build_remote_branch_store)
139
132
 
140
133
 
141
134
config.test_stack_builder_registry.register(
142
 
    'breezy', lambda test: config.GlobalStack())
 
135
    'bazaar', lambda test: config.GlobalStack())
143
136
config.test_stack_builder_registry.register(
144
137
    'location', lambda test: config.LocationStack('.'))
145
138
 
148
141
    build_backing_branch(test, 'branch')
149
142
    b = branch.Branch.open('branch')
150
143
    return config.BranchStack(b)
151
 
 
152
 
 
153
144
config.test_stack_builder_registry.register('branch', build_branch_stack)
154
145
 
155
146
 
161
152
    build_backing_branch(test, 'branch', transport_class, server_class)
162
153
    b = branch.Branch.open(test.get_url('branch'))
163
154
    return config.BranchOnlyStack(b)
164
 
 
165
 
 
166
155
config.test_stack_builder_registry.register('branch_only',
167
156
                                            build_branch_only_stack)
168
157
 
169
 
 
170
158
def build_remote_control_stack(test):
171
159
    # There is only one permutation (but we won't be able to handle more with
172
160
    # this design anyway)
176
164
    # creating a dedicated helper to create only the bzrdir
177
165
    build_backing_branch(test, 'branch', transport_class, server_class)
178
166
    b = branch.Branch.open(test.get_url('branch'))
179
 
    return config.RemoteControlStack(b.controldir)
180
 
 
181
 
 
 
167
    return config.RemoteControlStack(b.bzrdir)
182
168
config.test_stack_builder_registry.register('remote_control',
183
169
                                            build_remote_control_stack)
184
170
 
185
171
 
186
 
sample_long_alias = "log -r-15..-1 --line"
 
172
sample_long_alias="log -r-15..-1 --line"
187
173
sample_config_text = u"""
188
174
[DEFAULT]
189
175
email=Erik B\u00e5gfors <erik@bagfors.nu>
190
176
editor=vim
191
 
change_editor=vimdiff -of {new_path} {old_path}
 
177
change_editor=vimdiff -of @new_path @old_path
 
178
gpg_signing_command=gnome-gpg
192
179
gpg_signing_key=DD4D5088
193
180
log_format=short
194
181
validate_signatures_in_log=true
200
187
bzr.default_mergetool=sometool
201
188
[ALIASES]
202
189
h=help
203
 
ll=""".encode('utf-8') + sample_long_alias.encode('utf-8') + b"\n"
204
 
 
205
 
 
206
 
sample_always_signatures = b"""
 
190
ll=""" + sample_long_alias + "\n"
 
191
 
 
192
 
 
193
sample_always_signatures = """
207
194
[DEFAULT]
208
195
check_signatures=ignore
209
196
create_signatures=always
210
197
"""
211
198
 
212
 
sample_ignore_signatures = b"""
 
199
sample_ignore_signatures = """
213
200
[DEFAULT]
214
201
check_signatures=require
215
202
create_signatures=never
216
203
"""
217
204
 
218
 
sample_maybe_signatures = b"""
 
205
sample_maybe_signatures = """
219
206
[DEFAULT]
220
207
check_signatures=ignore
221
208
create_signatures=when-required
222
209
"""
223
210
 
224
 
sample_branches_text = b"""
 
211
sample_branches_text = """
225
212
[http://www.example.com]
226
213
# Top level policy
227
214
email=Robert Collins <robertc@example.org>
244
231
# test trailing / matching with no children
245
232
[/a/]
246
233
check_signatures=check-available
 
234
gpg_signing_command=false
247
235
gpg_signing_key=default
248
236
user_local_option=local
249
237
# test trailing / matching
251
239
#subdirs will match but not the parent
252
240
[/a/c]
253
241
check_signatures=ignore
254
 
post_commit=breezy.tests.test_config.post_commit
 
242
post_commit=brzlib.tests.test_config.post_commit
255
243
#testing explicit beats globs
256
244
"""
257
245
 
266
254
 
267
255
    - locations_config : A LocationConfig for the associated branch
268
256
 
269
 
    - breezy_config: A GlobalConfig.
 
257
    - bazaar_config: A GlobalConfig.
270
258
 
271
259
    The tree and branch are created in a 'tree' subdirectory so the tests can
272
260
    still use the test directory to stay outside of the branch.
275
263
    test.tree = tree
276
264
    test.branch_config = config.BranchConfig(tree.branch)
277
265
    test.locations_config = config.LocationConfig(tree.basedir)
278
 
    test.breezy_config = config.GlobalConfig()
 
266
    test.bazaar_config = config.GlobalConfig()
279
267
 
280
268
 
281
269
def create_configs_with_file_option(test):
285
273
    configuration with a value which allows identifying the configuration file.
286
274
    """
287
275
    create_configs(test)
288
 
    test.breezy_config.set_user_option('file', 'breezy')
 
276
    test.bazaar_config.set_user_option('file', 'bazaar')
289
277
    test.locations_config.set_user_option('file', 'locations')
290
278
    test.branch_config.set_user_option('file', 'branch')
291
279
 
352
340
        return config.TransportConfig(self._transport, 'branch.conf')
353
341
 
354
342
    def lock_write(self):
355
 
        return lock.LogicalLockResult(self.unlock)
 
343
        pass
356
344
 
357
345
    def unlock(self):
358
346
        pass
367
355
    def get(self, filename):
368
356
        # from Transport
369
357
        try:
370
 
            return BytesIO(self.files[filename])
 
358
            return StringIO(self.files[filename])
371
359
        except KeyError:
372
360
            raise errors.NoSuchFile(filename)
373
361
 
392
380
        super(InstrumentedConfig, self).__init__()
393
381
        self._calls = []
394
382
        self._signatures = config.CHECK_NEVER
395
 
        self._change_editor = 'vimdiff -fo {new_path} {old_path}'
396
383
 
397
384
    def _get_user_id(self):
398
385
        self._calls.append('_get_user_id')
404
391
 
405
392
    def _get_change_editor(self):
406
393
        self._calls.append('_get_change_editor')
407
 
        return self._change_editor
408
 
 
409
 
 
410
 
bool_config = b"""[DEFAULT]
 
394
        return 'vimdiff -fo @new_path @old_path'
 
395
 
 
396
 
 
397
bool_config = """[DEFAULT]
411
398
active = true
412
399
inactive = false
413
400
[UPPERCASE]
419
406
class TestConfigObj(tests.TestCase):
420
407
 
421
408
    def test_get_bool(self):
422
 
        co = config.ConfigObj(BytesIO(bool_config))
 
409
        co = config.ConfigObj(StringIO(bool_config))
423
410
        self.assertIs(co.get_bool('DEFAULT', 'active'), True)
424
411
        self.assertIs(co.get_bool('DEFAULT', 'inactive'), False)
425
412
        self.assertIs(co.get_bool('UPPERCASE', 'active'), True)
432
419
        """
433
420
        co = config.ConfigObj()
434
421
        co['test'] = 'foo#bar'
435
 
        outfile = BytesIO()
 
422
        outfile = StringIO()
436
423
        co.write(outfile=outfile)
437
424
        lines = outfile.getvalue().splitlines()
438
 
        self.assertEqual(lines, [b'test = "foo#bar"'])
 
425
        self.assertEqual(lines, ['test = "foo#bar"'])
439
426
        co2 = config.ConfigObj(lines)
440
427
        self.assertEqual(co2['test'], 'foo#bar')
441
428
 
456
443
        # This issue only affects test, but it's better to avoid
457
444
        # `co.write()` construct at all.
458
445
        # [bialix 20110222] bug report sent to ConfigObj's author
459
 
        outfile = BytesIO()
 
446
        outfile = StringIO()
460
447
        co.write(outfile=outfile)
461
448
        output = outfile.getvalue()
462
449
        # now we're trying to read it back
463
 
        co2 = config.ConfigObj(BytesIO(output))
 
450
        co2 = config.ConfigObj(StringIO(output))
464
451
        self.assertEqual(triple_quotes_value, co2['test'])
465
452
 
466
453
 
467
 
erroneous_config = b"""[section] # line 1
 
454
erroneous_config = """[section] # line 1
468
455
good=good # line 2
469
456
[section] # line 3
470
457
whocares=notme # line 4
475
462
 
476
463
    def test_duplicate_section_name_error_line(self):
477
464
        try:
478
 
            co = configobj.ConfigObj(BytesIO(erroneous_config),
 
465
            co = configobj.ConfigObj(StringIO(erroneous_config),
479
466
                                     raise_errors=True)
480
 
        except config.configobj.DuplicateError as e:
 
467
        except config.configobj.DuplicateError, e:
481
468
            self.assertEqual(3, e.line_number)
482
469
        else:
483
470
            self.fail('Error in config file not detected')
499
486
                         my_config.username())
500
487
        self.assertEqual(['_get_user_id'], my_config._calls)
501
488
 
 
489
    def test_signatures_default(self):
 
490
        my_config = config.Config()
 
491
        self.assertFalse(
 
492
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
493
                my_config.signature_needed))
 
494
        self.assertEqual(config.CHECK_IF_POSSIBLE,
 
495
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
496
                my_config.signature_checking))
 
497
        self.assertEqual(config.SIGN_WHEN_REQUIRED,
 
498
                self.applyDeprecated(deprecated_in((2, 5, 0)),
 
499
                    my_config.signing_policy))
 
500
 
 
501
    def test_signatures_template_method(self):
 
502
        my_config = InstrumentedConfig()
 
503
        self.assertEqual(config.CHECK_NEVER,
 
504
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
505
                my_config.signature_checking))
 
506
        self.assertEqual(['_get_signature_checking'], my_config._calls)
 
507
 
 
508
    def test_signatures_template_method_none(self):
 
509
        my_config = InstrumentedConfig()
 
510
        my_config._signatures = None
 
511
        self.assertEqual(config.CHECK_IF_POSSIBLE,
 
512
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
513
                             my_config.signature_checking))
 
514
        self.assertEqual(['_get_signature_checking'], my_config._calls)
 
515
 
 
516
    def test_gpg_signing_command_default(self):
 
517
        my_config = config.Config()
 
518
        self.assertEqual('gpg',
 
519
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
520
                my_config.gpg_signing_command))
 
521
 
502
522
    def test_get_user_option_default(self):
503
523
        my_config = config.Config()
504
524
        self.assertEqual(None, my_config.get_user_option('no_option'))
505
525
 
 
526
    def test_post_commit_default(self):
 
527
        my_config = config.Config()
 
528
        self.assertEqual(None, self.applyDeprecated(deprecated_in((2, 5, 0)),
 
529
                                                    my_config.post_commit))
 
530
 
 
531
 
 
532
    def test_log_format_default(self):
 
533
        my_config = config.Config()
 
534
        self.assertEqual('long',
 
535
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
536
                                              my_config.log_format))
 
537
 
 
538
    def test_acceptable_keys_default(self):
 
539
        my_config = config.Config()
 
540
        self.assertEqual(None, self.applyDeprecated(deprecated_in((2, 5, 0)),
 
541
            my_config.acceptable_keys))
 
542
 
506
543
    def test_validate_signatures_in_log_default(self):
507
544
        my_config = config.Config()
508
545
        self.assertEqual(False, my_config.validate_signatures_in_log())
512
549
        change_editor = my_config.get_change_editor('old_tree', 'new_tree')
513
550
        self.assertEqual(['_get_change_editor'], my_config._calls)
514
551
        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)
 
552
        self.assertEqual(['vimdiff', '-fo', '@new_path', '@old_path'],
 
553
                         change_editor.command_template)
 
554
 
 
555
 
 
556
class TestConfigPath(tests.TestCase):
 
557
 
 
558
    def setUp(self):
 
559
        super(TestConfigPath, self).setUp()
 
560
        self.overrideEnv('HOME', '/home/bogus')
 
561
        self.overrideEnv('XDG_CACHE_HOME', '')
 
562
        if sys.platform == 'win32':
 
563
            self.overrideEnv(
 
564
                'BZR_HOME', r'C:\Documents and Settings\bogus\Application Data')
 
565
            self.bzr_home = \
 
566
                'C:/Documents and Settings/bogus/Application Data/bazaar/2.0'
 
567
        else:
 
568
            self.bzr_home = '/home/bogus/.bazaar'
 
569
 
 
570
    def test_config_dir(self):
 
571
        self.assertEqual(config.config_dir(), self.bzr_home)
 
572
 
 
573
    def test_config_dir_is_unicode(self):
 
574
        self.assertIsInstance(config.config_dir(), unicode)
 
575
 
 
576
    def test_config_filename(self):
 
577
        self.assertEqual(config.config_filename(),
 
578
                         self.bzr_home + '/bazaar.conf')
 
579
 
 
580
    def test_locations_config_filename(self):
 
581
        self.assertEqual(config.locations_config_filename(),
 
582
                         self.bzr_home + '/locations.conf')
 
583
 
 
584
    def test_authentication_config_filename(self):
 
585
        self.assertEqual(config.authentication_config_filename(),
 
586
                         self.bzr_home + '/authentication.conf')
 
587
 
 
588
    def test_xdg_cache_dir(self):
 
589
        self.assertEqual(config.xdg_cache_dir(),
 
590
            '/home/bogus/.cache')
 
591
 
 
592
 
 
593
class TestXDGConfigDir(tests.TestCaseInTempDir):
 
594
    # must be in temp dir because config tests for the existence of the bazaar
 
595
    # subdirectory of $XDG_CONFIG_HOME
 
596
 
 
597
    def setUp(self):
 
598
        if sys.platform == 'win32':
 
599
            raise tests.TestNotApplicable(
 
600
                'XDG config dir not used on this platform')
 
601
        super(TestXDGConfigDir, self).setUp()
 
602
        self.overrideEnv('HOME', self.test_home_dir)
 
603
        # BZR_HOME overrides everything we want to test so unset it.
 
604
        self.overrideEnv('BZR_HOME', None)
 
605
 
 
606
    def test_xdg_config_dir_exists(self):
 
607
        """When ~/.config/bazaar exists, use it as the config dir."""
 
608
        newdir = osutils.pathjoin(self.test_home_dir, '.config', 'bazaar')
 
609
        os.makedirs(newdir)
 
610
        self.assertEqual(config.config_dir(), newdir)
 
611
 
 
612
    def test_xdg_config_home(self):
 
613
        """When XDG_CONFIG_HOME is set, use it."""
 
614
        xdgconfigdir = osutils.pathjoin(self.test_home_dir, 'xdgconfig')
 
615
        self.overrideEnv('XDG_CONFIG_HOME', xdgconfigdir)
 
616
        newdir = osutils.pathjoin(xdgconfigdir, 'bazaar')
 
617
        os.makedirs(newdir)
 
618
        self.assertEqual(config.config_dir(), newdir)
538
619
 
539
620
 
540
621
class TestIniConfig(tests.TestCaseInTempDir):
572
653
        self.assertTrue(isinstance(self.uid, int))
573
654
        self.assertTrue(isinstance(self.gid, int))
574
655
 
 
656
    def test_get_filename_parameter_is_deprecated_(self):
 
657
        conf = self.callDeprecated([
 
658
            'IniBasedConfig.__init__(get_filename) was deprecated in 2.3.'
 
659
            ' Use file_name instead.'],
 
660
            config.IniBasedConfig, lambda: 'ini.conf')
 
661
        self.assertEqual('ini.conf', conf.file_name)
 
662
 
 
663
    def test_get_parser_file_parameter_is_deprecated_(self):
 
664
        config_file = StringIO(sample_config_text.encode('utf-8'))
 
665
        conf = config.IniBasedConfig.from_string(sample_config_text)
 
666
        conf = self.callDeprecated([
 
667
            'IniBasedConfig._get_parser(file=xxx) was deprecated in 2.3.'
 
668
            ' Use IniBasedConfig(_content=xxx) instead.'],
 
669
            conf._get_parser, file=config_file)
 
670
 
575
671
 
576
672
class TestIniConfigSaving(tests.TestCaseInTempDir):
577
673
 
580
676
        self.assertRaises(AssertionError, conf._write_config_file)
581
677
 
582
678
    def test_saved_with_content(self):
583
 
        content = b'foo = bar\n'
 
679
        content = 'foo = bar\n'
584
680
        config.IniBasedConfig.from_string(content, file_name='./test.conf',
585
681
                                          save=True)
586
682
        self.assertFileEqual(content, 'test.conf')
598
694
 
599
695
    def get_config(self, string=None):
600
696
        if string is None:
601
 
            string = b''
 
697
            string = ''
602
698
        c = config.IniBasedConfig.from_string(string)
603
699
        return c
604
700
 
623
719
 
624
720
    def test_unknown_ref(self):
625
721
        c = self.get_config('')
626
 
        self.assertRaises(config.ExpandingUnknownOption,
 
722
        self.assertRaises(errors.ExpandingUnknownOption,
627
723
                          c.expand_options, '{foo}')
628
724
 
629
725
    def test_indirect_ref(self):
642
738
 
643
739
    def test_simple_loop(self):
644
740
        c = self.get_config('foo={foo}')
645
 
        self.assertRaises(config.OptionExpansionLoop, c.expand_options,
646
 
                          '{foo}')
 
741
        self.assertRaises(errors.OptionExpansionLoop, c.expand_options, '{foo}')
647
742
 
648
743
    def test_indirect_loop(self):
649
744
        c = self.get_config('''
650
745
foo={bar}
651
746
bar={baz}
652
747
baz={foo}''')
653
 
        e = self.assertRaises(config.OptionExpansionLoop,
 
748
        e = self.assertRaises(errors.OptionExpansionLoop,
654
749
                              c.expand_options, '{foo}')
655
750
        self.assertEqual('foo->bar->baz', e.refs)
656
751
        self.assertEqual('{foo}', e.string)
663
758
list={foo},{bar},{baz}
664
759
''')
665
760
        self.assertEqual(['start', 'middle', 'end'],
666
 
                         conf.get_user_option('list', expand=True))
 
761
                           conf.get_user_option('list', expand=True))
667
762
 
668
763
    def test_cascading_list(self):
669
764
        conf = self.get_config('''
673
768
list={foo}
674
769
''')
675
770
        self.assertEqual(['start', 'middle', 'end'],
676
 
                         conf.get_user_option('list', expand=True))
 
771
                           conf.get_user_option('list', expand=True))
677
772
 
678
773
    def test_pathological_hidden_list(self):
679
774
        conf = self.get_config('''
687
782
        # Nope, it's either a string or a list, and the list wins as soon as a
688
783
        # ',' appears, so the string concatenation never occur.
689
784
        self.assertEqual(['{foo', '}', '{', 'bar}'],
690
 
                         conf.get_user_option('hidden', expand=True))
 
785
                          conf.get_user_option('hidden', expand=True))
691
786
 
692
787
 
693
788
class TestLocationConfigOptionExpansion(tests.TestCaseInTempDir):
701
796
        return c
702
797
 
703
798
    def test_dont_cross_unrelated_section(self):
704
 
        c = self.get_config('/another/branch/path', '''
 
799
        c = self.get_config('/another/branch/path','''
705
800
[/one/branch/path]
706
801
foo = hello
707
802
bar = {foo}/2
709
804
[/another/branch/path]
710
805
bar = {foo}/2
711
806
''')
712
 
        self.assertRaises(config.ExpandingUnknownOption,
 
807
        self.assertRaises(errors.ExpandingUnknownOption,
713
808
                          c.get_user_option, 'bar', expand=True)
714
809
 
715
810
    def test_cross_related_sections(self):
716
 
        c = self.get_config('/project/branch/path', '''
 
811
        c = self.get_config('/project/branch/path','''
717
812
[/project]
718
813
foo = qu
719
814
 
805
900
        after_writing = threading.Event()
806
901
        writing_done = threading.Event()
807
902
        c1_orig = c1._write_config_file
808
 
 
809
903
        def c1_write_config_file():
810
904
            before_writing.set()
811
905
            c1_orig()
813
907
            # continue
814
908
            after_writing.wait()
815
909
        c1._write_config_file = c1_write_config_file
816
 
 
817
910
        def c1_set_option():
818
911
            c1.set_user_option('one', 'c1')
819
912
            writing_done.set()
835
928
        self.assertEqual('c2', c2.get_user_option('one'))
836
929
 
837
930
    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'))
 
931
       c1 = self.config
 
932
       # We spawn a thread that will pause *during* the write
 
933
       ready_to_write = threading.Event()
 
934
       do_writing = threading.Event()
 
935
       writing_done = threading.Event()
 
936
       c1_orig = c1._write_config_file
 
937
       def c1_write_config_file():
 
938
           ready_to_write.set()
 
939
           # The lock is held. We wait for the main thread to decide when to
 
940
           # continue
 
941
           do_writing.wait()
 
942
           c1_orig()
 
943
           writing_done.set()
 
944
       c1._write_config_file = c1_write_config_file
 
945
       def c1_set_option():
 
946
           c1.set_user_option('one', 'c1')
 
947
       t1 = threading.Thread(target=c1_set_option)
 
948
       # Collect the thread after the test
 
949
       self.addCleanup(t1.join)
 
950
       # Be ready to unblock the thread if the test goes wrong
 
951
       self.addCleanup(do_writing.set)
 
952
       t1.start()
 
953
       # Ensure the thread is ready to write
 
954
       ready_to_write.wait()
 
955
       self.assertTrue(c1._lock.is_held)
 
956
       self.assertEqual('c1', c1.get_user_option('one'))
 
957
       # If we read during the write, we get the old value
 
958
       c2 = self.get_existing_config()
 
959
       self.assertEqual('1', c2.get_user_option('one'))
 
960
       # Let the writing occur and ensure it occurred
 
961
       do_writing.set()
 
962
       writing_done.wait()
 
963
       # Now we get the updated value
 
964
       c3 = self.get_existing_config()
 
965
       self.assertEqual('c1', c3.get_user_option('one'))
875
966
 
876
967
 
877
968
class TestGetUserOptionAs(TestIniConfig):
887
978
        self.assertEqual(True, get_bool('a_true_bool'))
888
979
        self.assertEqual(False, get_bool('a_false_bool'))
889
980
        warnings = []
890
 
 
891
981
        def warning(*args):
892
982
            warnings.append(args[0] % args[1:])
893
983
        self.overrideAttr(trace, 'warning', warning)
911
1001
        # automatically cast to list
912
1002
        self.assertEqual(['x'], get_list('one_item'))
913
1003
 
 
1004
    def test_get_user_option_as_int_from_SI(self):
 
1005
        conf, parser = self.make_config_parser("""
 
1006
plain = 100
 
1007
si_k = 5k,
 
1008
si_kb = 5kb,
 
1009
si_m = 5M,
 
1010
si_mb = 5MB,
 
1011
si_g = 5g,
 
1012
si_gb = 5gB,
 
1013
""")
 
1014
        def get_si(s, default=None):
 
1015
            return self.applyDeprecated(
 
1016
                deprecated_in((2, 5, 0)),
 
1017
                conf.get_user_option_as_int_from_SI, s, default)
 
1018
        self.assertEqual(100, get_si('plain'))
 
1019
        self.assertEqual(5000, get_si('si_k'))
 
1020
        self.assertEqual(5000, get_si('si_kb'))
 
1021
        self.assertEqual(5000000, get_si('si_m'))
 
1022
        self.assertEqual(5000000, get_si('si_mb'))
 
1023
        self.assertEqual(5000000000, get_si('si_g'))
 
1024
        self.assertEqual(5000000000, get_si('si_gb'))
 
1025
        self.assertEqual(None, get_si('non-exist'))
 
1026
        self.assertEqual(42, get_si('non-exist-with-default',  42))
 
1027
 
914
1028
 
915
1029
class TestSupressWarning(TestIniConfig):
916
1030
 
929
1043
        self.assertEqual(True, suppress_warning('b'))
930
1044
 
931
1045
 
932
 
class TestGetConfig(tests.TestCaseInTempDir):
 
1046
class TestGetConfig(tests.TestCase):
933
1047
 
934
1048
    def test_constructs(self):
935
1049
        config.GlobalConfig()
944
1058
        finally:
945
1059
            config.ConfigObj = oldparserclass
946
1060
        self.assertIsInstance(parser, InstrumentedConfigObj)
947
 
        self.assertEqual(parser._calls, [('__init__', bedding.config_path(),
 
1061
        self.assertEqual(parser._calls, [('__init__', config.config_filename(),
948
1062
                                          'utf-8')])
949
1063
 
950
1064
 
1003
1117
 
1004
1118
        local_path = osutils.getcwd().encode('utf8')
1005
1119
        config.LocationConfig.from_string(
1006
 
            b'[%s/branch]\nnickname = barry' % (local_path,),
1007
 
            'branch', save=True)
 
1120
            '[%s/branch]\nnickname = barry' % (local_path,),
 
1121
            'branch',  save=True)
1008
1122
        # Now the branch will find its nick via the location config
1009
1123
        self.assertEqual('barry', branch.nick)
1010
1124
 
1014
1128
        branch.set_push_location('http://foobar')
1015
1129
        local_path = osutils.getcwd().encode('utf8')
1016
1130
        # 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'
 
1131
        self.check_file_contents(config.locations_config_filename(),
 
1132
                                 '[%s/branch]\n'
 
1133
                                 'push_location = http://foobar\n'
 
1134
                                 'push_location:policy = norecurse\n'
1021
1135
                                 % (local_path,))
1022
1136
 
1023
1137
    def test_autonick_urlencoded(self):
1030
1144
 
1031
1145
    def test_warn_if_masked(self):
1032
1146
        warnings = []
1033
 
 
1034
1147
        def warning(*args):
1035
1148
            warnings.append(args[0] % args[1:])
1036
1149
        self.overrideAttr(trace, 'warning', warning)
1039
1152
            warnings[:] = []
1040
1153
            conf.set_user_option('example_option', repr(store), store=store,
1041
1154
                                 warn_masked=warn_masked)
1042
 
 
1043
1155
        def assertWarning(warning):
1044
1156
            if warning is None:
1045
1157
                self.assertEqual(0, len(warnings))
1066
1178
 
1067
1179
class TestGlobalConfigItems(tests.TestCaseInTempDir):
1068
1180
 
1069
 
    def _get_empty_config(self):
1070
 
        my_config = config.GlobalConfig()
1071
 
        return my_config
1072
 
 
1073
 
    def _get_sample_config(self):
1074
 
        my_config = config.GlobalConfig.from_string(sample_config_text)
1075
 
        return my_config
1076
 
 
1077
1181
    def test_user_id(self):
1078
1182
        my_config = config.GlobalConfig.from_string(sample_config_text)
1079
1183
        self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
1083
1187
        my_config = config.GlobalConfig()
1084
1188
        self.assertEqual(None, my_config._get_user_id())
1085
1189
 
 
1190
    def test_signatures_always(self):
 
1191
        my_config = config.GlobalConfig.from_string(sample_always_signatures)
 
1192
        self.assertEqual(config.CHECK_NEVER,
 
1193
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1194
                             my_config.signature_checking))
 
1195
        self.assertEqual(config.SIGN_ALWAYS,
 
1196
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1197
                             my_config.signing_policy))
 
1198
        self.assertEqual(True,
 
1199
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1200
                my_config.signature_needed))
 
1201
 
 
1202
    def test_signatures_if_possible(self):
 
1203
        my_config = config.GlobalConfig.from_string(sample_maybe_signatures)
 
1204
        self.assertEqual(config.CHECK_NEVER,
 
1205
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1206
                             my_config.signature_checking))
 
1207
        self.assertEqual(config.SIGN_WHEN_REQUIRED,
 
1208
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1209
                             my_config.signing_policy))
 
1210
        self.assertEqual(False, self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1211
            my_config.signature_needed))
 
1212
 
 
1213
    def test_signatures_ignore(self):
 
1214
        my_config = config.GlobalConfig.from_string(sample_ignore_signatures)
 
1215
        self.assertEqual(config.CHECK_ALWAYS,
 
1216
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1217
                             my_config.signature_checking))
 
1218
        self.assertEqual(config.SIGN_NEVER,
 
1219
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1220
                             my_config.signing_policy))
 
1221
        self.assertEqual(False, self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1222
            my_config.signature_needed))
 
1223
 
 
1224
    def _get_sample_config(self):
 
1225
        my_config = config.GlobalConfig.from_string(sample_config_text)
 
1226
        return my_config
 
1227
 
 
1228
    def test_gpg_signing_command(self):
 
1229
        my_config = self._get_sample_config()
 
1230
        self.assertEqual("gnome-gpg",
 
1231
            self.applyDeprecated(
 
1232
                deprecated_in((2, 5, 0)), my_config.gpg_signing_command))
 
1233
        self.assertEqual(False, self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1234
            my_config.signature_needed))
 
1235
 
 
1236
    def test_gpg_signing_key(self):
 
1237
        my_config = self._get_sample_config()
 
1238
        self.assertEqual("DD4D5088",
 
1239
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1240
                my_config.gpg_signing_key))
 
1241
 
 
1242
    def _get_empty_config(self):
 
1243
        my_config = config.GlobalConfig()
 
1244
        return my_config
 
1245
 
 
1246
    def test_gpg_signing_command_unset(self):
 
1247
        my_config = self._get_empty_config()
 
1248
        self.assertEqual("gpg",
 
1249
            self.applyDeprecated(
 
1250
                deprecated_in((2, 5, 0)), my_config.gpg_signing_command))
 
1251
 
1086
1252
    def test_get_user_option_default(self):
1087
1253
        my_config = self._get_empty_config()
1088
1254
        self.assertEqual(None, my_config.get_user_option('no_option'))
1092
1258
        self.assertEqual("something",
1093
1259
                         my_config.get_user_option('user_global_option'))
1094
1260
 
 
1261
    def test_post_commit_default(self):
 
1262
        my_config = self._get_sample_config()
 
1263
        self.assertEqual(None,
 
1264
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1265
                                              my_config.post_commit))
 
1266
 
 
1267
    def test_configured_logformat(self):
 
1268
        my_config = self._get_sample_config()
 
1269
        self.assertEqual("short",
 
1270
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1271
                                              my_config.log_format))
 
1272
 
 
1273
    def test_configured_acceptable_keys(self):
 
1274
        my_config = self._get_sample_config()
 
1275
        self.assertEqual("amy",
 
1276
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1277
                my_config.acceptable_keys))
 
1278
 
1095
1279
    def test_configured_validate_signatures_in_log(self):
1096
1280
        my_config = self._get_sample_config()
1097
1281
        self.assertEqual(True, my_config.validate_signatures_in_log())
1120
1304
        my_config = self._get_sample_config()
1121
1305
        change_editor = my_config.get_change_editor('old', 'new')
1122
1306
        self.assertIs(diff.DiffFromTool, change_editor.__class__)
1123
 
        self.assertEqual('vimdiff -of {new_path} {old_path}',
 
1307
        self.assertEqual('vimdiff -of @new_path @old_path',
1124
1308
                         ' '.join(change_editor.command_template))
1125
1309
 
1126
1310
    def test_get_no_change_editor(self):
1133
1317
        tools = conf.get_merge_tools()
1134
1318
        self.log(repr(tools))
1135
1319
        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}'},
 
1320
            {u'funkytool' : u'funkytool "arg with spaces" {this_temp}',
 
1321
            u'sometool' : u'sometool {base} {this} {other} -o {result}',
 
1322
            u'newtool' : u'"newtool with spaces" {this_temp}'},
1139
1323
            tools)
1140
1324
 
1141
1325
    def test_get_merge_tools_empty(self):
1209
1393
            config.ConfigObj = oldparserclass
1210
1394
        self.assertIsInstance(parser, InstrumentedConfigObj)
1211
1395
        self.assertEqual(parser._calls,
1212
 
                         [('__init__', bedding.locations_config_path(),
 
1396
                         [('__init__', config.locations_config_filename(),
1213
1397
                           'utf-8')])
1214
1398
 
1215
1399
    def test_get_global_config(self):
1277
1461
        self.get_branch_config('http://www.example.com')
1278
1462
        self.assertEqual(
1279
1463
            self.my_location_config._get_config_policy(
1280
 
                'http://www.example.com', 'normal_option'),
 
1464
            'http://www.example.com', 'normal_option'),
1281
1465
            config.POLICY_NONE)
1282
1466
 
1283
1467
    def test__get_option_policy_norecurse(self):
1284
1468
        self.get_branch_config('http://www.example.com')
1285
1469
        self.assertEqual(
1286
1470
            self.my_location_config._get_option_policy(
1287
 
                'http://www.example.com', 'norecurse_option'),
 
1471
            'http://www.example.com', 'norecurse_option'),
1288
1472
            config.POLICY_NORECURSE)
1289
1473
        # Test old recurse=False setting:
1290
1474
        self.assertEqual(
1291
1475
            self.my_location_config._get_option_policy(
1292
 
                'http://www.example.com/norecurse', 'normal_option'),
 
1476
            'http://www.example.com/norecurse', 'normal_option'),
1293
1477
            config.POLICY_NORECURSE)
1294
1478
 
1295
1479
    def test__get_option_policy_normal(self):
1296
1480
        self.get_branch_config('http://www.example.com')
1297
1481
        self.assertEqual(
1298
1482
            self.my_location_config._get_option_policy(
1299
 
                'http://www.example.com', 'appendpath_option'),
 
1483
            'http://www.example.com', 'appendpath_option'),
1300
1484
            config.POLICY_APPENDPATH)
1301
1485
 
1302
1486
    def test__get_options_with_policy(self):
1330
1514
        self.assertEqual('Robert Collins <robertc@example.org>',
1331
1515
                         self.my_config.username())
1332
1516
 
 
1517
    def test_signatures_not_set(self):
 
1518
        self.get_branch_config('http://www.example.com',
 
1519
                                 global_config=sample_ignore_signatures)
 
1520
        self.assertEqual(config.CHECK_ALWAYS,
 
1521
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1522
                             self.my_config.signature_checking))
 
1523
        self.assertEqual(config.SIGN_NEVER,
 
1524
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1525
                             self.my_config.signing_policy))
 
1526
 
 
1527
    def test_signatures_never(self):
 
1528
        self.get_branch_config('/a/c')
 
1529
        self.assertEqual(config.CHECK_NEVER,
 
1530
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1531
                             self.my_config.signature_checking))
 
1532
 
 
1533
    def test_signatures_when_available(self):
 
1534
        self.get_branch_config('/a/', global_config=sample_ignore_signatures)
 
1535
        self.assertEqual(config.CHECK_IF_POSSIBLE,
 
1536
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1537
                             self.my_config.signature_checking))
 
1538
 
 
1539
    def test_signatures_always(self):
 
1540
        self.get_branch_config('/b')
 
1541
        self.assertEqual(config.CHECK_ALWAYS,
 
1542
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1543
                         self.my_config.signature_checking))
 
1544
 
 
1545
    def test_gpg_signing_command(self):
 
1546
        self.get_branch_config('/b')
 
1547
        self.assertEqual("gnome-gpg",
 
1548
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1549
                self.my_config.gpg_signing_command))
 
1550
 
 
1551
    def test_gpg_signing_command_missing(self):
 
1552
        self.get_branch_config('/a')
 
1553
        self.assertEqual("false",
 
1554
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1555
                self.my_config.gpg_signing_command))
 
1556
 
 
1557
    def test_gpg_signing_key(self):
 
1558
        self.get_branch_config('/b')
 
1559
        self.assertEqual("DD4D5088", self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1560
            self.my_config.gpg_signing_key))
 
1561
 
 
1562
    def test_gpg_signing_key_default(self):
 
1563
        self.get_branch_config('/a')
 
1564
        self.assertEqual("erik@bagfors.nu",
 
1565
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1566
                self.my_config.gpg_signing_key))
 
1567
 
1333
1568
    def test_get_user_option_global(self):
1334
1569
        self.get_branch_config('/a')
1335
1570
        self.assertEqual('something',
1378
1613
                                       store=config.STORE_LOCATION_NORECURSE)
1379
1614
        self.assertEqual(
1380
1615
            self.my_location_config._get_option_policy(
1381
 
                'http://www.example.com', 'foo'),
 
1616
            'http://www.example.com', 'foo'),
1382
1617
            config.POLICY_NORECURSE)
1383
1618
 
1384
1619
    def test_set_user_option_appendpath(self):
1387
1622
                                       store=config.STORE_LOCATION_APPENDPATH)
1388
1623
        self.assertEqual(
1389
1624
            self.my_location_config._get_option_policy(
1390
 
                'http://www.example.com', 'foo'),
 
1625
            'http://www.example.com', 'foo'),
1391
1626
            config.POLICY_APPENDPATH)
1392
1627
 
1393
1628
    def test_set_user_option_change_policy(self):
1396
1631
                                       store=config.STORE_LOCATION)
1397
1632
        self.assertEqual(
1398
1633
            self.my_location_config._get_option_policy(
1399
 
                'http://www.example.com', 'norecurse_option'),
1400
 
            config.POLICY_NONE)
 
1634
            'http://www.example.com', 'norecurse_option'),
 
1635
            config.POLICY_NONE)
 
1636
 
 
1637
    def test_set_user_option_recurse_false_section(self):
 
1638
        # The following section has recurse=False set.  The test is to
 
1639
        # make sure that a normal option can be added to the section,
 
1640
        # converting recurse=False to the norecurse policy.
 
1641
        self.get_branch_config('http://www.example.com/norecurse')
 
1642
        self.callDeprecated(['The recurse option is deprecated as of 0.14.  '
 
1643
                             'The section "http://www.example.com/norecurse" '
 
1644
                             'has been converted to use policies.'],
 
1645
                            self.my_config.set_user_option,
 
1646
                            'foo', 'bar', store=config.STORE_LOCATION)
 
1647
        self.assertEqual(
 
1648
            self.my_location_config._get_option_policy(
 
1649
            'http://www.example.com/norecurse', 'foo'),
 
1650
            config.POLICY_NONE)
 
1651
        # The previously existing option is still norecurse:
 
1652
        self.assertEqual(
 
1653
            self.my_location_config._get_option_policy(
 
1654
            'http://www.example.com/norecurse', 'normal_option'),
 
1655
            config.POLICY_NORECURSE)
 
1656
 
 
1657
    def test_post_commit_default(self):
 
1658
        self.get_branch_config('/a/c')
 
1659
        self.assertEqual('brzlib.tests.test_config.post_commit',
 
1660
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1661
                                              self.my_config.post_commit))
1401
1662
 
1402
1663
    def get_branch_config(self, location, global_config=None,
1403
1664
                          location_config=None):
1414
1675
        self.my_config = my_config
1415
1676
        self.my_location_config = my_config._get_location_config()
1416
1677
 
 
1678
    def test_set_user_setting_sets_and_saves(self):
 
1679
        self.get_branch_config('/a/c')
 
1680
        record = InstrumentedConfigObj("foo")
 
1681
        self.my_location_config._parser = record
 
1682
 
 
1683
        self.callDeprecated(['The recurse option is deprecated as of '
 
1684
                             '0.14.  The section "/a/c" has been '
 
1685
                             'converted to use policies.'],
 
1686
                            self.my_config.set_user_option,
 
1687
                            'foo', 'bar', store=config.STORE_LOCATION)
 
1688
        self.assertEqual([('reload',),
 
1689
                          ('__contains__', '/a/c'),
 
1690
                          ('__contains__', '/a/c/'),
 
1691
                          ('__setitem__', '/a/c', {}),
 
1692
                          ('__getitem__', '/a/c'),
 
1693
                          ('__setitem__', 'foo', 'bar'),
 
1694
                          ('__getitem__', '/a/c'),
 
1695
                          ('as_bool', 'recurse'),
 
1696
                          ('__getitem__', '/a/c'),
 
1697
                          ('__delitem__', 'recurse'),
 
1698
                          ('__getitem__', '/a/c'),
 
1699
                          ('keys',),
 
1700
                          ('__getitem__', '/a/c'),
 
1701
                          ('__contains__', 'foo:policy'),
 
1702
                          ('write',)],
 
1703
                         record._calls[1:])
 
1704
 
1417
1705
    def test_set_user_setting_sets_and_saves2(self):
1418
1706
        self.get_branch_config('/a/c')
1419
1707
        self.assertIs(self.my_config.get_user_option('foo'), None)
1420
1708
        self.my_config.set_user_option('foo', 'bar')
1421
1709
        self.assertEqual(
1422
1710
            self.my_config.branch.control_files.files['branch.conf'].strip(),
1423
 
            b'foo = bar')
 
1711
            'foo = bar')
1424
1712
        self.assertEqual(self.my_config.get_user_option('foo'), 'bar')
1425
1713
        self.my_config.set_user_option('foo', 'baz',
1426
1714
                                       store=config.STORE_LOCATION)
1437
1725
        self.assertEqual('/environ-bzr', my_config.get_bzr_remote_path())
1438
1726
 
1439
1727
 
1440
 
precedence_global = b'option = global'
1441
 
precedence_branch = b'option = branch'
1442
 
precedence_location = b"""
 
1728
precedence_global = 'option = global'
 
1729
precedence_branch = 'option = branch'
 
1730
precedence_location = """
1443
1731
[http://]
1444
1732
recurse = true
1445
1733
option = recurse
1447
1735
option = exact
1448
1736
"""
1449
1737
 
1450
 
 
1451
1738
class TestBranchConfigItems(tests.TestCaseInTempDir):
1452
1739
 
1453
1740
    def get_branch_config(self, global_config=None, location=None,
1472
1759
        my_config.set_user_option('email',
1473
1760
                                  "Robert Collins <robertc@example.org>")
1474
1761
        self.assertEqual("Robert Collins <robertc@example.org>",
1475
 
                         my_config.username())
 
1762
                        my_config.username())
1476
1763
 
1477
 
    def test_BRZ_EMAIL_OVERRIDES(self):
1478
 
        self.overrideEnv('BRZ_EMAIL', "Robert Collins <robertc@example.org>")
 
1764
    def test_BZR_EMAIL_OVERRIDES(self):
 
1765
        self.overrideEnv('BZR_EMAIL', "Robert Collins <robertc@example.org>")
1479
1766
        branch = FakeBranch()
1480
1767
        my_config = config.BranchConfig(branch)
1481
1768
        self.assertEqual("Robert Collins <robertc@example.org>",
1482
1769
                         my_config.username())
1483
1770
 
 
1771
    def test_signatures_forced(self):
 
1772
        my_config = self.get_branch_config(
 
1773
            global_config=sample_always_signatures)
 
1774
        self.assertEqual(config.CHECK_NEVER,
 
1775
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1776
                my_config.signature_checking))
 
1777
        self.assertEqual(config.SIGN_ALWAYS,
 
1778
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1779
                my_config.signing_policy))
 
1780
        self.assertTrue(self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1781
            my_config.signature_needed))
 
1782
 
 
1783
    def test_signatures_forced_branch(self):
 
1784
        my_config = self.get_branch_config(
 
1785
            global_config=sample_ignore_signatures,
 
1786
            branch_data_config=sample_always_signatures)
 
1787
        self.assertEqual(config.CHECK_NEVER,
 
1788
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1789
                my_config.signature_checking))
 
1790
        self.assertEqual(config.SIGN_ALWAYS,
 
1791
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1792
                my_config.signing_policy))
 
1793
        self.assertTrue(self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1794
            my_config.signature_needed))
 
1795
 
 
1796
    def test_gpg_signing_command(self):
 
1797
        my_config = self.get_branch_config(
 
1798
            global_config=sample_config_text,
 
1799
            # branch data cannot set gpg_signing_command
 
1800
            branch_data_config="gpg_signing_command=pgp")
 
1801
        self.assertEqual('gnome-gpg',
 
1802
            self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1803
                my_config.gpg_signing_command))
 
1804
 
1484
1805
    def test_get_user_option_global(self):
1485
1806
        my_config = self.get_branch_config(global_config=sample_config_text)
1486
1807
        self.assertEqual('something',
1487
1808
                         my_config.get_user_option('user_global_option'))
1488
1809
 
 
1810
    def test_post_commit_default(self):
 
1811
        my_config = self.get_branch_config(global_config=sample_config_text,
 
1812
                                      location='/a/c',
 
1813
                                      location_config=sample_branches_text)
 
1814
        self.assertEqual(my_config.branch.base, '/a/c')
 
1815
        self.assertEqual('brzlib.tests.test_config.post_commit',
 
1816
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1817
                                              my_config.post_commit))
 
1818
        my_config.set_user_option('post_commit', 'rmtree_root')
 
1819
        # post-commit is ignored when present in branch data
 
1820
        self.assertEqual('brzlib.tests.test_config.post_commit',
 
1821
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1822
                                              my_config.post_commit))
 
1823
        my_config.set_user_option('post_commit', 'rmtree_root',
 
1824
                                  store=config.STORE_LOCATION)
 
1825
        self.assertEqual('rmtree_root',
 
1826
                         self.applyDeprecated(deprecated_in((2, 5, 0)),
 
1827
                                              my_config.post_commit))
 
1828
 
1489
1829
    def test_config_precedence(self):
1490
1830
        # FIXME: eager test, luckily no persitent config file makes it fail
1491
1831
        # -- vila 20100716
1512
1852
    def test_extract_email_address(self):
1513
1853
        self.assertEqual('jane@test.com',
1514
1854
                         config.extract_email_address('Jane <jane@test.com>'))
1515
 
        self.assertRaises(config.NoEmailInUsername,
 
1855
        self.assertRaises(errors.NoEmailInUsername,
1516
1856
                          config.extract_email_address, 'Jane Tester')
1517
1857
 
1518
1858
    def test_parse_username(self):
1527
1867
        self.assertEqual(('John Doe', 'jdoe@example.com'),
1528
1868
                         config.parse_username('John Doe jdoe@example.com'))
1529
1869
 
1530
 
 
1531
1870
class TestTreeConfig(tests.TestCaseWithTransport):
1532
1871
 
1533
1872
    def test_get_value(self):
1572
1911
    def test_load_non_ascii(self):
1573
1912
        """Ensure we display a proper error on non-ascii, non utf-8 content."""
1574
1913
        t = self.get_transport()
1575
 
        t.put_bytes('foo.conf', b'user=foo\n#\xff\n')
 
1914
        t.put_bytes('foo.conf', 'user=foo\n#\xff\n')
1576
1915
        conf = config.TransportConfig(t, 'foo.conf')
1577
 
        self.assertRaises(config.ConfigContentError, conf._get_configobj)
 
1916
        self.assertRaises(errors.ConfigContentError, conf._get_configobj)
1578
1917
 
1579
1918
    def test_load_erroneous_content(self):
1580
1919
        """Ensure we display a proper error on content that can't be parsed."""
1581
1920
        t = self.get_transport()
1582
 
        t.put_bytes('foo.conf', b'[open_section\n')
 
1921
        t.put_bytes('foo.conf', '[open_section\n')
1583
1922
        conf = config.TransportConfig(t, 'foo.conf')
1584
 
        self.assertRaises(config.ParseConfigError, conf._get_configobj)
 
1923
        self.assertRaises(errors.ParseConfigError, conf._get_configobj)
1585
1924
 
1586
1925
    def test_load_permission_denied(self):
1587
1926
        """Ensure we get an empty config file if the file is inaccessible."""
1588
1927
        warnings = []
1589
 
 
1590
1928
        def warning(*args):
1591
1929
            warnings.append(args[0] % args[1:])
1592
1930
        self.overrideAttr(trace, 'warning', warning)
1634
1972
        self.assertEqual(value, 'value3-section')
1635
1973
 
1636
1974
    def test_set_unset_default_stack_on(self):
1637
 
        my_dir = self.make_controldir('.')
 
1975
        my_dir = self.make_bzrdir('.')
1638
1976
        bzrdir_config = config.BzrDirConfig(my_dir)
1639
1977
        self.assertIs(None, bzrdir_config.get_default_stack_on())
1640
1978
        bzrdir_config.set_default_stack_on('Foo')
1653
1991
 
1654
1992
    def assertGetHook(self, conf, name, value):
1655
1993
        calls = []
1656
 
 
1657
1994
        def hook(*args):
1658
1995
            calls.append(args)
1659
1996
        config.OldConfigHooks.install_named_hook('get', hook, None)
1665
2002
        self.assertLength(1, calls)
1666
2003
        self.assertEqual((conf, name, value), calls[0])
1667
2004
 
1668
 
    def test_get_hook_breezy(self):
1669
 
        self.assertGetHook(self.breezy_config, 'file', 'breezy')
 
2005
    def test_get_hook_bazaar(self):
 
2006
        self.assertGetHook(self.bazaar_config, 'file', 'bazaar')
1670
2007
 
1671
2008
    def test_get_hook_locations(self):
1672
2009
        self.assertGetHook(self.locations_config, 'file', 'locations')
1678
2015
 
1679
2016
    def assertSetHook(self, conf, name, value):
1680
2017
        calls = []
1681
 
 
1682
2018
        def hook(*args):
1683
2019
            calls.append(args)
1684
2020
        config.OldConfigHooks.install_named_hook('set', hook, None)
1692
2028
        # coverage here.
1693
2029
        self.assertEqual((name, value), calls[0][1:])
1694
2030
 
1695
 
    def test_set_hook_breezy(self):
1696
 
        self.assertSetHook(self.breezy_config, 'foo', 'breezy')
 
2031
    def test_set_hook_bazaar(self):
 
2032
        self.assertSetHook(self.bazaar_config, 'foo', 'bazaar')
1697
2033
 
1698
2034
    def test_set_hook_locations(self):
1699
2035
        self.assertSetHook(self.locations_config, 'foo', 'locations')
1703
2039
 
1704
2040
    def assertRemoveHook(self, conf, name, section_name=None):
1705
2041
        calls = []
1706
 
 
1707
2042
        def hook(*args):
1708
2043
            calls.append(args)
1709
2044
        config.OldConfigHooks.install_named_hook('remove', hook, None)
1717
2052
        # coverage here.
1718
2053
        self.assertEqual((name,), calls[0][1:])
1719
2054
 
1720
 
    def test_remove_hook_breezy(self):
1721
 
        self.assertRemoveHook(self.breezy_config, 'file')
 
2055
    def test_remove_hook_bazaar(self):
 
2056
        self.assertRemoveHook(self.bazaar_config, 'file')
1722
2057
 
1723
2058
    def test_remove_hook_locations(self):
1724
2059
        self.assertRemoveHook(self.locations_config, 'file',
1729
2064
 
1730
2065
    def assertLoadHook(self, name, conf_class, *conf_args):
1731
2066
        calls = []
1732
 
 
1733
2067
        def hook(*args):
1734
2068
            calls.append(args)
1735
2069
        config.OldConfigHooks.install_named_hook('load', hook, None)
1743
2077
        self.assertLength(1, calls)
1744
2078
        # Since we can't assert about conf, we just use the number of calls ;-/
1745
2079
 
1746
 
    def test_load_hook_breezy(self):
 
2080
    def test_load_hook_bazaar(self):
1747
2081
        self.assertLoadHook('file', config.GlobalConfig)
1748
2082
 
1749
2083
    def test_load_hook_locations(self):
1754
2088
 
1755
2089
    def assertSaveHook(self, conf):
1756
2090
        calls = []
1757
 
 
1758
2091
        def hook(*args):
1759
2092
            calls.append(args)
1760
2093
        config.OldConfigHooks.install_named_hook('save', hook, None)
1766
2099
        self.assertLength(1, calls)
1767
2100
        # Since we can't assert about conf, we just use the number of calls ;-/
1768
2101
 
1769
 
    def test_save_hook_breezy(self):
1770
 
        self.assertSaveHook(self.breezy_config)
 
2102
    def test_save_hook_bazaar(self):
 
2103
        self.assertSaveHook(self.bazaar_config)
1771
2104
 
1772
2105
    def test_save_hook_locations(self):
1773
2106
        self.assertSaveHook(self.locations_config)
1789
2122
 
1790
2123
    def assertGetHook(self, conf, name, value):
1791
2124
        calls = []
1792
 
 
1793
2125
        def hook(*args):
1794
2126
            calls.append(args)
1795
2127
        config.OldConfigHooks.install_named_hook('get', hook, None)
1813
2145
 
1814
2146
    def assertSetHook(self, conf, name, value):
1815
2147
        calls = []
1816
 
 
1817
2148
        def hook(*args):
1818
2149
            calls.append(args)
1819
2150
        config.OldConfigHooks.install_named_hook('set', hook, None)
1840
2171
 
1841
2172
    def assertLoadHook(self, expected_nb_calls, name, conf_class, *conf_args):
1842
2173
        calls = []
1843
 
 
1844
2174
        def hook(*args):
1845
2175
            calls.append(args)
1846
2176
        config.OldConfigHooks.install_named_hook('load', hook, None)
1856
2186
 
1857
2187
    def test_load_hook_remote_branch(self):
1858
2188
        remote_branch = branch.Branch.open(self.get_url('tree'))
1859
 
        self.assertLoadHook(
1860
 
            1, 'file', remote.RemoteBranchConfig, remote_branch)
 
2189
        self.assertLoadHook(1, 'file', remote.RemoteBranchConfig, remote_branch)
1861
2190
 
1862
2191
    def test_load_hook_remote_bzrdir(self):
1863
2192
        remote_bzrdir = controldir.ControlDir.open(self.get_url('tree'))
1868
2197
        # caused by the differences in implementations betwen
1869
2198
        # SmartServerBzrDirRequestConfigFile (in smart/bzrdir.py) and
1870
2199
        # SmartServerBranchGetConfigFile (in smart/branch.py)
1871
 
        self.assertLoadHook(
1872
 
            2, 'file', remote.RemoteBzrDirConfig, remote_bzrdir)
 
2200
        self.assertLoadHook(2 ,'file', remote.RemoteBzrDirConfig, remote_bzrdir)
1873
2201
 
1874
2202
    def assertSaveHook(self, conf):
1875
2203
        calls = []
1876
 
 
1877
2204
        def hook(*args):
1878
2205
            calls.append(args)
1879
2206
        config.OldConfigHooks.install_named_hook('save', hook, None)
1976
2303
 
1977
2304
    def test_not_supported_callable_default_value_not_unicode(self):
1978
2305
        def bar_not_unicode():
1979
 
            return b'bar'
 
2306
            return 'bar'
1980
2307
        opt = config.Option('foo', default=bar_not_unicode)
1981
2308
        self.assertRaises(AssertionError, opt.get_default)
1982
2309
 
2003
2330
            warnings[0])
2004
2331
 
2005
2332
    def assertCallsError(self, opt, value):
2006
 
        self.assertRaises(config.ConfigOptionValueError,
 
2333
        self.assertRaises(errors.ConfigOptionValueError,
2007
2334
                          opt.convert_from_unicode, None, value)
2008
2335
 
2009
2336
    def assertConvertInvalid(self, opt, invalid_value):
2170
2497
        self.assertEqual('A simple option', self.registry.get_help('foo'))
2171
2498
 
2172
2499
    def test_dont_register_illegal_name(self):
2173
 
        self.assertRaises(config.IllegalOptionName,
 
2500
        self.assertRaises(errors.IllegalOptionName,
2174
2501
                          self.registry.register, config.Option(' foo'))
2175
 
        self.assertRaises(config.IllegalOptionName,
 
2502
        self.assertRaises(errors.IllegalOptionName,
2176
2503
                          self.registry.register, config.Option('bar,'))
2177
2504
 
2178
2505
    lazy_option = config.Option('lazy_foo', help='Lazy help')
2193
2520
        # the option name which indirectly requires that the option name is a
2194
2521
        # valid python identifier. We violate that rule here (using a key that
2195
2522
        # doesn't match the option name) to test the option name checking.
2196
 
        self.assertRaises(config.IllegalOptionName,
 
2523
        self.assertRaises(errors.IllegalOptionName,
2197
2524
                          self.registry.register_lazy, ' foo', self.__module__,
2198
2525
                          'TestOptionRegistry.lazy_option')
2199
 
        self.assertRaises(config.IllegalOptionName,
 
2526
        self.assertRaises(errors.IllegalOptionName,
2200
2527
                          self.registry.register_lazy, '1,2', self.__module__,
2201
2528
                          'TestOptionRegistry.lazy_option')
2202
2529
 
2238
2565
        a_dict = dict()
2239
2566
        section = config.Section(None, a_dict)
2240
2567
        self.assertEqual('out of thin air',
2241
 
                         section.get('foo', 'out of thin air'))
 
2568
                          section.get('foo', 'out of thin air'))
2242
2569
 
2243
2570
    def test_options_is_shared(self):
2244
2571
        a_dict = dict()
2250
2577
 
2251
2578
    scenarios = [('mutable',
2252
2579
                  {'get_section':
2253
 
                   lambda opts: config.MutableSection('myID', opts)},),
2254
 
                 ]
 
2580
                       lambda opts: config.MutableSection('myID', opts)},),
 
2581
        ]
2255
2582
 
2256
2583
    def test_set(self):
2257
2584
        a_dict = dict(foo='bar')
2344
2671
        self.assertRaises(errors.BzrCommandError,
2345
2672
                          self.store._from_cmdline, ['a=b', 'c'])
2346
2673
 
2347
 
 
2348
2674
class TestStoreMinimalAPI(tests.TestCaseWithTransport):
2349
2675
 
2350
2676
    scenarios = [(key, {'get_store': builder}) for key, builder
2353
2679
 
2354
2680
    def test_id(self):
2355
2681
        store = self.get_store(self)
2356
 
        if isinstance(store, config.TransportIniFileStore):
 
2682
        if type(store) == config.TransportIniFileStore:
2357
2683
            raise tests.TestNotApplicable(
2358
2684
                "%s is not a concrete Store implementation"
2359
2685
                " so it doesn't need an id" % (store.__class__.__name__,))
2362
2688
 
2363
2689
class TestStore(tests.TestCaseWithTransport):
2364
2690
 
2365
 
    def assertSectionContent(self, expected, store_and_section):
 
2691
    def assertSectionContent(self, expected, (store, section)):
2366
2692
        """Assert that some options have the proper values in a section."""
2367
 
        _, section = store_and_section
2368
2693
        expected_name, expected_options = expected
2369
2694
        self.assertEqual(expected_name, section.id)
2370
2695
        self.assertEqual(
2380
2705
    def test_building_delays_load(self):
2381
2706
        store = self.get_store(self)
2382
2707
        self.assertEqual(False, store.is_loaded())
2383
 
        store._load_from_string(b'')
 
2708
        store._load_from_string('')
2384
2709
        self.assertEqual(True, store.is_loaded())
2385
2710
 
2386
2711
    def test_get_no_sections_for_empty(self):
2387
2712
        store = self.get_store(self)
2388
 
        store._load_from_string(b'')
 
2713
        store._load_from_string('')
2389
2714
        self.assertEqual([], list(store.get_sections()))
2390
2715
 
2391
2716
    def test_get_default_section(self):
2392
2717
        store = self.get_store(self)
2393
 
        store._load_from_string(b'foo=bar')
 
2718
        store._load_from_string('foo=bar')
2394
2719
        sections = list(store.get_sections())
2395
2720
        self.assertLength(1, sections)
2396
2721
        self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2397
2722
 
2398
2723
    def test_get_named_section(self):
2399
2724
        store = self.get_store(self)
2400
 
        store._load_from_string(b'[baz]\nfoo=bar')
 
2725
        store._load_from_string('[baz]\nfoo=bar')
2401
2726
        sections = list(store.get_sections())
2402
2727
        self.assertLength(1, sections)
2403
2728
        self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2404
2729
 
2405
2730
    def test_load_from_string_fails_for_non_empty_store(self):
2406
2731
        store = self.get_store(self)
2407
 
        store._load_from_string(b'foo=bar')
2408
 
        self.assertRaises(AssertionError, store._load_from_string, b'bar=baz')
 
2732
        store._load_from_string('foo=bar')
 
2733
        self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2409
2734
 
2410
2735
 
2411
2736
class TestStoreQuoting(TestStore):
2417
2742
        super(TestStoreQuoting, self).setUp()
2418
2743
        self.store = self.get_store(self)
2419
2744
        # We need a loaded store but any content will do
2420
 
        self.store._load_from_string(b'')
 
2745
        self.store._load_from_string('')
2421
2746
 
2422
2747
    def assertIdempotent(self, s):
2423
2748
        """Assert that quoting an unquoted string is a no-op and vice-versa.
2449
2774
    def test_simple_comma(self):
2450
2775
        if isinstance(self.store, config.IniFileStore):
2451
2776
            # configobj requires that lists are special-cased
2452
 
            self.assertRaises(AssertionError,
2453
 
                              self.assertIdempotent, ',')
 
2777
           self.assertRaises(AssertionError,
 
2778
                             self.assertIdempotent, ',')
2454
2779
        else:
2455
2780
            self.assertIdempotent(',')
2456
2781
        # When a single comma is required, quoting is also required
2468
2793
class TestDictFromStore(tests.TestCase):
2469
2794
 
2470
2795
    def test_unquote_not_string(self):
2471
 
        conf = config.MemoryStack(b'x=2\n[a_section]\na=1\n')
 
2796
        conf = config.MemoryStack('x=2\n[a_section]\na=1\n')
2472
2797
        value = conf.get('a_section')
2473
2798
        # Urgh, despite 'conf' asking for the no-name section, we get the
2474
2799
        # content of another section as a dict o_O
2478
2803
        # are getting the value or displaying it. In the later case, '%s' will
2479
2804
        # do).
2480
2805
        self.assertEqual({'a': '1'}, unquoted)
2481
 
        self.assertIn('%s' % (unquoted,), ("{u'a': u'1'}", "{'a': '1'}"))
 
2806
        self.assertEqual("{u'a': u'1'}", '%s' % (unquoted,))
2482
2807
 
2483
2808
 
2484
2809
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2490
2815
    loaded. We need to issue proper error messages in this case.
2491
2816
    """
2492
2817
 
2493
 
    invalid_utf8_char = b'\xff'
 
2818
    invalid_utf8_char = '\xff'
2494
2819
 
2495
2820
    def test_load_utf8(self):
2496
2821
        """Ensure we can load an utf8-encoded file."""
2509
2834
    def test_load_non_ascii(self):
2510
2835
        """Ensure we display a proper error on non-ascii, non utf-8 content."""
2511
2836
        t = self.get_transport()
2512
 
        t.put_bytes('foo.conf', b'user=foo\n#%s\n' % (self.invalid_utf8_char,))
 
2837
        t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2513
2838
        store = config.TransportIniFileStore(t, 'foo.conf')
2514
 
        self.assertRaises(config.ConfigContentError, store.load)
 
2839
        self.assertRaises(errors.ConfigContentError, store.load)
2515
2840
 
2516
2841
    def test_load_erroneous_content(self):
2517
2842
        """Ensure we display a proper error on content that can't be parsed."""
2518
2843
        t = self.get_transport()
2519
 
        t.put_bytes('foo.conf', b'[open_section\n')
 
2844
        t.put_bytes('foo.conf', '[open_section\n')
2520
2845
        store = config.TransportIniFileStore(t, 'foo.conf')
2521
 
        self.assertRaises(config.ParseConfigError, store.load)
 
2846
        self.assertRaises(errors.ParseConfigError, store.load)
2522
2847
 
2523
2848
    def test_load_permission_denied(self):
2524
2849
        """Ensure we get warned when trying to load an inaccessible file."""
2525
2850
        warnings = []
2526
 
 
2527
2851
        def warning(*args):
2528
2852
            warnings.append(args[0] % args[1:])
2529
2853
        self.overrideAttr(trace, 'warning', warning)
2550
2874
    loaded. We need to issue proper error messages in this case.
2551
2875
    """
2552
2876
 
2553
 
    invalid_utf8_char = b'\xff'
 
2877
    invalid_utf8_char = '\xff'
2554
2878
 
2555
2879
    def test_load_utf8(self):
2556
2880
        """Ensure we can load an utf8-encoded file."""
2567
2891
    def test_load_badly_encoded_content(self):
2568
2892
        """Ensure we display a proper error on non-ascii, non utf-8 content."""
2569
2893
        with open('foo.conf', 'wb') as f:
2570
 
            f.write(b'user=foo\n#%s\n' % (self.invalid_utf8_char,))
 
2894
            f.write('user=foo\n#%s\n' % (self.invalid_utf8_char,))
2571
2895
        conf = config.IniBasedConfig(file_name='foo.conf')
2572
 
        self.assertRaises(config.ConfigContentError, conf._get_parser)
 
2896
        self.assertRaises(errors.ConfigContentError, conf._get_parser)
2573
2897
 
2574
2898
    def test_load_erroneous_content(self):
2575
2899
        """Ensure we display a proper error on content that can't be parsed."""
2576
2900
        with open('foo.conf', 'wb') as f:
2577
 
            f.write(b'[open_section\n')
 
2901
            f.write('[open_section\n')
2578
2902
        conf = config.IniBasedConfig(file_name='foo.conf')
2579
 
        self.assertRaises(config.ParseConfigError, conf._get_parser)
 
2903
        self.assertRaises(errors.ParseConfigError, conf._get_parser)
2580
2904
 
2581
2905
 
2582
2906
class TestMutableStore(TestStore):
2605
2929
 
2606
2930
    def test_mutable_section_shared(self):
2607
2931
        store = self.get_store(self)
2608
 
        store._load_from_string(b'foo=bar\n')
 
2932
        store._load_from_string('foo=bar\n')
2609
2933
        # FIXME: There should be a better way than relying on the test
2610
2934
        # parametrization to identify branch.conf -- vila 2011-0526
2611
2935
        if self.store_id in ('branch', 'remote_branch'):
2619
2943
 
2620
2944
    def test_save_emptied_succeeds(self):
2621
2945
        store = self.get_store(self)
2622
 
        store._load_from_string(b'foo=bar\n')
 
2946
        store._load_from_string('foo=bar\n')
2623
2947
        # FIXME: There should be a better way than relying on the test
2624
2948
        # parametrization to identify branch.conf -- vila 2011-0526
2625
2949
        if self.store_id in ('branch', 'remote_branch'):
2640
2964
            raise tests.TestNotApplicable(
2641
2965
                'branch.conf is *always* created when a branch is initialized')
2642
2966
        store = self.get_store(self)
2643
 
        store._load_from_string(b'foo=bar\n')
 
2967
        store._load_from_string('foo=bar\n')
2644
2968
        self.assertEqual(False, self.has_store(store))
2645
2969
        store.save()
2646
2970
        self.assertEqual(True, self.has_store(store))
2666
2990
 
2667
2991
    def test_set_option_in_default_section(self):
2668
2992
        store = self.get_store(self)
2669
 
        store._load_from_string(b'')
 
2993
        store._load_from_string('')
2670
2994
        # FIXME: There should be a better way than relying on the test
2671
2995
        # parametrization to identify branch.conf -- vila 2011-0526
2672
2996
        if self.store_id in ('branch', 'remote_branch'):
2682
3006
 
2683
3007
    def test_set_option_in_named_section(self):
2684
3008
        store = self.get_store(self)
2685
 
        store._load_from_string(b'')
 
3009
        store._load_from_string('')
2686
3010
        # FIXME: There should be a better way than relying on the test
2687
3011
        # parametrization to identify branch.conf -- vila 2011-0526
2688
3012
        if self.store_id in ('branch', 'remote_branch'):
2710
3034
        # Now we can try to load it
2711
3035
        store = self.get_store(self)
2712
3036
        calls = []
2713
 
 
2714
3037
        def hook(*args):
2715
3038
            calls.append(args)
2716
3039
        config.ConfigHooks.install_named_hook('load', hook, None)
2721
3044
 
2722
3045
    def test_save_hook(self):
2723
3046
        calls = []
2724
 
 
2725
3047
        def hook(*args):
2726
3048
            calls.append(args)
2727
3049
        config.ConfigHooks.install_named_hook('save', hook, None)
2739
3061
        self.assertEqual((store,), calls[0])
2740
3062
 
2741
3063
    def test_set_mark_dirty(self):
2742
 
        stack = config.MemoryStack(b'')
 
3064
        stack = config.MemoryStack('')
2743
3065
        self.assertLength(0, stack.store.dirty_sections)
2744
3066
        stack.set('foo', 'baz')
2745
3067
        self.assertLength(1, stack.store.dirty_sections)
2746
3068
        self.assertTrue(stack.store._need_saving())
2747
3069
 
2748
3070
    def test_remove_mark_dirty(self):
2749
 
        stack = config.MemoryStack(b'foo=bar')
 
3071
        stack = config.MemoryStack('foo=bar')
2750
3072
        self.assertLength(0, stack.store.dirty_sections)
2751
3073
        stack.remove('foo')
2752
3074
        self.assertLength(1, stack.store.dirty_sections)
2764
3086
        self.st1 = config.TransportIniFileStore(self.transport, 'foo.conf')
2765
3087
        self.st2 = config.TransportIniFileStore(self.transport, 'foo.conf')
2766
3088
        self.warnings = []
2767
 
 
2768
3089
        def warning(*args):
2769
3090
            self.warnings.append(args[0] % args[1:])
2770
3091
        self.overrideAttr(trace, 'warning', warning)
2816
3137
                            ' The baz value will be saved.')
2817
3138
 
2818
3139
    def test_concurrent_deletion(self):
2819
 
        self.st1._load_from_string(b'foo=bar')
 
3140
        self.st1._load_from_string('foo=bar')
2820
3141
        self.st1.save()
2821
3142
        s1 = self.get_stack(self.st1)
2822
3143
        s2 = self.get_stack(self.st2)
2841
3162
 
2842
3163
    def test_get_quoted_string(self):
2843
3164
        store = self.get_store()
2844
 
        store._load_from_string(b'foo= " abc "')
 
3165
        store._load_from_string('foo= " abc "')
2845
3166
        stack = config.Stack([store.get_sections])
2846
3167
        self.assertEqual(' abc ', stack.get('foo'))
2847
3168
 
2850
3171
        stack = config.Stack([store.get_sections], store)
2851
3172
        stack.set('foo', ' a b c ')
2852
3173
        store.save()
2853
 
        self.assertFileEqual(b'foo = " a b c "' +
2854
 
                             os.linesep.encode('ascii'), 'foo.conf')
 
3174
        self.assertFileEqual('foo = " a b c "' + os.linesep, 'foo.conf')
2855
3175
 
2856
3176
 
2857
3177
class TestTransportIniFileStore(TestStore):
2858
3178
 
2859
3179
    def test_loading_unknown_file_fails(self):
2860
3180
        store = config.TransportIniFileStore(self.get_transport(),
2861
 
                                             'I-do-not-exist')
 
3181
            'I-do-not-exist')
2862
3182
        self.assertRaises(errors.NoSuchFile, store.load)
2863
3183
 
2864
3184
    def test_invalid_content(self):
2865
3185
        store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
2866
3186
        self.assertEqual(False, store.is_loaded())
2867
3187
        exc = self.assertRaises(
2868
 
            config.ParseConfigError, store._load_from_string,
2869
 
            b'this is invalid !')
 
3188
            errors.ParseConfigError, store._load_from_string,
 
3189
            'this is invalid !')
2870
3190
        self.assertEndsWith(exc.filename, 'foo.conf')
2871
3191
        # And the load failed
2872
3192
        self.assertEqual(False, store.is_loaded())
2877
3197
        # FIXME: This should be fixed by forbidding dicts as values ?
2878
3198
        # -- vila 2011-04-05
2879
3199
        store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
2880
 
        store._load_from_string(b'''
 
3200
        store._load_from_string('''
2881
3201
foo=bar
2882
3202
l=1,2
2883
3203
[DEFAULT]
2981
3301
        after_writing = threading.Event()
2982
3302
        writing_done = threading.Event()
2983
3303
        c1_save_without_locking_orig = c1.store.save_without_locking
2984
 
 
2985
3304
        def c1_save_without_locking():
2986
3305
            before_writing.set()
2987
3306
            c1_save_without_locking_orig()
2989
3308
            # continue
2990
3309
            after_writing.wait()
2991
3310
        c1.store.save_without_locking = c1_save_without_locking
2992
 
 
2993
3311
        def c1_set():
2994
3312
            c1.set('one', 'c1')
2995
3313
            writing_done.set()
3010
3328
        self.assertEqual('c2', c2.get('one'))
3011
3329
 
3012
3330
    def test_read_while_writing(self):
3013
 
        c1 = self.stack
3014
 
        # We spawn a thread that will pause *during* the write
3015
 
        ready_to_write = threading.Event()
3016
 
        do_writing = threading.Event()
3017
 
        writing_done = threading.Event()
3018
 
        # We override the _save implementation so we know the store is locked
3019
 
        c1_save_without_locking_orig = c1.store.save_without_locking
3020
 
 
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
 
 
3030
 
        def c1_set():
3031
 
            c1.set('one', 'c1')
3032
 
        t1 = threading.Thread(target=c1_set)
3033
 
        # Collect the thread after the test
3034
 
        self.addCleanup(t1.join)
3035
 
        # Be ready to unblock the thread if the test goes wrong
3036
 
        self.addCleanup(do_writing.set)
3037
 
        t1.start()
3038
 
        # Ensure the thread is ready to write
3039
 
        ready_to_write.wait()
3040
 
        self.assertEqual('c1', c1.get('one'))
3041
 
        # If we read during the write, we get the old value
3042
 
        c2 = self.get_stack(self)
3043
 
        self.assertEqual('1', c2.get('one'))
3044
 
        # Let the writing occur and ensure it occurred
3045
 
        do_writing.set()
3046
 
        writing_done.wait()
3047
 
        # Now we get the updated value
3048
 
        c3 = self.get_stack(self)
3049
 
        self.assertEqual('c1', c3.get('one'))
 
3331
       c1 = self.stack
 
3332
       # We spawn a thread that will pause *during* the write
 
3333
       ready_to_write = threading.Event()
 
3334
       do_writing = threading.Event()
 
3335
       writing_done = threading.Event()
 
3336
       # We override the _save implementation so we know the store is locked
 
3337
       c1_save_without_locking_orig = c1.store.save_without_locking
 
3338
       def c1_save_without_locking():
 
3339
           ready_to_write.set()
 
3340
           # The lock is held. We wait for the main thread to decide when to
 
3341
           # continue
 
3342
           do_writing.wait()
 
3343
           c1_save_without_locking_orig()
 
3344
           writing_done.set()
 
3345
       c1.store.save_without_locking = c1_save_without_locking
 
3346
       def c1_set():
 
3347
           c1.set('one', 'c1')
 
3348
       t1 = threading.Thread(target=c1_set)
 
3349
       # Collect the thread after the test
 
3350
       self.addCleanup(t1.join)
 
3351
       # Be ready to unblock the thread if the test goes wrong
 
3352
       self.addCleanup(do_writing.set)
 
3353
       t1.start()
 
3354
       # Ensure the thread is ready to write
 
3355
       ready_to_write.wait()
 
3356
       self.assertEqual('c1', c1.get('one'))
 
3357
       # If we read during the write, we get the old value
 
3358
       c2 = self.get_stack(self)
 
3359
       self.assertEqual('1', c2.get('one'))
 
3360
       # Let the writing occur and ensure it occurred
 
3361
       do_writing.set()
 
3362
       writing_done.wait()
 
3363
       # Now we get the updated value
 
3364
       c3 = self.get_stack(self)
 
3365
       self.assertEqual('c1', c3.get('one'))
3050
3366
 
3051
3367
    # FIXME: It may be worth looking into removing the lock dir when it's not
3052
3368
    # needed anymore and look at possible fallouts for concurrent lockers. This
3053
 
    # will matter if/when we use config files outside of breezy directories
3054
 
    # (.config/breezy or .bzr) -- vila 20110-04-111
 
3369
    # will matter if/when we use config files outside of bazaar directories
 
3370
    # (.bazaar or .bzr) -- vila 20110-04-111
3055
3371
 
3056
3372
 
3057
3373
class TestSectionMatcher(TestStore):
3058
3374
 
3059
3375
    scenarios = [('location', {'matcher': config.LocationMatcher}),
3060
 
                 ('id', {'matcher': config.NameMatcher}), ]
 
3376
                 ('id', {'matcher': config.NameMatcher}),]
3061
3377
 
3062
3378
    def setUp(self):
3063
3379
        super(TestSectionMatcher, self).setUp()
3066
3382
 
3067
3383
    def test_no_matches_for_empty_stores(self):
3068
3384
        store = self.get_store(self)
3069
 
        store._load_from_string(b'')
 
3385
        store._load_from_string('')
3070
3386
        matcher = self.matcher(store, '/bar')
3071
3387
        self.assertEqual([], list(matcher.get_sections()))
3072
3388
 
3107
3423
 
3108
3424
    def test_unrelated_section_excluded(self):
3109
3425
        store = self.get_store(self)
3110
 
        store._load_from_string(b'''
 
3426
        store._load_from_string('''
3111
3427
[/foo]
3112
3428
section=/foo
3113
3429
[/foo/baz]
3120
3436
section=/quux/quux
3121
3437
''')
3122
3438
        self.assertEqual(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
3123
 
                          '/quux/quux'],
3124
 
                         [section.id for _, section in store.get_sections()])
 
3439
                           '/quux/quux'],
 
3440
                          [section.id for _, section in store.get_sections()])
3125
3441
        matcher = config.LocationMatcher(store, '/foo/bar/quux')
3126
3442
        sections = [section for _, section in matcher.get_sections()]
3127
3443
        self.assertEqual(['/foo/bar', '/foo'],
3128
 
                         [section.id for section in sections])
 
3444
                          [section.id for section in sections])
3129
3445
        self.assertEqual(['quux', 'bar/quux'],
3130
 
                         [section.extra_path for section in sections])
 
3446
                          [section.extra_path for section in sections])
3131
3447
 
3132
3448
    def test_more_specific_sections_first(self):
3133
3449
        store = self.get_store(self)
3134
 
        store._load_from_string(b'''
 
3450
        store._load_from_string('''
3135
3451
[/foo]
3136
3452
section=/foo
3137
3453
[/foo/bar]
3138
3454
section=/foo/bar
3139
3455
''')
3140
3456
        self.assertEqual(['/foo', '/foo/bar'],
3141
 
                         [section.id for _, section in store.get_sections()])
 
3457
                          [section.id for _, section in store.get_sections()])
3142
3458
        matcher = config.LocationMatcher(store, '/foo/bar/baz')
3143
3459
        sections = [section for _, section in matcher.get_sections()]
3144
3460
        self.assertEqual(['/foo/bar', '/foo'],
3145
 
                         [section.id for section in sections])
 
3461
                          [section.id for section in sections])
3146
3462
        self.assertEqual(['baz', 'bar/baz'],
3147
 
                         [section.extra_path for section in sections])
 
3463
                          [section.extra_path for section in sections])
3148
3464
 
3149
3465
    def test_appendpath_in_no_name_section(self):
3150
3466
        # It's a bit weird to allow appendpath in a no-name section, but
3151
3467
        # someone may found a use for it
3152
3468
        store = self.get_store(self)
3153
 
        store._load_from_string(b'''
 
3469
        store._load_from_string('''
3154
3470
foo=bar
3155
3471
foo:policy = appendpath
3156
3472
''')
3175
3491
        store._load_from_string(dedent("""\
3176
3492
            [/]
3177
3493
            push_location=my{branchname}
3178
 
        """).encode('ascii'))
 
3494
        """))
3179
3495
        matcher = config.LocationMatcher(store, 'file:///,branch=example%3c')
3180
3496
        self.assertEqual('example<', matcher.branch_name)
3181
3497
        ((_, section),) = matcher.get_sections()
3186
3502
        store._load_from_string(dedent("""\
3187
3503
            [/]
3188
3504
            push_location=my{branchname}
3189
 
        """).encode('ascii'))
 
3505
        """))
3190
3506
        matcher = config.LocationMatcher(store, 'file:///parent/example%3c')
3191
3507
        self.assertEqual('example<', matcher.branch_name)
3192
3508
        ((_, section),) = matcher.get_sections()
3209
3525
        return sections
3210
3526
 
3211
3527
    def test_empty(self):
3212
 
        self.assertSectionIDs([], self.get_url(), b'')
 
3528
        self.assertSectionIDs([], self.get_url(), '')
3213
3529
 
3214
3530
    def test_url_vs_local_paths(self):
3215
3531
        # The matcher location is an url and the section names are local paths
3216
3532
        self.assertSectionIDs(['/foo/bar', '/foo'],
3217
 
                              'file:///foo/bar/baz', b'''\
 
3533
                              'file:///foo/bar/baz', '''\
3218
3534
[/foo]
3219
3535
[/foo/bar]
3220
3536
''')
3222
3538
    def test_local_path_vs_url(self):
3223
3539
        # The matcher location is a local path and the section names are urls
3224
3540
        self.assertSectionIDs(['file:///foo/bar', 'file:///foo'],
3225
 
                              '/foo/bar/baz', b'''\
 
3541
                              '/foo/bar/baz', '''\
3226
3542
[file:///foo]
3227
3543
[file:///foo/bar]
3228
3544
''')
3229
3545
 
 
3546
 
3230
3547
    def test_no_name_section_included_when_present(self):
3231
3548
        # Note that other tests will cover the case where the no-name section
3232
3549
        # is empty and as such, not included.
3233
3550
        sections = self.assertSectionIDs(['/foo/bar', '/foo', None],
3234
 
                                         '/foo/bar/baz', b'''\
 
3551
                                         '/foo/bar/baz', '''\
3235
3552
option = defined so the no-name section exists
3236
3553
[/foo]
3237
3554
[/foo/bar]
3238
3555
''')
3239
3556
        self.assertEqual(['baz', 'bar/baz', '/foo/bar/baz'],
3240
 
                         [s.locals['relpath'] for _, s in sections])
 
3557
                          [s.locals['relpath'] for _, s in sections])
3241
3558
 
3242
3559
    def test_order_reversed(self):
3243
 
        self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', b'''\
 
3560
        self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
3244
3561
[/foo]
3245
3562
[/foo/bar]
3246
3563
''')
3247
3564
 
3248
3565
    def test_unrelated_section_excluded(self):
3249
 
        self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', b'''\
 
3566
        self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
3250
3567
[/foo]
3251
3568
[/foo/qux]
3252
3569
[/foo/bar]
3254
3571
 
3255
3572
    def test_glob_included(self):
3256
3573
        sections = self.assertSectionIDs(['/foo/*/baz', '/foo/b*', '/foo'],
3257
 
                                         '/foo/bar/baz', b'''\
 
3574
                                         '/foo/bar/baz', '''\
3258
3575
[/foo]
3259
3576
[/foo/qux]
3260
3577
[/foo/b*]
3264
3581
        # nothing really is... as far using {relpath} to append it to something
3265
3582
        # else, this seems good enough though.
3266
3583
        self.assertEqual(['', 'baz', 'bar/baz'],
3267
 
                         [s.locals['relpath'] for _, s in sections])
 
3584
                          [s.locals['relpath'] for _, s in sections])
3268
3585
 
3269
3586
    def test_respect_order(self):
3270
3587
        self.assertSectionIDs(['/foo', '/foo/b*', '/foo/*/baz'],
3271
 
                              '/foo/bar/baz', b'''\
 
3588
                              '/foo/bar/baz', '''\
3272
3589
[/foo/*/baz]
3273
3590
[/foo/qux]
3274
3591
[/foo/b*]
3286
3603
 
3287
3604
    def get_matching_sections(self, name):
3288
3605
        store = self.get_store(self)
3289
 
        store._load_from_string(b'''
 
3606
        store._load_from_string('''
3290
3607
[foo]
3291
3608
option=foo
3292
3609
[foo/baz]
3315
3632
 
3316
3633
    def test_get_first_definition(self):
3317
3634
        store1 = config.IniFileStore()
3318
 
        store1._load_from_string(b'foo=bar')
 
3635
        store1._load_from_string('foo=bar')
3319
3636
        store2 = config.IniFileStore()
3320
 
        store2._load_from_string(b'foo=baz')
 
3637
        store2._load_from_string('foo=baz')
3321
3638
        conf = config.Stack([store1.get_sections, store2.get_sections])
3322
3639
        self.assertEqual('bar', conf.get('foo'))
3323
3640
 
3336
3653
        self.assertEqual(None, conf_stack.get('foo'))
3337
3654
 
3338
3655
    def test_get_for_empty_section_callable(self):
3339
 
        conf_stack = config.Stack([lambda: []])
 
3656
        conf_stack = config.Stack([lambda : []])
3340
3657
        self.assertEqual(None, conf_stack.get('foo'))
3341
3658
 
3342
3659
    def test_get_for_broken_callable(self):
3361
3678
            config.Option('foo', default='bar', override_from_env=['FOO']))
3362
3679
        self.overrideEnv('FOO', 'quux')
3363
3680
        # Env variable provides a default taking over the option one
3364
 
        conf = self.get_conf(b'foo=store')
 
3681
        conf = self.get_conf('foo=store')
3365
3682
        self.assertEqual('quux', conf.get('foo'))
3366
3683
 
3367
3684
    def test_first_override_value_from_env_wins(self):
3374
3691
        self.overrideEnv('FOO', 'foo')
3375
3692
        self.overrideEnv('BAZ', 'baz')
3376
3693
        # The first env var set wins
3377
 
        conf = self.get_conf(b'foo=store')
 
3694
        conf = self.get_conf('foo=store')
3378
3695
        self.assertEqual('foo', conf.get('foo'))
3379
3696
 
3380
3697
 
3381
3698
class TestMemoryStack(tests.TestCase):
3382
3699
 
3383
3700
    def test_get(self):
3384
 
        conf = config.MemoryStack(b'foo=bar')
 
3701
        conf = config.MemoryStack('foo=bar')
3385
3702
        self.assertEqual('bar', conf.get('foo'))
3386
3703
 
3387
3704
    def test_set(self):
3388
 
        conf = config.MemoryStack(b'foo=bar')
 
3705
        conf = config.MemoryStack('foo=bar')
3389
3706
        conf.set('foo', 'baz')
3390
3707
        self.assertEqual('baz', conf.get('foo'))
3391
3708
 
3395
3712
        self.assertFalse(conf.store.is_loaded())
3396
3713
        self.assertRaises(NotImplementedError, conf.get, 'foo')
3397
3714
        # But a content can still be provided
3398
 
        conf.store._load_from_string(b'foo=bar')
 
3715
        conf.store._load_from_string('foo=bar')
3399
3716
        self.assertEqual('bar', conf.get('foo'))
3400
3717
 
3401
3718
 
3408
3725
 
3409
3726
    def test_empty_store(self):
3410
3727
        store = config.IniFileStore()
3411
 
        store._load_from_string(b'')
 
3728
        store._load_from_string('')
3412
3729
        conf = config.Stack([store.get_sections])
3413
3730
        sections = list(conf.iter_sections())
3414
3731
        self.assertLength(0, sections)
3415
3732
 
3416
3733
    def test_simple_store(self):
3417
3734
        store = config.IniFileStore()
3418
 
        store._load_from_string(b'foo=bar')
 
3735
        store._load_from_string('foo=bar')
3419
3736
        conf = config.Stack([store.get_sections])
3420
3737
        tuples = list(conf.iter_sections())
3421
3738
        self.assertLength(1, tuples)
3424
3741
 
3425
3742
    def test_two_stores(self):
3426
3743
        store1 = config.IniFileStore()
3427
 
        store1._load_from_string(b'foo=bar')
 
3744
        store1._load_from_string('foo=bar')
3428
3745
        store2 = config.IniFileStore()
3429
 
        store2._load_from_string(b'bar=qux')
 
3746
        store2._load_from_string('bar=qux')
3430
3747
        conf = config.Stack([store1.get_sections, store2.get_sections])
3431
3748
        tuples = list(conf.iter_sections())
3432
3749
        self.assertLength(2, tuples)
3459
3776
    def test_get_hook(self):
3460
3777
        self.conf.set('foo', 'bar')
3461
3778
        calls = []
3462
 
 
3463
3779
        def hook(*args):
3464
3780
            calls.append(args)
3465
3781
        config.ConfigHooks.install_named_hook('get', hook, None)
3488
3804
 
3489
3805
    def test_get_default_bool_None(self):
3490
3806
        self.register_bool_option('foo')
3491
 
        conf = self.get_conf(b'')
 
3807
        conf = self.get_conf('')
3492
3808
        self.assertEqual(None, conf.get('foo'))
3493
3809
 
3494
3810
    def test_get_default_bool_True(self):
3495
3811
        self.register_bool_option('foo', u'True')
3496
 
        conf = self.get_conf(b'')
 
3812
        conf = self.get_conf('')
3497
3813
        self.assertEqual(True, conf.get('foo'))
3498
3814
 
3499
3815
    def test_get_default_bool_False(self):
3500
3816
        self.register_bool_option('foo', False)
3501
 
        conf = self.get_conf(b'')
 
3817
        conf = self.get_conf('')
3502
3818
        self.assertEqual(False, conf.get('foo'))
3503
3819
 
3504
3820
    def test_get_default_bool_False_as_string(self):
3505
3821
        self.register_bool_option('foo', u'False')
3506
 
        conf = self.get_conf(b'')
 
3822
        conf = self.get_conf('')
3507
3823
        self.assertEqual(False, conf.get('foo'))
3508
3824
 
3509
3825
    def test_get_default_bool_from_env_converted(self):
3510
3826
        self.register_bool_option('foo', u'True', default_from_env=['FOO'])
3511
3827
        self.overrideEnv('FOO', 'False')
3512
 
        conf = self.get_conf(b'')
 
3828
        conf = self.get_conf('')
3513
3829
        self.assertEqual(False, conf.get('foo'))
3514
3830
 
3515
3831
    def test_get_default_bool_when_conversion_fails(self):
3516
3832
        self.register_bool_option('foo', default='True')
3517
 
        conf = self.get_conf(b'foo=invalid boolean')
 
3833
        conf = self.get_conf('foo=invalid boolean')
3518
3834
        self.assertEqual(True, conf.get('foo'))
3519
3835
 
3520
3836
    def register_integer_option(self, name,
3526
3842
 
3527
3843
    def test_get_default_integer_None(self):
3528
3844
        self.register_integer_option('foo')
3529
 
        conf = self.get_conf(b'')
 
3845
        conf = self.get_conf('')
3530
3846
        self.assertEqual(None, conf.get('foo'))
3531
3847
 
3532
3848
    def test_get_default_integer(self):
3533
3849
        self.register_integer_option('foo', 42)
3534
 
        conf = self.get_conf(b'')
 
3850
        conf = self.get_conf('')
3535
3851
        self.assertEqual(42, conf.get('foo'))
3536
3852
 
3537
3853
    def test_get_default_integer_as_string(self):
3538
3854
        self.register_integer_option('foo', u'42')
3539
 
        conf = self.get_conf(b'')
 
3855
        conf = self.get_conf('')
3540
3856
        self.assertEqual(42, conf.get('foo'))
3541
3857
 
3542
3858
    def test_get_default_integer_from_env(self):
3543
3859
        self.register_integer_option('foo', default_from_env=['FOO'])
3544
3860
        self.overrideEnv('FOO', '18')
3545
 
        conf = self.get_conf(b'')
 
3861
        conf = self.get_conf('')
3546
3862
        self.assertEqual(18, conf.get('foo'))
3547
3863
 
3548
3864
    def test_get_default_integer_when_conversion_fails(self):
3549
3865
        self.register_integer_option('foo', default='12')
3550
 
        conf = self.get_conf(b'foo=invalid integer')
 
3866
        conf = self.get_conf('foo=invalid integer')
3551
3867
        self.assertEqual(12, conf.get('foo'))
3552
3868
 
3553
3869
    def register_list_option(self, name, default=None, default_from_env=None):
3557
3873
 
3558
3874
    def test_get_default_list_None(self):
3559
3875
        self.register_list_option('foo')
3560
 
        conf = self.get_conf(b'')
 
3876
        conf = self.get_conf('')
3561
3877
        self.assertEqual(None, conf.get('foo'))
3562
3878
 
3563
3879
    def test_get_default_list_empty(self):
3564
3880
        self.register_list_option('foo', '')
3565
 
        conf = self.get_conf(b'')
 
3881
        conf = self.get_conf('')
3566
3882
        self.assertEqual([], conf.get('foo'))
3567
3883
 
3568
3884
    def test_get_default_list_from_env(self):
3569
3885
        self.register_list_option('foo', default_from_env=['FOO'])
3570
3886
        self.overrideEnv('FOO', '')
3571
 
        conf = self.get_conf(b'')
 
3887
        conf = self.get_conf('')
3572
3888
        self.assertEqual([], conf.get('foo'))
3573
3889
 
3574
3890
    def test_get_with_list_converter_no_item(self):
3575
3891
        self.register_list_option('foo', None)
3576
 
        conf = self.get_conf(b'foo=,')
 
3892
        conf = self.get_conf('foo=,')
3577
3893
        self.assertEqual([], conf.get('foo'))
3578
3894
 
3579
3895
    def test_get_with_list_converter_many_items(self):
3580
3896
        self.register_list_option('foo', None)
3581
 
        conf = self.get_conf(b'foo=m,o,r,e')
 
3897
        conf = self.get_conf('foo=m,o,r,e')
3582
3898
        self.assertEqual(['m', 'o', 'r', 'e'], conf.get('foo'))
3583
3899
 
3584
3900
    def test_get_with_list_converter_embedded_spaces_many_items(self):
3585
3901
        self.register_list_option('foo', None)
3586
 
        conf = self.get_conf(b'foo=" bar", "baz "')
 
3902
        conf = self.get_conf('foo=" bar", "baz "')
3587
3903
        self.assertEqual([' bar', 'baz '], conf.get('foo'))
3588
3904
 
3589
3905
    def test_get_with_list_converter_stripped_spaces_many_items(self):
3590
3906
        self.register_list_option('foo', None)
3591
 
        conf = self.get_conf(b'foo= bar ,  baz ')
 
3907
        conf = self.get_conf('foo= bar ,  baz ')
3592
3908
        self.assertEqual(['bar', 'baz'], conf.get('foo'))
3593
3909
 
3594
3910
 
3617
3933
    def test_two_refs(self):
3618
3934
        self.assertRefs([(False, ''), (True, '{foo}'),
3619
3935
                         (False, ''), (True, '{bar}'),
3620
 
                         (False, ''), ],
 
3936
                         (False, ''),],
3621
3937
                        '{foo}{bar}')
3622
3938
 
3623
3939
    def test_newline_in_refs_are_not_matched(self):
3640
3956
        self.assertExpansion('foo', 'foo')
3641
3957
 
3642
3958
    def test_expand_default_value(self):
3643
 
        self.conf.store._load_from_string(b'bar=baz')
 
3959
        self.conf.store._load_from_string('bar=baz')
3644
3960
        self.registry.register(config.Option('foo', default=u'{bar}'))
3645
3961
        self.assertEqual('baz', self.conf.get('foo', expand=True))
3646
3962
 
3647
3963
    def test_expand_default_from_env(self):
3648
 
        self.conf.store._load_from_string(b'bar=baz')
 
3964
        self.conf.store._load_from_string('bar=baz')
3649
3965
        self.registry.register(config.Option('foo', default_from_env=['FOO']))
3650
3966
        self.overrideEnv('FOO', '{bar}')
3651
3967
        self.assertEqual('baz', self.conf.get('foo', expand=True))
3652
3968
 
3653
3969
    def test_expand_default_on_failed_conversion(self):
3654
 
        self.conf.store._load_from_string(b'baz=bogus\nbar=42\nfoo={baz}')
 
3970
        self.conf.store._load_from_string('baz=bogus\nbar=42\nfoo={baz}')
3655
3971
        self.registry.register(
3656
3972
            config.Option('foo', default=u'{bar}',
3657
3973
                          from_unicode=config.int_from_store))
3661
3977
        self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3662
3978
 
3663
3979
    def test_env_overriding_options(self):
3664
 
        self.conf.store._load_from_string(b'foo=baz')
 
3980
        self.conf.store._load_from_string('foo=baz')
3665
3981
        self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3666
3982
 
3667
3983
    def test_simple_ref(self):
3668
 
        self.conf.store._load_from_string(b'foo=xxx')
 
3984
        self.conf.store._load_from_string('foo=xxx')
3669
3985
        self.assertExpansion('xxx', '{foo}')
3670
3986
 
3671
3987
    def test_unknown_ref(self):
3672
 
        self.assertRaises(config.ExpandingUnknownOption,
 
3988
        self.assertRaises(errors.ExpandingUnknownOption,
3673
3989
                          self.conf.expand_options, '{foo}')
3674
3990
 
3675
3991
    def test_illegal_def_is_ignored(self):
3678
3994
        self.assertExpansion('${Foo,f}', '${Foo,f}')
3679
3995
 
3680
3996
    def test_indirect_ref(self):
3681
 
        self.conf.store._load_from_string(b'''
 
3997
        self.conf.store._load_from_string('''
3682
3998
foo=xxx
3683
3999
bar={foo}
3684
4000
''')
3685
4001
        self.assertExpansion('xxx', '{bar}')
3686
4002
 
3687
4003
    def test_embedded_ref(self):
3688
 
        self.conf.store._load_from_string(b'''
 
4004
        self.conf.store._load_from_string('''
3689
4005
foo=xxx
3690
4006
bar=foo
3691
4007
''')
3692
4008
        self.assertExpansion('xxx', '{{bar}}')
3693
4009
 
3694
4010
    def test_simple_loop(self):
3695
 
        self.conf.store._load_from_string(b'foo={foo}')
3696
 
        self.assertRaises(config.OptionExpansionLoop,
 
4011
        self.conf.store._load_from_string('foo={foo}')
 
4012
        self.assertRaises(errors.OptionExpansionLoop,
3697
4013
                          self.conf.expand_options, '{foo}')
3698
4014
 
3699
4015
    def test_indirect_loop(self):
3700
 
        self.conf.store._load_from_string(b'''
 
4016
        self.conf.store._load_from_string('''
3701
4017
foo={bar}
3702
4018
bar={baz}
3703
4019
baz={foo}''')
3704
 
        e = self.assertRaises(config.OptionExpansionLoop,
 
4020
        e = self.assertRaises(errors.OptionExpansionLoop,
3705
4021
                              self.conf.expand_options, '{foo}')
3706
4022
        self.assertEqual('foo->bar->baz', e.refs)
3707
4023
        self.assertEqual('{foo}', e.string)
3708
4024
 
3709
4025
    def test_list(self):
3710
 
        self.conf.store._load_from_string(b'''
 
4026
        self.conf.store._load_from_string('''
3711
4027
foo=start
3712
4028
bar=middle
3713
4029
baz=end
3716
4032
        self.registry.register(
3717
4033
            config.ListOption('list'))
3718
4034
        self.assertEqual(['start', 'middle', 'end'],
3719
 
                         self.conf.get('list', expand=True))
 
4035
                           self.conf.get('list', expand=True))
3720
4036
 
3721
4037
    def test_cascading_list(self):
3722
 
        self.conf.store._load_from_string(b'''
 
4038
        self.conf.store._load_from_string('''
3723
4039
foo=start,{bar}
3724
4040
bar=middle,{baz}
3725
4041
baz=end
3731
4047
        # option ('list' here).
3732
4048
        self.registry.register(config.ListOption('baz'))
3733
4049
        self.assertEqual(['start', 'middle', 'end'],
3734
 
                         self.conf.get('list', expand=True))
 
4050
                           self.conf.get('list', expand=True))
3735
4051
 
3736
4052
    def test_pathologically_hidden_list(self):
3737
 
        self.conf.store._load_from_string(b'''
 
4053
        self.conf.store._load_from_string('''
3738
4054
foo=bin
3739
4055
bar=go
3740
4056
start={foo
3746
4062
        # only after all expansions have been performed
3747
4063
        self.registry.register(config.ListOption('hidden'))
3748
4064
        self.assertEqual(['bin', 'go'],
3749
 
                         self.conf.get('hidden', expand=True))
 
4065
                          self.conf.get('hidden', expand=True))
3750
4066
 
3751
4067
 
3752
4068
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
3756
4072
 
3757
4073
    def get_config(self, location, string):
3758
4074
        if string is None:
3759
 
            string = b''
 
4075
            string = ''
3760
4076
        # Since we don't save the config we won't strictly require to inherit
3761
4077
        # from TestCaseInTempDir, but an error occurs so quickly...
3762
4078
        c = config.LocationStack(location)
3764
4080
        return c
3765
4081
 
3766
4082
    def test_dont_cross_unrelated_section(self):
3767
 
        c = self.get_config('/another/branch/path', b'''
 
4083
        c = self.get_config('/another/branch/path','''
3768
4084
[/one/branch/path]
3769
4085
foo = hello
3770
4086
bar = {foo}/2
3772
4088
[/another/branch/path]
3773
4089
bar = {foo}/2
3774
4090
''')
3775
 
        self.assertRaises(config.ExpandingUnknownOption,
 
4091
        self.assertRaises(errors.ExpandingUnknownOption,
3776
4092
                          c.get, 'bar', expand=True)
3777
4093
 
3778
4094
    def test_cross_related_sections(self):
3779
 
        c = self.get_config('/project/branch/path', b'''
 
4095
        c = self.get_config('/project/branch/path','''
3780
4096
[/project]
3781
4097
foo = qu
3782
4098
 
3790
4106
 
3791
4107
    def test_cross_global_locations(self):
3792
4108
        l_store = config.LocationStore()
3793
 
        l_store._load_from_string(b'''
 
4109
        l_store._load_from_string('''
3794
4110
[/branch]
3795
4111
lfoo = loc-foo
3796
4112
lbar = {gbar}
3797
4113
''')
3798
4114
        l_store.save()
3799
4115
        g_store = config.GlobalStore()
3800
 
        g_store._load_from_string(b'''
 
4116
        g_store._load_from_string('''
3801
4117
[DEFAULT]
3802
4118
gfoo = {lfoo}
3803
4119
gbar = glob-bar
3812
4128
 
3813
4129
    def test_expand_locals_empty(self):
3814
4130
        l_store = config.LocationStore()
3815
 
        l_store._load_from_string(b'''
 
4131
        l_store._load_from_string('''
3816
4132
[/home/user/project]
3817
4133
base = {basename}
3818
4134
rel = {relpath}
3824
4140
 
3825
4141
    def test_expand_basename_locally(self):
3826
4142
        l_store = config.LocationStore()
3827
 
        l_store._load_from_string(b'''
 
4143
        l_store._load_from_string('''
3828
4144
[/home/user/project]
3829
4145
bfoo = {basename}
3830
4146
''')
3834
4150
 
3835
4151
    def test_expand_basename_locally_longer_path(self):
3836
4152
        l_store = config.LocationStore()
3837
 
        l_store._load_from_string(b'''
 
4153
        l_store._load_from_string('''
3838
4154
[/home/user]
3839
4155
bfoo = {basename}
3840
4156
''')
3844
4160
 
3845
4161
    def test_expand_relpath_locally(self):
3846
4162
        l_store = config.LocationStore()
3847
 
        l_store._load_from_string(b'''
 
4163
        l_store._load_from_string('''
3848
4164
[/home/user/project]
3849
4165
lfoo = loc-foo/{relpath}
3850
4166
''')
3854
4170
 
3855
4171
    def test_expand_relpath_unknonw_in_global(self):
3856
4172
        g_store = config.GlobalStore()
3857
 
        g_store._load_from_string(b'''
 
4173
        g_store._load_from_string('''
3858
4174
[DEFAULT]
3859
4175
gfoo = {relpath}
3860
4176
''')
3861
4177
        g_store.save()
3862
4178
        stack = config.LocationStack('/home/user/project/branch')
3863
 
        self.assertRaises(config.ExpandingUnknownOption,
 
4179
        self.assertRaises(errors.ExpandingUnknownOption,
3864
4180
                          stack.get, 'gfoo', expand=True)
3865
4181
 
3866
4182
    def test_expand_local_option_locally(self):
3867
4183
        l_store = config.LocationStore()
3868
 
        l_store._load_from_string(b'''
 
4184
        l_store._load_from_string('''
3869
4185
[/home/user/project]
3870
4186
lfoo = loc-foo/{relpath}
3871
4187
lbar = {gbar}
3872
4188
''')
3873
4189
        l_store.save()
3874
4190
        g_store = config.GlobalStore()
3875
 
        g_store._load_from_string(b'''
 
4191
        g_store._load_from_string('''
3876
4192
[DEFAULT]
3877
4193
gfoo = {lfoo}
3878
4194
gbar = glob-bar
3886
4202
        """Make sure we chose the right local in presence of several sections.
3887
4203
        """
3888
4204
        l_store = config.LocationStore()
3889
 
        l_store._load_from_string(b'''
 
4205
        l_store._load_from_string('''
3890
4206
[/home/user]
3891
4207
lfoo = loc-foo/{relpath}
3892
4208
[/home/user/project]
3899
4215
        self.assertEqual('loc-foo/bar/baz', stack.get('lfoo', expand=True))
3900
4216
 
3901
4217
 
 
4218
 
3902
4219
class TestStackSet(TestStackWithTransport):
3903
4220
 
3904
4221
    def test_simple_set(self):
3915
4232
 
3916
4233
    def test_set_hook(self):
3917
4234
        calls = []
3918
 
 
3919
4235
        def hook(*args):
3920
4236
            calls.append(args)
3921
4237
        config.ConfigHooks.install_named_hook('set', hook, None)
3942
4258
 
3943
4259
    def test_remove_hook(self):
3944
4260
        calls = []
3945
 
 
3946
4261
        def hook(*args):
3947
4262
            calls.append(args)
3948
4263
        config.ConfigHooks.install_named_hook('remove', hook, None)
3961
4276
        create_configs(self)
3962
4277
 
3963
4278
    def test_no_variable(self):
3964
 
        # Using branch should query branch, locations and breezy
 
4279
        # Using branch should query branch, locations and bazaar
3965
4280
        self.assertOptions([], self.branch_config)
3966
4281
 
3967
 
    def test_option_in_breezy(self):
3968
 
        self.breezy_config.set_user_option('file', 'breezy')
3969
 
        self.assertOptions([('file', 'breezy', 'DEFAULT', 'breezy')],
3970
 
                           self.breezy_config)
 
4282
    def test_option_in_bazaar(self):
 
4283
        self.bazaar_config.set_user_option('file', 'bazaar')
 
4284
        self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
 
4285
                           self.bazaar_config)
3971
4286
 
3972
4287
    def test_option_in_locations(self):
3973
4288
        self.locations_config.set_user_option('file', 'locations')
3980
4295
        self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
3981
4296
                           self.branch_config)
3982
4297
 
3983
 
    def test_option_in_breezy_and_branch(self):
3984
 
        self.breezy_config.set_user_option('file', 'breezy')
 
4298
    def test_option_in_bazaar_and_branch(self):
 
4299
        self.bazaar_config.set_user_option('file', 'bazaar')
3985
4300
        self.branch_config.set_user_option('file', 'branch')
3986
4301
        self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
3987
 
                            ('file', 'breezy', 'DEFAULT', 'breezy'), ],
 
4302
                            ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3988
4303
                           self.branch_config)
3989
4304
 
3990
4305
    def test_option_in_branch_and_locations(self):
3993
4308
        self.branch_config.set_user_option('file', 'branch')
3994
4309
        self.assertOptions(
3995
4310
            [('file', 'locations', self.tree.basedir, 'locations'),
3996
 
             ('file', 'branch', 'DEFAULT', 'branch'), ],
 
4311
             ('file', 'branch', 'DEFAULT', 'branch'),],
3997
4312
            self.branch_config)
3998
4313
 
3999
 
    def test_option_in_breezy_locations_and_branch(self):
4000
 
        self.breezy_config.set_user_option('file', 'breezy')
 
4314
    def test_option_in_bazaar_locations_and_branch(self):
 
4315
        self.bazaar_config.set_user_option('file', 'bazaar')
4001
4316
        self.locations_config.set_user_option('file', 'locations')
4002
4317
        self.branch_config.set_user_option('file', 'branch')
4003
4318
        self.assertOptions(
4004
4319
            [('file', 'locations', self.tree.basedir, 'locations'),
4005
4320
             ('file', 'branch', 'DEFAULT', 'branch'),
4006
 
             ('file', 'breezy', 'DEFAULT', 'breezy'), ],
 
4321
             ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4007
4322
            self.branch_config)
4008
4323
 
4009
4324
 
4017
4332
        self.locations_config.remove_user_option('file', self.tree.basedir)
4018
4333
        self.assertOptions(
4019
4334
            [('file', 'branch', 'DEFAULT', 'branch'),
4020
 
             ('file', 'breezy', 'DEFAULT', 'breezy'), ],
 
4335
             ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4021
4336
            self.branch_config)
4022
4337
 
4023
4338
    def test_remove_in_branch(self):
4024
4339
        self.branch_config.remove_user_option('file')
4025
4340
        self.assertOptions(
4026
4341
            [('file', 'locations', self.tree.basedir, 'locations'),
4027
 
             ('file', 'breezy', 'DEFAULT', 'breezy'), ],
 
4342
             ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4028
4343
            self.branch_config)
4029
4344
 
4030
 
    def test_remove_in_breezy(self):
4031
 
        self.breezy_config.remove_user_option('file')
 
4345
    def test_remove_in_bazaar(self):
 
4346
        self.bazaar_config.remove_user_option('file')
4032
4347
        self.assertOptions(
4033
4348
            [('file', 'locations', self.tree.basedir, 'locations'),
4034
 
             ('file', 'branch', 'DEFAULT', 'branch'), ],
 
4349
             ('file', 'branch', 'DEFAULT', 'branch'),],
4035
4350
            self.branch_config)
4036
4351
 
4037
4352
 
4057
4372
        self.assertLength(len(expected), sections)
4058
4373
        self.assertEqual(expected, [n for n, _, _ in sections])
4059
4374
 
4060
 
    def test_breezy_default_section(self):
4061
 
        self.assertSectionNames(['DEFAULT'], self.breezy_config)
 
4375
    def test_bazaar_default_section(self):
 
4376
        self.assertSectionNames(['DEFAULT'], self.bazaar_config)
4062
4377
 
4063
4378
    def test_locations_default_section(self):
4064
4379
        # No sections are defined in an empty file
4098
4413
        self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
4099
4414
                                self.branch_config)
4100
4415
 
4101
 
    def test_breezy_named_section(self):
 
4416
    def test_bazaar_named_section(self):
4102
4417
        # We need to cheat as the API doesn't give direct access to sections
4103
4418
        # other than DEFAULT.
4104
 
        self.breezy_config.set_alias('breezy', 'bzr')
4105
 
        self.assertSectionNames(['ALIASES'], self.breezy_config, 'ALIASES')
 
4419
        self.bazaar_config.set_alias('bazaar', 'bzr')
 
4420
        self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
4106
4421
 
4107
4422
 
4108
4423
class TestSharedStores(tests.TestCaseInTempDir):
4109
4424
 
4110
 
    def test_breezy_conf_shared(self):
 
4425
    def test_bazaar_conf_shared(self):
4111
4426
        g1 = config.GlobalStack()
4112
4427
        g2 = config.GlobalStack()
4113
4428
        # The two stacks share the same store
4114
4429
        self.assertIs(g1.store, g2.store)
4115
4430
 
4116
4431
 
4117
 
class TestAuthenticationConfigFilePermissions(tests.TestCaseInTempDir):
4118
 
    """Test warning for permissions of authentication.conf."""
4119
 
 
4120
 
    def setUp(self):
4121
 
        super(TestAuthenticationConfigFilePermissions, self).setUp()
4122
 
        self.path = osutils.pathjoin(self.test_dir, 'authentication.conf')
4123
 
        with open(self.path, 'wb') as f:
4124
 
            f.write(b"""[broken]
4125
 
scheme=ftp
4126
 
user=joe
4127
 
port=port # Error: Not an int
4128
 
""")
4129
 
        self.overrideAttr(bedding, 'authentication_config_path',
4130
 
                          lambda: self.path)
4131
 
        osutils.chmod_if_possible(self.path, 0o755)
4132
 
 
4133
 
    def test_check_warning(self):
4134
 
        conf = config.AuthenticationConfig()
4135
 
        self.assertEqual(conf._filename, self.path)
4136
 
        self.assertContainsRe(self.get_log(),
4137
 
                              'Saved passwords may be accessible by other users.')
4138
 
 
4139
 
    def test_check_suppressed_warning(self):
4140
 
        global_config = config.GlobalConfig()
4141
 
        global_config.set_user_option('suppress_warnings',
4142
 
                                      'insecure_permissions')
4143
 
        conf = config.AuthenticationConfig()
4144
 
        self.assertEqual(conf._filename, self.path)
4145
 
        self.assertNotContainsRe(self.get_log(),
4146
 
                                 'Saved passwords may be accessible by other users.')
4147
 
 
4148
 
 
4149
4432
class TestAuthenticationConfigFile(tests.TestCase):
4150
4433
    """Test the authentication.conf file matching"""
4151
4434
 
4162
4445
        self.assertEqual(expected_password, password)
4163
4446
 
4164
4447
    def test_empty_config(self):
4165
 
        conf = config.AuthenticationConfig(_file=BytesIO())
 
4448
        conf = config.AuthenticationConfig(_file=StringIO())
4166
4449
        self.assertEqual({}, conf._get_config())
4167
4450
        self._got_user_passwd(None, None, conf, 'http', 'foo.net')
4168
4451
 
4169
4452
    def test_non_utf8_config(self):
4170
 
        conf = config.AuthenticationConfig(_file=BytesIO(b'foo = bar\xff'))
4171
 
        self.assertRaises(config.ConfigContentError, conf._get_config)
 
4453
        conf = config.AuthenticationConfig(_file=StringIO(
 
4454
                'foo = bar\xff'))
 
4455
        self.assertRaises(errors.ConfigContentError, conf._get_config)
4172
4456
 
4173
4457
    def test_missing_auth_section_header(self):
4174
 
        conf = config.AuthenticationConfig(_file=BytesIO(b'foo = bar'))
 
4458
        conf = config.AuthenticationConfig(_file=StringIO('foo = bar'))
4175
4459
        self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
4176
4460
 
4177
4461
    def test_auth_section_header_not_closed(self):
4178
 
        conf = config.AuthenticationConfig(_file=BytesIO(b'[DEF'))
4179
 
        self.assertRaises(config.ParseConfigError, conf._get_config)
 
4462
        conf = config.AuthenticationConfig(_file=StringIO('[DEF'))
 
4463
        self.assertRaises(errors.ParseConfigError, conf._get_config)
4180
4464
 
4181
4465
    def test_auth_value_not_boolean(self):
4182
 
        conf = config.AuthenticationConfig(_file=BytesIO(b"""\
4183
 
[broken]
 
4466
        conf = config.AuthenticationConfig(_file=StringIO(
 
4467
                """[broken]
4184
4468
scheme=ftp
4185
4469
user=joe
4186
4470
verify_certificates=askme # Error: Not a boolean
4188
4472
        self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
4189
4473
 
4190
4474
    def test_auth_value_not_int(self):
4191
 
        conf = config.AuthenticationConfig(_file=BytesIO(b"""\
4192
 
[broken]
 
4475
        conf = config.AuthenticationConfig(_file=StringIO(
 
4476
                """[broken]
4193
4477
scheme=ftp
4194
4478
user=joe
4195
4479
port=port # Error: Not an int
4197
4481
        self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
4198
4482
 
4199
4483
    def test_unknown_password_encoding(self):
4200
 
        conf = config.AuthenticationConfig(_file=BytesIO(b"""\
4201
 
[broken]
 
4484
        conf = config.AuthenticationConfig(_file=StringIO(
 
4485
                """[broken]
4202
4486
scheme=ftp
4203
4487
user=joe
4204
4488
password_encoding=unknown
4207
4491
                          'ftp', 'foo.net', 'joe')
4208
4492
 
4209
4493
    def test_credentials_for_scheme_host(self):
4210
 
        conf = config.AuthenticationConfig(_file=BytesIO(b"""\
4211
 
# Identity on foo.net
 
4494
        conf = config.AuthenticationConfig(_file=StringIO(
 
4495
                """# Identity on foo.net
4212
4496
[ftp definition]
4213
4497
scheme=ftp
4214
4498
host=foo.net
4223
4507
        self._got_user_passwd(None, None, conf, 'ftp', 'bar.net')
4224
4508
 
4225
4509
    def test_credentials_for_host_port(self):
4226
 
        conf = config.AuthenticationConfig(_file=BytesIO(b"""\
4227
 
# Identity on foo.net
 
4510
        conf = config.AuthenticationConfig(_file=StringIO(
 
4511
                """# Identity on foo.net
4228
4512
[ftp definition]
4229
4513
scheme=ftp
4230
4514
port=10021
4239
4523
        self._got_user_passwd(None, None, conf, 'ftp', 'foo.net')
4240
4524
 
4241
4525
    def test_for_matching_host(self):
4242
 
        conf = config.AuthenticationConfig(_file=BytesIO(b"""\
4243
 
# Identity on foo.net
 
4526
        conf = config.AuthenticationConfig(_file=StringIO(
 
4527
                """# Identity on foo.net
4244
4528
[sourceforge]
4245
4529
scheme=bzr
4246
4530
host=bzr.sf.net
4260
4544
                              conf, 'bzr', 'bbzr.sf.net')
4261
4545
 
4262
4546
    def test_for_matching_host_None(self):
4263
 
        conf = config.AuthenticationConfig(_file=BytesIO(b"""\
4264
 
# Identity on foo.net
 
4547
        conf = config.AuthenticationConfig(_file=StringIO(
 
4548
                """# Identity on foo.net
4265
4549
[catchup bzr]
4266
4550
scheme=bzr
4267
4551
user=joe
4278
4562
                              conf, 'ftp', 'quux.net')
4279
4563
 
4280
4564
    def test_credentials_for_path(self):
4281
 
        conf = config.AuthenticationConfig(_file=BytesIO(b"""
 
4565
        conf = config.AuthenticationConfig(_file=StringIO(
 
4566
                """
4282
4567
[http dir1]
4283
4568
scheme=http
4284
4569
host=bar.org
4300
4585
                              conf, 'http', host='bar.org', path='/dir2')
4301
4586
        # matching subdir
4302
4587
        self._got_user_passwd('jim', 'jimpass',
4303
 
                              conf, 'http', host='bar.org', path='/dir1/subdir')
 
4588
                              conf, 'http', host='bar.org',path='/dir1/subdir')
4304
4589
 
4305
4590
    def test_credentials_for_user(self):
4306
 
        conf = config.AuthenticationConfig(_file=BytesIO(b"""
 
4591
        conf = config.AuthenticationConfig(_file=StringIO(
 
4592
                """
4307
4593
[with user]
4308
4594
scheme=http
4309
4595
host=bar.org
4321
4607
                              conf, 'http', 'bar.org', user='georges')
4322
4608
 
4323
4609
    def test_credentials_for_user_without_password(self):
4324
 
        conf = config.AuthenticationConfig(_file=BytesIO(b"""
 
4610
        conf = config.AuthenticationConfig(_file=StringIO(
 
4611
                """
4325
4612
[without password]
4326
4613
scheme=http
4327
4614
host=bar.org
4332
4619
                              conf, 'http', 'bar.org')
4333
4620
 
4334
4621
    def test_verify_certificates(self):
4335
 
        conf = config.AuthenticationConfig(_file=BytesIO(b"""
 
4622
        conf = config.AuthenticationConfig(_file=StringIO(
 
4623
                """
4336
4624
[self-signed]
4337
4625
scheme=https
4338
4626
host=bar.org
4356
4644
    def test_set_credentials(self):
4357
4645
        conf = config.AuthenticationConfig()
4358
4646
        conf.set_credentials('name', 'host', 'user', 'scheme', 'password',
4359
 
                             99, path='/foo', verify_certificates=False, realm='realm')
 
4647
        99, path='/foo', verify_certificates=False, realm='realm')
4360
4648
        credentials = conf.get_credentials(host='host', scheme='scheme',
4361
4649
                                           port=99, path='/foo',
4362
4650
                                           realm='realm')
4382
4670
        self.assertEqual(CREDENTIALS, credentials)
4383
4671
 
4384
4672
 
4385
 
class TestAuthenticationConfig(tests.TestCaseInTempDir):
 
4673
class TestAuthenticationConfig(tests.TestCase):
4386
4674
    """Test AuthenticationConfig behaviour"""
4387
4675
 
4388
4676
    def _check_default_password_prompt(self, expected_prompt_format, scheme,
4395
4683
            'scheme': scheme, 'host': host, 'port': port,
4396
4684
            'user': user, 'realm': realm}
4397
4685
 
4398
 
        ui.ui_factory = tests.TestUIFactory(stdin=password + '\n')
 
4686
        stdout = tests.StringIOWrapper()
 
4687
        stderr = tests.StringIOWrapper()
 
4688
        ui.ui_factory = tests.TestUIFactory(stdin=password + '\n',
 
4689
                                            stdout=stdout, stderr=stderr)
4399
4690
        # We use an empty conf so that the user is always prompted
4400
4691
        conf = config.AuthenticationConfig()
4401
4692
        self.assertEqual(password,
4402
 
                         conf.get_password(scheme, host, user, port=port,
4403
 
                                           realm=realm, path=path))
4404
 
        self.assertEqual(expected_prompt, ui.ui_factory.stderr.getvalue())
4405
 
        self.assertEqual('', ui.ui_factory.stdout.getvalue())
 
4693
                          conf.get_password(scheme, host, user, port=port,
 
4694
                                            realm=realm, path=path))
 
4695
        self.assertEqual(expected_prompt, stderr.getvalue())
 
4696
        self.assertEqual('', stdout.getvalue())
4406
4697
 
4407
4698
    def _check_default_username_prompt(self, expected_prompt_format, scheme,
4408
4699
                                       host=None, port=None, realm=None,
4413
4704
        expected_prompt = expected_prompt_format % {
4414
4705
            'scheme': scheme, 'host': host, 'port': port,
4415
4706
            'realm': realm}
4416
 
        ui.ui_factory = tests.TestUIFactory(stdin=username + '\n')
 
4707
        stdout = tests.StringIOWrapper()
 
4708
        stderr = tests.StringIOWrapper()
 
4709
        ui.ui_factory = tests.TestUIFactory(stdin=username+ '\n',
 
4710
                                            stdout=stdout, stderr=stderr)
4417
4711
        # We use an empty conf so that the user is always prompted
4418
4712
        conf = config.AuthenticationConfig()
4419
4713
        self.assertEqual(username, conf.get_user(scheme, host, port=port,
4420
 
                                                 realm=realm, path=path, ask=True))
4421
 
        self.assertEqual(expected_prompt, ui.ui_factory.stderr.getvalue())
4422
 
        self.assertEqual('', ui.ui_factory.stdout.getvalue())
 
4714
                          realm=realm, path=path, ask=True))
 
4715
        self.assertEqual(expected_prompt, stderr.getvalue())
 
4716
        self.assertEqual('', stdout.getvalue())
4423
4717
 
4424
4718
    def test_username_defaults_prompts(self):
4425
4719
        # HTTP prompts can't be tested here, see test_http.py
4432
4726
    def test_username_default_no_prompt(self):
4433
4727
        conf = config.AuthenticationConfig()
4434
4728
        self.assertEqual(None,
4435
 
                         conf.get_user('ftp', 'example.com'))
 
4729
            conf.get_user('ftp', 'example.com'))
4436
4730
        self.assertEqual("explicitdefault",
4437
 
                         conf.get_user('ftp', 'example.com', default="explicitdefault"))
 
4731
            conf.get_user('ftp', 'example.com', default="explicitdefault"))
4438
4732
 
4439
4733
    def test_password_default_prompts(self):
4440
4734
        # HTTP prompts can't be tested here, see test_http.py
4456
4750
            u'SMTP %(user)s@%(host)s:%(port)d password: ', 'smtp', port=10025)
4457
4751
 
4458
4752
    def test_ssh_password_emits_warning(self):
4459
 
        conf = config.AuthenticationConfig(_file=BytesIO(b"""
 
4753
        conf = config.AuthenticationConfig(_file=StringIO(
 
4754
                """
4460
4755
[ssh with password]
4461
4756
scheme=ssh
4462
4757
host=bar.org
4464
4759
password=jimpass
4465
4760
"""))
4466
4761
        entered_password = 'typed-by-hand'
4467
 
        ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n')
 
4762
        stdout = tests.StringIOWrapper()
 
4763
        stderr = tests.StringIOWrapper()
 
4764
        ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
 
4765
                                            stdout=stdout, stderr=stderr)
4468
4766
 
4469
4767
        # Since the password defined in the authentication config is ignored,
4470
4768
        # the user is prompted
4471
4769
        self.assertEqual(entered_password,
4472
 
                         conf.get_password('ssh', 'bar.org', user='jim'))
 
4770
                          conf.get_password('ssh', 'bar.org', user='jim'))
4473
4771
        self.assertContainsRe(
4474
4772
            self.get_log(),
4475
 
            'password ignored in section \\[ssh with password\\]')
 
4773
            'password ignored in section \[ssh with password\]')
4476
4774
 
4477
4775
    def test_ssh_without_password_doesnt_emit_warning(self):
4478
 
        conf = config.AuthenticationConfig(_file=BytesIO(b"""
 
4776
        conf = config.AuthenticationConfig(_file=StringIO(
 
4777
                """
4479
4778
[ssh with password]
4480
4779
scheme=ssh
4481
4780
host=bar.org
4482
4781
user=jim
4483
4782
"""))
4484
4783
        entered_password = 'typed-by-hand'
4485
 
        ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n')
 
4784
        stdout = tests.StringIOWrapper()
 
4785
        stderr = tests.StringIOWrapper()
 
4786
        ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
 
4787
                                            stdout=stdout,
 
4788
                                            stderr=stderr)
4486
4789
 
4487
4790
        # Since the password defined in the authentication config is ignored,
4488
4791
        # the user is prompted
4489
4792
        self.assertEqual(entered_password,
4490
 
                         conf.get_password('ssh', 'bar.org', user='jim'))
 
4793
                          conf.get_password('ssh', 'bar.org', user='jim'))
4491
4794
        # No warning shoud be emitted since there is no password. We are only
4492
4795
        # providing "user".
4493
4796
        self.assertNotContainsRe(
4494
4797
            self.get_log(),
4495
 
            'password ignored in section \\[ssh with password\\]')
 
4798
            'password ignored in section \[ssh with password\]')
4496
4799
 
4497
4800
    def test_uses_fallback_stores(self):
4498
4801
        self.overrideAttr(config, 'credential_store_registry',
4500
4803
        store = StubCredentialStore()
4501
4804
        store.add_credentials("http", "example.com", "joe", "secret")
4502
4805
        config.credential_store_registry.register("stub", store, fallback=True)
4503
 
        conf = config.AuthenticationConfig(_file=BytesIO())
 
4806
        conf = config.AuthenticationConfig(_file=StringIO())
4504
4807
        creds = conf.get_credentials("http", "example.com")
4505
4808
        self.assertEqual("joe", creds["user"])
4506
4809
        self.assertEqual("secret", creds["password"])
4517
4820
        self._password[(scheme, host)] = password
4518
4821
 
4519
4822
    def get_credentials(self, scheme, host, port=None, user=None,
4520
 
                        path=None, realm=None):
 
4823
        path=None, realm=None):
4521
4824
        key = (scheme, host)
4522
 
        if key not in self._username:
 
4825
        if not key in self._username:
4523
4826
            return None
4524
 
        return {"scheme": scheme, "host": host, "port": port,
 
4827
        return { "scheme": scheme, "host": host, "port": port,
4525
4828
                "user": self._username[key], "password": self._password[key]}
4526
4829
 
4527
4830
 
4531
4834
        self._calls = 0
4532
4835
 
4533
4836
    def get_credentials(self, scheme, host, port=None, user=None,
4534
 
                        path=None, realm=None):
 
4837
        path=None, realm=None):
4535
4838
        self._calls += 1
4536
4839
        return None
4537
4840
 
4555
4858
    def test_fallback_none_registered(self):
4556
4859
        r = config.CredentialStoreRegistry()
4557
4860
        self.assertEqual(None,
4558
 
                         r.get_fallback_credentials("http", "example.com"))
 
4861
                          r.get_fallback_credentials("http", "example.com"))
4559
4862
 
4560
4863
    def test_register(self):
4561
4864
        r = config.CredentialStoreRegistry()
4565
4868
 
4566
4869
    def test_register_lazy(self):
4567
4870
        r = config.CredentialStoreRegistry()
4568
 
        r.register_lazy("stub", "breezy.tests.test_config",
 
4871
        r.register_lazy("stub", "brzlib.tests.test_config",
4569
4872
                        "StubCredentialStore", fallback=False)
4570
4873
        self.assertEqual(["stub"], r.keys())
4571
4874
        self.assertIsInstance(r.get_credential_store("stub"),
4583
4886
        store = CountingCredentialStore()
4584
4887
        r.register("count", store, fallback=False)
4585
4888
        self.assertEqual(None,
4586
 
                         r.get_fallback_credentials("http", "example.com"))
 
4889
                          r.get_fallback_credentials("http", "example.com"))
4587
4890
        self.assertEqual(0, store._calls)
4588
4891
 
4589
4892
    def test_fallback_credentials(self):
4626
4929
        r = config.credential_store_registry
4627
4930
        plain_text = r.get_credential_store('base64')
4628
4931
        decoded = plain_text.decode_password(dict(password='c2VjcmV0'))
4629
 
        self.assertEqual(b'secret', decoded)
 
4932
        self.assertEqual('secret', decoded)
4630
4933
 
4631
4934
 
4632
4935
# FIXME: Once we have a way to declare authentication to all test servers, we
4641
4944
    pass
4642
4945
 
4643
4946
 
 
4947
class TestAutoUserId(tests.TestCase):
 
4948
    """Test inferring an automatic user name."""
 
4949
 
 
4950
    def test_auto_user_id(self):
 
4951
        """Automatic inference of user name.
 
4952
 
 
4953
        This is a bit hard to test in an isolated way, because it depends on
 
4954
        system functions that go direct to /etc or perhaps somewhere else.
 
4955
        But it's reasonable to say that on Unix, with an /etc/mailname, we ought
 
4956
        to be able to choose a user name with no configuration.
 
4957
        """
 
4958
        if sys.platform == 'win32':
 
4959
            raise tests.TestSkipped(
 
4960
                "User name inference not implemented on win32")
 
4961
        realname, address = config._auto_user_id()
 
4962
        if os.path.exists('/etc/mailname'):
 
4963
            self.assertIsNot(None, realname)
 
4964
            self.assertIsNot(None, address)
 
4965
        else:
 
4966
            self.assertEqual((None, None), (realname, address))
 
4967
 
 
4968
 
 
4969
class TestDefaultMailDomain(tests.TestCaseInTempDir):
 
4970
    """Test retrieving default domain from mailname file"""
 
4971
 
 
4972
    def test_default_mail_domain_simple(self):
 
4973
        f = file('simple', 'w')
 
4974
        try:
 
4975
            f.write("domainname.com\n")
 
4976
        finally:
 
4977
            f.close()
 
4978
        r = config._get_default_mail_domain('simple')
 
4979
        self.assertEqual('domainname.com', r)
 
4980
 
 
4981
    def test_default_mail_domain_no_eol(self):
 
4982
        f = file('no_eol', 'w')
 
4983
        try:
 
4984
            f.write("domainname.com")
 
4985
        finally:
 
4986
            f.close()
 
4987
        r = config._get_default_mail_domain('no_eol')
 
4988
        self.assertEqual('domainname.com', r)
 
4989
 
 
4990
    def test_default_mail_domain_multiple_lines(self):
 
4991
        f = file('multiple_lines', 'w')
 
4992
        try:
 
4993
            f.write("domainname.com\nsome other text\n")
 
4994
        finally:
 
4995
            f.close()
 
4996
        r = config._get_default_mail_domain('multiple_lines')
 
4997
        self.assertEqual('domainname.com', r)
 
4998
 
 
4999
 
4644
5000
class EmailOptionTests(tests.TestCase):
4645
5001
 
4646
 
    def test_default_email_uses_BRZ_EMAIL(self):
4647
 
        conf = config.MemoryStack(b'email=jelmer@debian.org')
4648
 
        # BRZ_EMAIL takes precedence over EMAIL
4649
 
        self.overrideEnv('BRZ_EMAIL', 'jelmer@samba.org')
 
5002
    def test_default_email_uses_BZR_EMAIL(self):
 
5003
        conf = config.MemoryStack('email=jelmer@debian.org')
 
5004
        # BZR_EMAIL takes precedence over EMAIL
 
5005
        self.overrideEnv('BZR_EMAIL', 'jelmer@samba.org')
4650
5006
        self.overrideEnv('EMAIL', 'jelmer@apache.org')
4651
5007
        self.assertEqual('jelmer@samba.org', conf.get('email'))
4652
5008
 
4653
5009
    def test_default_email_uses_EMAIL(self):
4654
 
        conf = config.MemoryStack(b'')
4655
 
        self.overrideEnv('BRZ_EMAIL', None)
 
5010
        conf = config.MemoryStack('')
 
5011
        self.overrideEnv('BZR_EMAIL', None)
4656
5012
        self.overrideEnv('EMAIL', 'jelmer@apache.org')
4657
5013
        self.assertEqual('jelmer@apache.org', conf.get('email'))
4658
5014
 
4659
 
    def test_BRZ_EMAIL_overrides(self):
4660
 
        conf = config.MemoryStack(b'email=jelmer@debian.org')
4661
 
        self.overrideEnv('BRZ_EMAIL', 'jelmer@apache.org')
 
5015
    def test_BZR_EMAIL_overrides(self):
 
5016
        conf = config.MemoryStack('email=jelmer@debian.org')
 
5017
        self.overrideEnv('BZR_EMAIL', 'jelmer@apache.org')
4662
5018
        self.assertEqual('jelmer@apache.org', conf.get('email'))
4663
 
        self.overrideEnv('BRZ_EMAIL', None)
 
5019
        self.overrideEnv('BZR_EMAIL', None)
4664
5020
        self.overrideEnv('EMAIL', 'jelmer@samba.org')
4665
5021
        self.assertEqual('jelmer@debian.org', conf.get('email'))
4666
5022
 
4668
5024
class MailClientOptionTests(tests.TestCase):
4669
5025
 
4670
5026
    def test_default(self):
4671
 
        conf = config.MemoryStack(b'')
 
5027
        conf = config.MemoryStack('')
4672
5028
        client = conf.get('mail_client')
4673
5029
        self.assertIs(client, mail_client.DefaultMail)
4674
5030
 
4675
5031
    def test_evolution(self):
4676
 
        conf = config.MemoryStack(b'mail_client=evolution')
 
5032
        conf = config.MemoryStack('mail_client=evolution')
4677
5033
        client = conf.get('mail_client')
4678
5034
        self.assertIs(client, mail_client.Evolution)
4679
5035
 
4680
5036
    def test_kmail(self):
4681
 
        conf = config.MemoryStack(b'mail_client=kmail')
 
5037
        conf = config.MemoryStack('mail_client=kmail')
4682
5038
        client = conf.get('mail_client')
4683
5039
        self.assertIs(client, mail_client.KMail)
4684
5040
 
4685
5041
    def test_mutt(self):
4686
 
        conf = config.MemoryStack(b'mail_client=mutt')
 
5042
        conf = config.MemoryStack('mail_client=mutt')
4687
5043
        client = conf.get('mail_client')
4688
5044
        self.assertIs(client, mail_client.Mutt)
4689
5045
 
4690
5046
    def test_thunderbird(self):
4691
 
        conf = config.MemoryStack(b'mail_client=thunderbird')
 
5047
        conf = config.MemoryStack('mail_client=thunderbird')
4692
5048
        client = conf.get('mail_client')
4693
5049
        self.assertIs(client, mail_client.Thunderbird)
4694
5050
 
4695
5051
    def test_explicit_default(self):
4696
 
        conf = config.MemoryStack(b'mail_client=default')
 
5052
        conf = config.MemoryStack('mail_client=default')
4697
5053
        client = conf.get('mail_client')
4698
5054
        self.assertIs(client, mail_client.DefaultMail)
4699
5055
 
4700
5056
    def test_editor(self):
4701
 
        conf = config.MemoryStack(b'mail_client=editor')
 
5057
        conf = config.MemoryStack('mail_client=editor')
4702
5058
        client = conf.get('mail_client')
4703
5059
        self.assertIs(client, mail_client.Editor)
4704
5060
 
4705
5061
    def test_mapi(self):
4706
 
        conf = config.MemoryStack(b'mail_client=mapi')
 
5062
        conf = config.MemoryStack('mail_client=mapi')
4707
5063
        client = conf.get('mail_client')
4708
5064
        self.assertIs(client, mail_client.MAPIClient)
4709
5065
 
4710
5066
    def test_xdg_email(self):
4711
 
        conf = config.MemoryStack(b'mail_client=xdg-email')
 
5067
        conf = config.MemoryStack('mail_client=xdg-email')
4712
5068
        client = conf.get('mail_client')
4713
5069
        self.assertIs(client, mail_client.XDGEmail)
4714
5070
 
4715
5071
    def test_unknown(self):
4716
 
        conf = config.MemoryStack(b'mail_client=firebird')
4717
 
        self.assertRaises(config.ConfigOptionValueError, conf.get,
4718
 
                          'mail_client')
 
5072
        conf = config.MemoryStack('mail_client=firebird')
 
5073
        self.assertRaises(errors.ConfigOptionValueError, conf.get,
 
5074
                'mail_client')