~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_config.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2010-05-28 00:25:32 UTC
  • mfrom: (5264.1.2 command-help-bug-177500)
  • Revision ID: pqm@pqm.ubuntu.com-20100528002532-9bzj1fajyxckd1rg
(lifeless) Stop raising at runtime when a command has no help,
 instead have a test in the test suite that checks all known command objects.
 (Robert Collins)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
 
1
# Copyright (C) 2005-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
19
19
from cStringIO import StringIO
20
20
import os
21
21
import sys
22
 
import threading
23
 
 
24
 
 
25
 
from testtools import matchers
26
22
 
27
23
#import bzrlib specific imports here
28
24
from bzrlib import (
33
29
    errors,
34
30
    osutils,
35
31
    mail_client,
36
 
    mergetools,
37
32
    ui,
38
33
    urlutils,
39
 
    registry,
40
 
    remote,
41
34
    tests,
42
35
    trace,
43
36
    transport,
44
37
    )
45
 
from bzrlib.symbol_versioning import (
46
 
    deprecated_in,
47
 
    deprecated_method,
48
 
    )
49
 
from bzrlib.transport import remote as transport_remote
50
 
from bzrlib.tests import (
51
 
    features,
52
 
    scenarios,
53
 
    test_server,
54
 
    )
55
38
from bzrlib.util.configobj import configobj
56
39
 
57
40
 
58
 
def lockable_config_scenarios():
59
 
    return [
60
 
        ('global',
61
 
         {'config_class': config.GlobalConfig,
62
 
          'config_args': [],
63
 
          'config_section': 'DEFAULT'}),
64
 
        ('locations',
65
 
         {'config_class': config.LocationConfig,
66
 
          'config_args': ['.'],
67
 
          'config_section': '.'}),]
68
 
 
69
 
 
70
 
load_tests = scenarios.load_tests_apply_scenarios
71
 
 
72
 
# Register helpers to build stores
73
 
config.test_store_builder_registry.register(
74
 
    'configobj', lambda test: config.IniFileStore(test.get_transport(),
75
 
                                                  'configobj.conf'))
76
 
config.test_store_builder_registry.register(
77
 
    'bazaar', lambda test: config.GlobalStore())
78
 
config.test_store_builder_registry.register(
79
 
    'location', lambda test: config.LocationStore())
80
 
 
81
 
 
82
 
def build_backing_branch(test, relpath,
83
 
                         transport_class=None, server_class=None):
84
 
    """Test helper to create a backing branch only once.
85
 
 
86
 
    Some tests needs multiple stores/stacks to check concurrent update
87
 
    behaviours. As such, they need to build different branch *objects* even if
88
 
    they share the branch on disk.
89
 
 
90
 
    :param relpath: The relative path to the branch. (Note that the helper
91
 
        should always specify the same relpath).
92
 
 
93
 
    :param transport_class: The Transport class the test needs to use.
94
 
 
95
 
    :param server_class: The server associated with the ``transport_class``
96
 
        above.
97
 
 
98
 
    Either both or neither of ``transport_class`` and ``server_class`` should
99
 
    be specified.
100
 
    """
101
 
    if transport_class is not None and server_class is not None:
102
 
        test.transport_class = transport_class
103
 
        test.transport_server = server_class
104
 
    elif not (transport_class is None and server_class is None):
105
 
        raise AssertionError('Specify both ``transport_class`` and '
106
 
                             '``server_class`` or neither of them')
107
 
    if getattr(test, 'backing_branch', None) is None:
108
 
        # First call, let's build the branch on disk
109
 
        test.backing_branch = test.make_branch(relpath)
110
 
 
111
 
 
112
 
def build_branch_store(test):
113
 
    build_backing_branch(test, 'branch')
114
 
    b = branch.Branch.open('branch')
115
 
    return config.BranchStore(b)
116
 
config.test_store_builder_registry.register('branch', build_branch_store)
117
 
 
118
 
 
119
 
def build_remote_branch_store(test):
120
 
    # There is only one permutation (but we won't be able to handle more with
121
 
    # this design anyway)
122
 
    (transport_class,
123
 
     server_class) = transport_remote.get_test_permutations()[0]
124
 
    build_backing_branch(test, 'branch', transport_class, server_class)
125
 
    b = branch.Branch.open(test.get_url('branch'))
126
 
    return config.BranchStore(b)
127
 
config.test_store_builder_registry.register('remote_branch',
128
 
                                            build_remote_branch_store)
129
 
 
130
 
 
131
 
config.test_stack_builder_registry.register(
132
 
    'bazaar', lambda test: config.GlobalStack())
133
 
config.test_stack_builder_registry.register(
134
 
    'location', lambda test: config.LocationStack('.'))
135
 
 
136
 
 
137
 
def build_branch_stack(test):
138
 
    build_backing_branch(test, 'branch')
139
 
    b = branch.Branch.open('branch')
140
 
    return config.BranchStack(b)
141
 
config.test_stack_builder_registry.register('branch', build_branch_stack)
142
 
 
143
 
 
144
 
def build_remote_branch_stack(test):
145
 
    # There is only one permutation (but we won't be able to handle more with
146
 
    # this design anyway)
147
 
    (transport_class,
148
 
     server_class) = transport_remote.get_test_permutations()[0]
149
 
    build_backing_branch(test, 'branch', transport_class, server_class)
150
 
    b = branch.Branch.open(test.get_url('branch'))
151
 
    return config.BranchStack(b)
152
 
config.test_stack_builder_registry.register('remote_branch',
153
 
                                            build_remote_branch_stack)
154
 
 
155
 
 
156
41
sample_long_alias="log -r-15..-1 --line"
157
42
sample_config_text = u"""
158
43
[DEFAULT]
161
46
change_editor=vimdiff -of @new_path @old_path
162
47
gpg_signing_command=gnome-gpg
163
48
log_format=short
164
 
validate_signatures_in_log=true
165
 
acceptable_keys=amy
166
49
user_global_option=something
167
 
bzr.mergetool.sometool=sometool {base} {this} {other} -o {result}
168
 
bzr.mergetool.funkytool=funkytool "arg with spaces" {this_temp}
169
 
bzr.default_mergetool=sometool
170
50
[ALIASES]
171
51
h=help
172
52
ll=""" + sample_long_alias + "\n"
225
105
"""
226
106
 
227
107
 
228
 
def create_configs(test):
229
 
    """Create configuration files for a given test.
230
 
 
231
 
    This requires creating a tree (and populate the ``test.tree`` attribute)
232
 
    and its associated branch and will populate the following attributes:
233
 
 
234
 
    - branch_config: A BranchConfig for the associated branch.
235
 
 
236
 
    - locations_config : A LocationConfig for the associated branch
237
 
 
238
 
    - bazaar_config: A GlobalConfig.
239
 
 
240
 
    The tree and branch are created in a 'tree' subdirectory so the tests can
241
 
    still use the test directory to stay outside of the branch.
242
 
    """
243
 
    tree = test.make_branch_and_tree('tree')
244
 
    test.tree = tree
245
 
    test.branch_config = config.BranchConfig(tree.branch)
246
 
    test.locations_config = config.LocationConfig(tree.basedir)
247
 
    test.bazaar_config = config.GlobalConfig()
248
 
 
249
 
 
250
 
def create_configs_with_file_option(test):
251
 
    """Create configuration files with a ``file`` option set in each.
252
 
 
253
 
    This builds on ``create_configs`` and add one ``file`` option in each
254
 
    configuration with a value which allows identifying the configuration file.
255
 
    """
256
 
    create_configs(test)
257
 
    test.bazaar_config.set_user_option('file', 'bazaar')
258
 
    test.locations_config.set_user_option('file', 'locations')
259
 
    test.branch_config.set_user_option('file', 'branch')
260
 
 
261
 
 
262
 
class TestOptionsMixin:
263
 
 
264
 
    def assertOptions(self, expected, conf):
265
 
        # We don't care about the parser (as it will make tests hard to write
266
 
        # and error-prone anyway)
267
 
        self.assertThat([opt[:4] for opt in conf._get_options()],
268
 
                        matchers.Equals(expected))
269
 
 
270
 
 
271
108
class InstrumentedConfigObj(object):
272
109
    """A config obj look-enough-alike to record calls made to it."""
273
110
 
292
129
        self._calls.append(('keys',))
293
130
        return []
294
131
 
295
 
    def reload(self):
296
 
        self._calls.append(('reload',))
297
 
 
298
132
    def write(self, arg):
299
133
        self._calls.append(('write',))
300
134
 
406
240
        """
407
241
        co = config.ConfigObj()
408
242
        co['test'] = 'foo#bar'
409
 
        outfile = StringIO()
410
 
        co.write(outfile=outfile)
411
 
        lines = outfile.getvalue().splitlines()
 
243
        lines = co.write()
412
244
        self.assertEqual(lines, ['test = "foo#bar"'])
413
245
        co2 = config.ConfigObj(lines)
414
246
        self.assertEqual(co2['test'], 'foo#bar')
415
247
 
416
 
    def test_triple_quotes(self):
417
 
        # Bug #710410: if the value string has triple quotes
418
 
        # then ConfigObj versions up to 4.7.2 will quote them wrong
419
 
        # and won't able to read them back
420
 
        triple_quotes_value = '''spam
421
 
""" that's my spam """
422
 
eggs'''
423
 
        co = config.ConfigObj()
424
 
        co['test'] = triple_quotes_value
425
 
        # While writing this test another bug in ConfigObj has been found:
426
 
        # method co.write() without arguments produces list of lines
427
 
        # one option per line, and multiline values are not split
428
 
        # across multiple lines,
429
 
        # and that breaks the parsing these lines back by ConfigObj.
430
 
        # This issue only affects test, but it's better to avoid
431
 
        # `co.write()` construct at all.
432
 
        # [bialix 20110222] bug report sent to ConfigObj's author
433
 
        outfile = StringIO()
434
 
        co.write(outfile=outfile)
435
 
        output = outfile.getvalue()
436
 
        # now we're trying to read it back
437
 
        co2 = config.ConfigObj(StringIO(output))
438
 
        self.assertEquals(triple_quotes_value, co2['test'])
439
 
 
440
248
 
441
249
erroneous_config = """[section] # line 1
442
250
good=good # line 2
463
271
        config.Config()
464
272
 
465
273
    def test_no_default_editor(self):
466
 
        self.assertRaises(
467
 
            NotImplementedError,
468
 
            self.applyDeprecated, deprecated_in((2, 4, 0)),
469
 
            config.Config().get_editor)
 
274
        self.assertRaises(NotImplementedError, config.Config().get_editor)
470
275
 
471
276
    def test_user_email(self):
472
277
        my_config = InstrumentedConfig()
515
320
        my_config = config.Config()
516
321
        self.assertEqual('long', my_config.log_format())
517
322
 
518
 
    def test_acceptable_keys_default(self):
519
 
        my_config = config.Config()
520
 
        self.assertEqual(None, my_config.acceptable_keys())
521
 
 
522
 
    def test_validate_signatures_in_log_default(self):
523
 
        my_config = config.Config()
524
 
        self.assertEqual(False, my_config.validate_signatures_in_log())
525
 
 
526
323
    def test_get_change_editor(self):
527
324
        my_config = InstrumentedConfig()
528
325
        change_editor = my_config.get_change_editor('old_tree', 'new_tree')
536
333
 
537
334
    def setUp(self):
538
335
        super(TestConfigPath, self).setUp()
539
 
        self.overrideEnv('HOME', '/home/bogus')
540
 
        self.overrideEnv('XDG_CACHE_DIR', '')
 
336
        os.environ['HOME'] = '/home/bogus'
 
337
        os.environ['XDG_CACHE_DIR'] = ''
541
338
        if sys.platform == 'win32':
542
 
            self.overrideEnv(
543
 
                'BZR_HOME', r'C:\Documents and Settings\bogus\Application Data')
 
339
            os.environ['BZR_HOME'] = \
 
340
                r'C:\Documents and Settings\bogus\Application Data'
544
341
            self.bzr_home = \
545
342
                'C:/Documents and Settings/bogus/Application Data/bazaar/2.0'
546
343
        else:
553
350
        self.assertEqual(config.config_filename(),
554
351
                         self.bzr_home + '/bazaar.conf')
555
352
 
 
353
    def test_branches_config_filename(self):
 
354
        self.assertEqual(config.branches_config_filename(),
 
355
                         self.bzr_home + '/branches.conf')
 
356
 
556
357
    def test_locations_config_filename(self):
557
358
        self.assertEqual(config.locations_config_filename(),
558
359
                         self.bzr_home + '/locations.conf')
566
367
            '/home/bogus/.cache')
567
368
 
568
369
 
569
 
class TestXDGConfigDir(tests.TestCaseInTempDir):
570
 
    # must be in temp dir because config tests for the existence of the bazaar
571
 
    # subdirectory of $XDG_CONFIG_HOME
572
 
 
573
 
    def setUp(self):
574
 
        if sys.platform in ('darwin', 'win32'):
575
 
            raise tests.TestNotApplicable(
576
 
                'XDG config dir not used on this platform')
577
 
        super(TestXDGConfigDir, self).setUp()
578
 
        self.overrideEnv('HOME', self.test_home_dir)
579
 
        # BZR_HOME overrides everything we want to test so unset it.
580
 
        self.overrideEnv('BZR_HOME', None)
581
 
 
582
 
    def test_xdg_config_dir_exists(self):
583
 
        """When ~/.config/bazaar exists, use it as the config dir."""
584
 
        newdir = osutils.pathjoin(self.test_home_dir, '.config', 'bazaar')
585
 
        os.makedirs(newdir)
586
 
        self.assertEqual(config.config_dir(), newdir)
587
 
 
588
 
    def test_xdg_config_home(self):
589
 
        """When XDG_CONFIG_HOME is set, use it."""
590
 
        xdgconfigdir = osutils.pathjoin(self.test_home_dir, 'xdgconfig')
591
 
        self.overrideEnv('XDG_CONFIG_HOME', xdgconfigdir)
592
 
        newdir = osutils.pathjoin(xdgconfigdir, 'bazaar')
593
 
        os.makedirs(newdir)
594
 
        self.assertEqual(config.config_dir(), newdir)
595
 
 
596
 
 
597
 
class TestIniConfig(tests.TestCaseInTempDir):
 
370
class TestIniConfig(tests.TestCase):
598
371
 
599
372
    def make_config_parser(self, s):
600
 
        conf = config.IniBasedConfig.from_string(s)
601
 
        return conf, conf._get_parser()
 
373
        conf = config.IniBasedConfig(None)
 
374
        parser = conf._get_parser(file=StringIO(s.encode('utf-8')))
 
375
        return conf, parser
602
376
 
603
377
 
604
378
class TestIniConfigBuilding(TestIniConfig):
605
379
 
606
380
    def test_contructs(self):
607
 
        my_config = config.IniBasedConfig()
 
381
        my_config = config.IniBasedConfig("nothing")
608
382
 
609
383
    def test_from_fp(self):
610
 
        my_config = config.IniBasedConfig.from_string(sample_config_text)
611
 
        self.assertIsInstance(my_config._get_parser(), configobj.ConfigObj)
 
384
        config_file = StringIO(sample_config_text.encode('utf-8'))
 
385
        my_config = config.IniBasedConfig(None)
 
386
        self.failUnless(
 
387
            isinstance(my_config._get_parser(file=config_file),
 
388
                        configobj.ConfigObj))
612
389
 
613
390
    def test_cached(self):
614
 
        my_config = config.IniBasedConfig.from_string(sample_config_text)
615
 
        parser = my_config._get_parser()
616
 
        self.assertTrue(my_config._get_parser() is parser)
617
 
 
618
 
    def _dummy_chown(self, path, uid, gid):
619
 
        self.path, self.uid, self.gid = path, uid, gid
620
 
 
621
 
    def test_ini_config_ownership(self):
622
 
        """Ensure that chown is happening during _write_config_file"""
623
 
        self.requireFeature(features.chown_feature)
624
 
        self.overrideAttr(os, 'chown', self._dummy_chown)
625
 
        self.path = self.uid = self.gid = None
626
 
        conf = config.IniBasedConfig(file_name='./foo.conf')
627
 
        conf._write_config_file()
628
 
        self.assertEquals(self.path, './foo.conf')
629
 
        self.assertTrue(isinstance(self.uid, int))
630
 
        self.assertTrue(isinstance(self.gid, int))
631
 
 
632
 
    def test_get_filename_parameter_is_deprecated_(self):
633
 
        conf = self.callDeprecated([
634
 
            'IniBasedConfig.__init__(get_filename) was deprecated in 2.3.'
635
 
            ' Use file_name instead.'],
636
 
            config.IniBasedConfig, lambda: 'ini.conf')
637
 
        self.assertEqual('ini.conf', conf.file_name)
638
 
 
639
 
    def test_get_parser_file_parameter_is_deprecated_(self):
640
391
        config_file = StringIO(sample_config_text.encode('utf-8'))
641
 
        conf = config.IniBasedConfig.from_string(sample_config_text)
642
 
        conf = self.callDeprecated([
643
 
            'IniBasedConfig._get_parser(file=xxx) was deprecated in 2.3.'
644
 
            ' Use IniBasedConfig(_content=xxx) instead.'],
645
 
            conf._get_parser, file=config_file)
646
 
 
647
 
 
648
 
class TestIniConfigSaving(tests.TestCaseInTempDir):
649
 
 
650
 
    def test_cant_save_without_a_file_name(self):
651
 
        conf = config.IniBasedConfig()
652
 
        self.assertRaises(AssertionError, conf._write_config_file)
653
 
 
654
 
    def test_saved_with_content(self):
655
 
        content = 'foo = bar\n'
656
 
        conf = config.IniBasedConfig.from_string(
657
 
            content, file_name='./test.conf', save=True)
658
 
        self.assertFileEqual(content, 'test.conf')
659
 
 
660
 
 
661
 
class TestIniConfigOptionExpansionDefaultValue(tests.TestCaseInTempDir):
662
 
    """What is the default value of expand for config options.
663
 
 
664
 
    This is an opt-in beta feature used to evaluate whether or not option
665
 
    references can appear in dangerous place raising exceptions, disapearing
666
 
    (and as such corrupting data) or if it's safe to activate the option by
667
 
    default.
668
 
 
669
 
    Note that these tests relies on config._expand_default_value being already
670
 
    overwritten in the parent class setUp.
671
 
    """
672
 
 
673
 
    def setUp(self):
674
 
        super(TestIniConfigOptionExpansionDefaultValue, self).setUp()
675
 
        self.config = None
676
 
        self.warnings = []
677
 
        def warning(*args):
678
 
            self.warnings.append(args[0] % args[1:])
679
 
        self.overrideAttr(trace, 'warning', warning)
680
 
 
681
 
    def get_config(self, expand):
682
 
        c = config.GlobalConfig.from_string('bzr.config.expand=%s' % (expand,),
683
 
                                            save=True)
684
 
        return c
685
 
 
686
 
    def assertExpandIs(self, expected):
687
 
        actual = config._get_expand_default_value()
688
 
        #self.config.get_user_option_as_bool('bzr.config.expand')
689
 
        self.assertEquals(expected, actual)
690
 
 
691
 
    def test_default_is_None(self):
692
 
        self.assertEquals(None, config._expand_default_value)
693
 
 
694
 
    def test_default_is_False_even_if_None(self):
695
 
        self.config = self.get_config(None)
696
 
        self.assertExpandIs(False)
697
 
 
698
 
    def test_default_is_False_even_if_invalid(self):
699
 
        self.config = self.get_config('<your choice>')
700
 
        self.assertExpandIs(False)
701
 
        # ...
702
 
        # Huh ? My choice is False ? Thanks, always happy to hear that :D
703
 
        # Wait, you've been warned !
704
 
        self.assertLength(1, self.warnings)
705
 
        self.assertEquals(
706
 
            'Value "<your choice>" is not a boolean for "bzr.config.expand"',
707
 
            self.warnings[0])
708
 
 
709
 
    def test_default_is_True(self):
710
 
        self.config = self.get_config(True)
711
 
        self.assertExpandIs(True)
712
 
 
713
 
    def test_default_is_False(self):
714
 
        self.config = self.get_config(False)
715
 
        self.assertExpandIs(False)
716
 
 
717
 
 
718
 
class TestIniConfigOptionExpansion(tests.TestCase):
719
 
    """Test option expansion from the IniConfig level.
720
 
 
721
 
    What we really want here is to test the Config level, but the class being
722
 
    abstract as far as storing values is concerned, this can't be done
723
 
    properly (yet).
724
 
    """
725
 
    # FIXME: This should be rewritten when all configs share a storage
726
 
    # implementation -- vila 2011-02-18
727
 
 
728
 
    def get_config(self, string=None):
729
 
        if string is None:
730
 
            string = ''
731
 
        c = config.IniBasedConfig.from_string(string)
732
 
        return c
733
 
 
734
 
    def assertExpansion(self, expected, conf, string, env=None):
735
 
        self.assertEquals(expected, conf.expand_options(string, env))
736
 
 
737
 
    def test_no_expansion(self):
738
 
        c = self.get_config('')
739
 
        self.assertExpansion('foo', c, 'foo')
740
 
 
741
 
    def test_env_adding_options(self):
742
 
        c = self.get_config('')
743
 
        self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
744
 
 
745
 
    def test_env_overriding_options(self):
746
 
        c = self.get_config('foo=baz')
747
 
        self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
748
 
 
749
 
    def test_simple_ref(self):
750
 
        c = self.get_config('foo=xxx')
751
 
        self.assertExpansion('xxx', c, '{foo}')
752
 
 
753
 
    def test_unknown_ref(self):
754
 
        c = self.get_config('')
755
 
        self.assertRaises(errors.ExpandingUnknownOption,
756
 
                          c.expand_options, '{foo}')
757
 
 
758
 
    def test_indirect_ref(self):
759
 
        c = self.get_config('''
760
 
foo=xxx
761
 
bar={foo}
762
 
''')
763
 
        self.assertExpansion('xxx', c, '{bar}')
764
 
 
765
 
    def test_embedded_ref(self):
766
 
        c = self.get_config('''
767
 
foo=xxx
768
 
bar=foo
769
 
''')
770
 
        self.assertExpansion('xxx', c, '{{bar}}')
771
 
 
772
 
    def test_simple_loop(self):
773
 
        c = self.get_config('foo={foo}')
774
 
        self.assertRaises(errors.OptionExpansionLoop, c.expand_options, '{foo}')
775
 
 
776
 
    def test_indirect_loop(self):
777
 
        c = self.get_config('''
778
 
foo={bar}
779
 
bar={baz}
780
 
baz={foo}''')
781
 
        e = self.assertRaises(errors.OptionExpansionLoop,
782
 
                              c.expand_options, '{foo}')
783
 
        self.assertEquals('foo->bar->baz', e.refs)
784
 
        self.assertEquals('{foo}', e.string)
785
 
 
786
 
    def test_list(self):
787
 
        conf = self.get_config('''
788
 
foo=start
789
 
bar=middle
790
 
baz=end
791
 
list={foo},{bar},{baz}
792
 
''')
793
 
        self.assertEquals(['start', 'middle', 'end'],
794
 
                           conf.get_user_option('list', expand=True))
795
 
 
796
 
    def test_cascading_list(self):
797
 
        conf = self.get_config('''
798
 
foo=start,{bar}
799
 
bar=middle,{baz}
800
 
baz=end
801
 
list={foo}
802
 
''')
803
 
        self.assertEquals(['start', 'middle', 'end'],
804
 
                           conf.get_user_option('list', expand=True))
805
 
 
806
 
    def test_pathological_hidden_list(self):
807
 
        conf = self.get_config('''
808
 
foo=bin
809
 
bar=go
810
 
start={foo
811
 
middle=},{
812
 
end=bar}
813
 
hidden={start}{middle}{end}
814
 
''')
815
 
        # Nope, it's either a string or a list, and the list wins as soon as a
816
 
        # ',' appears, so the string concatenation never occur.
817
 
        self.assertEquals(['{foo', '}', '{', 'bar}'],
818
 
                          conf.get_user_option('hidden', expand=True))
819
 
 
820
 
class TestLocationConfigOptionExpansion(tests.TestCaseInTempDir):
821
 
 
822
 
    def get_config(self, location, string=None):
823
 
        if string is None:
824
 
            string = ''
825
 
        # Since we don't save the config we won't strictly require to inherit
826
 
        # from TestCaseInTempDir, but an error occurs so quickly...
827
 
        c = config.LocationConfig.from_string(string, location)
828
 
        return c
829
 
 
830
 
    def test_dont_cross_unrelated_section(self):
831
 
        c = self.get_config('/another/branch/path','''
832
 
[/one/branch/path]
833
 
foo = hello
834
 
bar = {foo}/2
835
 
 
836
 
[/another/branch/path]
837
 
bar = {foo}/2
838
 
''')
839
 
        self.assertRaises(errors.ExpandingUnknownOption,
840
 
                          c.get_user_option, 'bar', expand=True)
841
 
 
842
 
    def test_cross_related_sections(self):
843
 
        c = self.get_config('/project/branch/path','''
844
 
[/project]
845
 
foo = qu
846
 
 
847
 
[/project/branch/path]
848
 
bar = {foo}ux
849
 
''')
850
 
        self.assertEquals('quux', c.get_user_option('bar', expand=True))
851
 
 
852
 
 
853
 
class TestIniBaseConfigOnDisk(tests.TestCaseInTempDir):
854
 
 
855
 
    def test_cannot_reload_without_name(self):
856
 
        conf = config.IniBasedConfig.from_string(sample_config_text)
857
 
        self.assertRaises(AssertionError, conf.reload)
858
 
 
859
 
    def test_reload_see_new_value(self):
860
 
        c1 = config.IniBasedConfig.from_string('editor=vim\n',
861
 
                                               file_name='./test/conf')
862
 
        c1._write_config_file()
863
 
        c2 = config.IniBasedConfig.from_string('editor=emacs\n',
864
 
                                               file_name='./test/conf')
865
 
        c2._write_config_file()
866
 
        self.assertEqual('vim', c1.get_user_option('editor'))
867
 
        self.assertEqual('emacs', c2.get_user_option('editor'))
868
 
        # Make sure we get the Right value
869
 
        c1.reload()
870
 
        self.assertEqual('emacs', c1.get_user_option('editor'))
871
 
 
872
 
 
873
 
class TestLockableConfig(tests.TestCaseInTempDir):
874
 
 
875
 
    scenarios = lockable_config_scenarios()
876
 
 
877
 
    # Set by load_tests
878
 
    config_class = None
879
 
    config_args = None
880
 
    config_section = None
881
 
 
882
 
    def setUp(self):
883
 
        super(TestLockableConfig, self).setUp()
884
 
        self._content = '[%s]\none=1\ntwo=2\n' % (self.config_section,)
885
 
        self.config = self.create_config(self._content)
886
 
 
887
 
    def get_existing_config(self):
888
 
        return self.config_class(*self.config_args)
889
 
 
890
 
    def create_config(self, content):
891
 
        kwargs = dict(save=True)
892
 
        c = self.config_class.from_string(content, *self.config_args, **kwargs)
893
 
        return c
894
 
 
895
 
    def test_simple_read_access(self):
896
 
        self.assertEquals('1', self.config.get_user_option('one'))
897
 
 
898
 
    def test_simple_write_access(self):
899
 
        self.config.set_user_option('one', 'one')
900
 
        self.assertEquals('one', self.config.get_user_option('one'))
901
 
 
902
 
    def test_listen_to_the_last_speaker(self):
903
 
        c1 = self.config
904
 
        c2 = self.get_existing_config()
905
 
        c1.set_user_option('one', 'ONE')
906
 
        c2.set_user_option('two', 'TWO')
907
 
        self.assertEquals('ONE', c1.get_user_option('one'))
908
 
        self.assertEquals('TWO', c2.get_user_option('two'))
909
 
        # The second update respect the first one
910
 
        self.assertEquals('ONE', c2.get_user_option('one'))
911
 
 
912
 
    def test_last_speaker_wins(self):
913
 
        # If the same config is not shared, the same variable modified twice
914
 
        # can only see a single result.
915
 
        c1 = self.config
916
 
        c2 = self.get_existing_config()
917
 
        c1.set_user_option('one', 'c1')
918
 
        c2.set_user_option('one', 'c2')
919
 
        self.assertEquals('c2', c2._get_user_option('one'))
920
 
        # The first modification is still available until another refresh
921
 
        # occur
922
 
        self.assertEquals('c1', c1._get_user_option('one'))
923
 
        c1.set_user_option('two', 'done')
924
 
        self.assertEquals('c2', c1._get_user_option('one'))
925
 
 
926
 
    def test_writes_are_serialized(self):
927
 
        c1 = self.config
928
 
        c2 = self.get_existing_config()
929
 
 
930
 
        # We spawn a thread that will pause *during* the write
931
 
        before_writing = threading.Event()
932
 
        after_writing = threading.Event()
933
 
        writing_done = threading.Event()
934
 
        c1_orig = c1._write_config_file
935
 
        def c1_write_config_file():
936
 
            before_writing.set()
937
 
            c1_orig()
938
 
            # The lock is held. We wait for the main thread to decide when to
939
 
            # continue
940
 
            after_writing.wait()
941
 
        c1._write_config_file = c1_write_config_file
942
 
        def c1_set_option():
943
 
            c1.set_user_option('one', 'c1')
944
 
            writing_done.set()
945
 
        t1 = threading.Thread(target=c1_set_option)
946
 
        # Collect the thread after the test
947
 
        self.addCleanup(t1.join)
948
 
        # Be ready to unblock the thread if the test goes wrong
949
 
        self.addCleanup(after_writing.set)
950
 
        t1.start()
951
 
        before_writing.wait()
952
 
        self.assertTrue(c1._lock.is_held)
953
 
        self.assertRaises(errors.LockContention,
954
 
                          c2.set_user_option, 'one', 'c2')
955
 
        self.assertEquals('c1', c1.get_user_option('one'))
956
 
        # Let the lock be released
957
 
        after_writing.set()
958
 
        writing_done.wait()
959
 
        c2.set_user_option('one', 'c2')
960
 
        self.assertEquals('c2', c2.get_user_option('one'))
961
 
 
962
 
    def test_read_while_writing(self):
963
 
       c1 = self.config
964
 
       # We spawn a thread that will pause *during* the write
965
 
       ready_to_write = threading.Event()
966
 
       do_writing = threading.Event()
967
 
       writing_done = threading.Event()
968
 
       c1_orig = c1._write_config_file
969
 
       def c1_write_config_file():
970
 
           ready_to_write.set()
971
 
           # The lock is held. We wait for the main thread to decide when to
972
 
           # continue
973
 
           do_writing.wait()
974
 
           c1_orig()
975
 
           writing_done.set()
976
 
       c1._write_config_file = c1_write_config_file
977
 
       def c1_set_option():
978
 
           c1.set_user_option('one', 'c1')
979
 
       t1 = threading.Thread(target=c1_set_option)
980
 
       # Collect the thread after the test
981
 
       self.addCleanup(t1.join)
982
 
       # Be ready to unblock the thread if the test goes wrong
983
 
       self.addCleanup(do_writing.set)
984
 
       t1.start()
985
 
       # Ensure the thread is ready to write
986
 
       ready_to_write.wait()
987
 
       self.assertTrue(c1._lock.is_held)
988
 
       self.assertEquals('c1', c1.get_user_option('one'))
989
 
       # If we read during the write, we get the old value
990
 
       c2 = self.get_existing_config()
991
 
       self.assertEquals('1', c2.get_user_option('one'))
992
 
       # Let the writing occur and ensure it occurred
993
 
       do_writing.set()
994
 
       writing_done.wait()
995
 
       # Now we get the updated value
996
 
       c3 = self.get_existing_config()
997
 
       self.assertEquals('c1', c3.get_user_option('one'))
 
392
        my_config = config.IniBasedConfig(None)
 
393
        parser = my_config._get_parser(file=config_file)
 
394
        self.failUnless(my_config._get_parser() is parser)
998
395
 
999
396
 
1000
397
class TestGetUserOptionAs(TestIniConfig):
1065
462
            parser = my_config._get_parser()
1066
463
        finally:
1067
464
            config.ConfigObj = oldparserclass
1068
 
        self.assertIsInstance(parser, InstrumentedConfigObj)
 
465
        self.failUnless(isinstance(parser, InstrumentedConfigObj))
1069
466
        self.assertEqual(parser._calls, [('__init__', config.config_filename(),
1070
467
                                          'utf-8')])
1071
468
 
1082
479
        my_config = config.BranchConfig(branch)
1083
480
        location_config = my_config._get_location_config()
1084
481
        self.assertEqual(branch.base, location_config.location)
1085
 
        self.assertIs(location_config, my_config._get_location_config())
 
482
        self.failUnless(location_config is my_config._get_location_config())
1086
483
 
1087
484
    def test_get_config(self):
1088
485
        """The Branch.get_config method works properly"""
1108
505
        branch = self.make_branch('branch')
1109
506
        self.assertEqual('branch', branch.nick)
1110
507
 
 
508
        locations = config.locations_config_filename()
 
509
        config.ensure_config_dir_exists()
1111
510
        local_url = urlutils.local_path_to_url('branch')
1112
 
        conf = config.LocationConfig.from_string(
1113
 
            '[%s]\nnickname = foobar' % (local_url,),
1114
 
            local_url, save=True)
 
511
        open(locations, 'wb').write('[%s]\nnickname = foobar'
 
512
                                    % (local_url,))
1115
513
        self.assertEqual('foobar', branch.nick)
1116
514
 
1117
515
    def test_config_local_path(self):
1119
517
        branch = self.make_branch('branch')
1120
518
        self.assertEqual('branch', branch.nick)
1121
519
 
1122
 
        local_path = osutils.getcwd().encode('utf8')
1123
 
        conf = config.LocationConfig.from_string(
1124
 
            '[%s/branch]\nnickname = barry' % (local_path,),
1125
 
            'branch',  save=True)
 
520
        locations = config.locations_config_filename()
 
521
        config.ensure_config_dir_exists()
 
522
        open(locations, 'wb').write('[%s/branch]\nnickname = barry'
 
523
                                    % (osutils.getcwd().encode('utf8'),))
1126
524
        self.assertEqual('barry', branch.nick)
1127
525
 
1128
526
    def test_config_creates_local(self):
1129
527
        """Creating a new entry in config uses a local path."""
1130
528
        branch = self.make_branch('branch', format='knit')
1131
529
        branch.set_push_location('http://foobar')
 
530
        locations = config.locations_config_filename()
1132
531
        local_path = osutils.getcwd().encode('utf8')
1133
532
        # Surprisingly ConfigObj doesn't create a trailing newline
1134
 
        self.check_file_contents(config.locations_config_filename(),
 
533
        self.check_file_contents(locations,
1135
534
                                 '[%s/branch]\n'
1136
535
                                 'push_location = http://foobar\n'
1137
536
                                 'push_location:policy = norecurse\n'
1142
541
        self.assertEqual('!repo', b.get_config().get_nickname())
1143
542
 
1144
543
    def test_warn_if_masked(self):
 
544
        _warning = trace.warning
1145
545
        warnings = []
1146
546
        def warning(*args):
1147
547
            warnings.append(args[0] % args[1:])
1148
 
        self.overrideAttr(trace, 'warning', warning)
1149
548
 
1150
549
        def set_option(store, warn_masked=True):
1151
550
            warnings[:] = []
1157
556
            else:
1158
557
                self.assertEqual(1, len(warnings))
1159
558
                self.assertEqual(warning, warnings[0])
1160
 
        branch = self.make_branch('.')
1161
 
        conf = branch.get_config()
1162
 
        set_option(config.STORE_GLOBAL)
1163
 
        assertWarning(None)
1164
 
        set_option(config.STORE_BRANCH)
1165
 
        assertWarning(None)
1166
 
        set_option(config.STORE_GLOBAL)
1167
 
        assertWarning('Value "4" is masked by "3" from branch.conf')
1168
 
        set_option(config.STORE_GLOBAL, warn_masked=False)
1169
 
        assertWarning(None)
1170
 
        set_option(config.STORE_LOCATION)
1171
 
        assertWarning(None)
1172
 
        set_option(config.STORE_BRANCH)
1173
 
        assertWarning('Value "3" is masked by "0" from locations.conf')
1174
 
        set_option(config.STORE_BRANCH, warn_masked=False)
1175
 
        assertWarning(None)
1176
 
 
1177
 
 
1178
 
class TestGlobalConfigItems(tests.TestCaseInTempDir):
 
559
        trace.warning = warning
 
560
        try:
 
561
            branch = self.make_branch('.')
 
562
            conf = branch.get_config()
 
563
            set_option(config.STORE_GLOBAL)
 
564
            assertWarning(None)
 
565
            set_option(config.STORE_BRANCH)
 
566
            assertWarning(None)
 
567
            set_option(config.STORE_GLOBAL)
 
568
            assertWarning('Value "4" is masked by "3" from branch.conf')
 
569
            set_option(config.STORE_GLOBAL, warn_masked=False)
 
570
            assertWarning(None)
 
571
            set_option(config.STORE_LOCATION)
 
572
            assertWarning(None)
 
573
            set_option(config.STORE_BRANCH)
 
574
            assertWarning('Value "3" is masked by "0" from locations.conf')
 
575
            set_option(config.STORE_BRANCH, warn_masked=False)
 
576
            assertWarning(None)
 
577
        finally:
 
578
            trace.warning = _warning
 
579
 
 
580
 
 
581
class TestGlobalConfigItems(tests.TestCase):
1179
582
 
1180
583
    def test_user_id(self):
1181
 
        my_config = config.GlobalConfig.from_string(sample_config_text)
 
584
        config_file = StringIO(sample_config_text.encode('utf-8'))
 
585
        my_config = config.GlobalConfig()
 
586
        my_config._parser = my_config._get_parser(file=config_file)
1182
587
        self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
1183
588
                         my_config._get_user_id())
1184
589
 
1185
590
    def test_absent_user_id(self):
 
591
        config_file = StringIO("")
1186
592
        my_config = config.GlobalConfig()
 
593
        my_config._parser = my_config._get_parser(file=config_file)
1187
594
        self.assertEqual(None, my_config._get_user_id())
1188
595
 
1189
596
    def test_configured_editor(self):
1190
 
        my_config = config.GlobalConfig.from_string(sample_config_text)
1191
 
        editor = self.applyDeprecated(
1192
 
            deprecated_in((2, 4, 0)), my_config.get_editor)
1193
 
        self.assertEqual('vim', editor)
 
597
        config_file = StringIO(sample_config_text.encode('utf-8'))
 
598
        my_config = config.GlobalConfig()
 
599
        my_config._parser = my_config._get_parser(file=config_file)
 
600
        self.assertEqual("vim", my_config.get_editor())
1194
601
 
1195
602
    def test_signatures_always(self):
1196
 
        my_config = config.GlobalConfig.from_string(sample_always_signatures)
 
603
        config_file = StringIO(sample_always_signatures)
 
604
        my_config = config.GlobalConfig()
 
605
        my_config._parser = my_config._get_parser(file=config_file)
1197
606
        self.assertEqual(config.CHECK_NEVER,
1198
607
                         my_config.signature_checking())
1199
608
        self.assertEqual(config.SIGN_ALWAYS,
1201
610
        self.assertEqual(True, my_config.signature_needed())
1202
611
 
1203
612
    def test_signatures_if_possible(self):
1204
 
        my_config = config.GlobalConfig.from_string(sample_maybe_signatures)
 
613
        config_file = StringIO(sample_maybe_signatures)
 
614
        my_config = config.GlobalConfig()
 
615
        my_config._parser = my_config._get_parser(file=config_file)
1205
616
        self.assertEqual(config.CHECK_NEVER,
1206
617
                         my_config.signature_checking())
1207
618
        self.assertEqual(config.SIGN_WHEN_REQUIRED,
1209
620
        self.assertEqual(False, my_config.signature_needed())
1210
621
 
1211
622
    def test_signatures_ignore(self):
1212
 
        my_config = config.GlobalConfig.from_string(sample_ignore_signatures)
 
623
        config_file = StringIO(sample_ignore_signatures)
 
624
        my_config = config.GlobalConfig()
 
625
        my_config._parser = my_config._get_parser(file=config_file)
1213
626
        self.assertEqual(config.CHECK_ALWAYS,
1214
627
                         my_config.signature_checking())
1215
628
        self.assertEqual(config.SIGN_NEVER,
1217
630
        self.assertEqual(False, my_config.signature_needed())
1218
631
 
1219
632
    def _get_sample_config(self):
1220
 
        my_config = config.GlobalConfig.from_string(sample_config_text)
 
633
        config_file = StringIO(sample_config_text.encode('utf-8'))
 
634
        my_config = config.GlobalConfig()
 
635
        my_config._parser = my_config._get_parser(file=config_file)
1221
636
        return my_config
1222
637
 
1223
638
    def test_gpg_signing_command(self):
1226
641
        self.assertEqual(False, my_config.signature_needed())
1227
642
 
1228
643
    def _get_empty_config(self):
 
644
        config_file = StringIO("")
1229
645
        my_config = config.GlobalConfig()
 
646
        my_config._parser = my_config._get_parser(file=config_file)
1230
647
        return my_config
1231
648
 
1232
649
    def test_gpg_signing_command_unset(self):
1250
667
        my_config = self._get_sample_config()
1251
668
        self.assertEqual("short", my_config.log_format())
1252
669
 
1253
 
    def test_configured_acceptable_keys(self):
1254
 
        my_config = self._get_sample_config()
1255
 
        self.assertEqual("amy", my_config.acceptable_keys())
1256
 
 
1257
 
    def test_configured_validate_signatures_in_log(self):
1258
 
        my_config = self._get_sample_config()
1259
 
        self.assertEqual(True, my_config.validate_signatures_in_log())
1260
 
 
1261
670
    def test_get_alias(self):
1262
671
        my_config = self._get_sample_config()
1263
672
        self.assertEqual('help', my_config.get_alias('h'))
1290
699
        change_editor = my_config.get_change_editor('old', 'new')
1291
700
        self.assertIs(None, change_editor)
1292
701
 
1293
 
    def test_get_merge_tools(self):
1294
 
        conf = self._get_sample_config()
1295
 
        tools = conf.get_merge_tools()
1296
 
        self.log(repr(tools))
1297
 
        self.assertEqual(
1298
 
            {u'funkytool' : u'funkytool "arg with spaces" {this_temp}',
1299
 
            u'sometool' : u'sometool {base} {this} {other} -o {result}'},
1300
 
            tools)
1301
 
 
1302
 
    def test_get_merge_tools_empty(self):
1303
 
        conf = self._get_empty_config()
1304
 
        tools = conf.get_merge_tools()
1305
 
        self.assertEqual({}, tools)
1306
 
 
1307
 
    def test_find_merge_tool(self):
1308
 
        conf = self._get_sample_config()
1309
 
        cmdline = conf.find_merge_tool('sometool')
1310
 
        self.assertEqual('sometool {base} {this} {other} -o {result}', cmdline)
1311
 
 
1312
 
    def test_find_merge_tool_not_found(self):
1313
 
        conf = self._get_sample_config()
1314
 
        cmdline = conf.find_merge_tool('DOES NOT EXIST')
1315
 
        self.assertIs(cmdline, None)
1316
 
 
1317
 
    def test_find_merge_tool_known(self):
1318
 
        conf = self._get_empty_config()
1319
 
        cmdline = conf.find_merge_tool('kdiff3')
1320
 
        self.assertEquals('kdiff3 {base} {this} {other} -o {result}', cmdline)
1321
 
 
1322
 
    def test_find_merge_tool_override_known(self):
1323
 
        conf = self._get_empty_config()
1324
 
        conf.set_user_option('bzr.mergetool.kdiff3', 'kdiff3 blah')
1325
 
        cmdline = conf.find_merge_tool('kdiff3')
1326
 
        self.assertEqual('kdiff3 blah', cmdline)
1327
 
 
1328
702
 
1329
703
class TestGlobalConfigSavingOptions(tests.TestCaseInTempDir):
1330
704
 
1348
722
        self.assertIs(None, new_config.get_alias('commit'))
1349
723
 
1350
724
 
1351
 
class TestLocationConfig(tests.TestCaseInTempDir, TestOptionsMixin):
 
725
class TestLocationConfig(tests.TestCaseInTempDir):
1352
726
 
1353
727
    def test_constructs(self):
1354
728
        my_config = config.LocationConfig('http://example.com')
1366
740
            parser = my_config._get_parser()
1367
741
        finally:
1368
742
            config.ConfigObj = oldparserclass
1369
 
        self.assertIsInstance(parser, InstrumentedConfigObj)
 
743
        self.failUnless(isinstance(parser, InstrumentedConfigObj))
1370
744
        self.assertEqual(parser._calls,
1371
745
                         [('__init__', config.locations_config_filename(),
1372
746
                           'utf-8')])
 
747
        config.ensure_config_dir_exists()
 
748
        #os.mkdir(config.config_dir())
 
749
        f = file(config.branches_config_filename(), 'wb')
 
750
        f.write('')
 
751
        f.close()
 
752
        oldparserclass = config.ConfigObj
 
753
        config.ConfigObj = InstrumentedConfigObj
 
754
        try:
 
755
            my_config = config.LocationConfig('http://www.example.com')
 
756
            parser = my_config._get_parser()
 
757
        finally:
 
758
            config.ConfigObj = oldparserclass
1373
759
 
1374
760
    def test_get_global_config(self):
1375
761
        my_config = config.BranchConfig(FakeBranch('http://example.com'))
1376
762
        global_config = my_config._get_global_config()
1377
 
        self.assertIsInstance(global_config, config.GlobalConfig)
1378
 
        self.assertIs(global_config, my_config._get_global_config())
1379
 
 
1380
 
    def assertLocationMatching(self, expected):
1381
 
        self.assertEqual(expected,
1382
 
                         list(self.my_location_config._get_matching_sections()))
 
763
        self.failUnless(isinstance(global_config, config.GlobalConfig))
 
764
        self.failUnless(global_config is my_config._get_global_config())
1383
765
 
1384
766
    def test__get_matching_sections_no_match(self):
1385
767
        self.get_branch_config('/')
1386
 
        self.assertLocationMatching([])
 
768
        self.assertEqual([], self.my_location_config._get_matching_sections())
1387
769
 
1388
770
    def test__get_matching_sections_exact(self):
1389
771
        self.get_branch_config('http://www.example.com')
1390
 
        self.assertLocationMatching([('http://www.example.com', '')])
 
772
        self.assertEqual([('http://www.example.com', '')],
 
773
                         self.my_location_config._get_matching_sections())
1391
774
 
1392
775
    def test__get_matching_sections_suffix_does_not(self):
1393
776
        self.get_branch_config('http://www.example.com-com')
1394
 
        self.assertLocationMatching([])
 
777
        self.assertEqual([], self.my_location_config._get_matching_sections())
1395
778
 
1396
779
    def test__get_matching_sections_subdir_recursive(self):
1397
780
        self.get_branch_config('http://www.example.com/com')
1398
 
        self.assertLocationMatching([('http://www.example.com', 'com')])
 
781
        self.assertEqual([('http://www.example.com', 'com')],
 
782
                         self.my_location_config._get_matching_sections())
1399
783
 
1400
784
    def test__get_matching_sections_ignoreparent(self):
1401
785
        self.get_branch_config('http://www.example.com/ignoreparent')
1402
 
        self.assertLocationMatching([('http://www.example.com/ignoreparent',
1403
 
                                      '')])
 
786
        self.assertEqual([('http://www.example.com/ignoreparent', '')],
 
787
                         self.my_location_config._get_matching_sections())
1404
788
 
1405
789
    def test__get_matching_sections_ignoreparent_subdir(self):
1406
790
        self.get_branch_config(
1407
791
            'http://www.example.com/ignoreparent/childbranch')
1408
 
        self.assertLocationMatching([('http://www.example.com/ignoreparent',
1409
 
                                      'childbranch')])
 
792
        self.assertEqual([('http://www.example.com/ignoreparent',
 
793
                           'childbranch')],
 
794
                         self.my_location_config._get_matching_sections())
1410
795
 
1411
796
    def test__get_matching_sections_subdir_trailing_slash(self):
1412
797
        self.get_branch_config('/b')
1413
 
        self.assertLocationMatching([('/b/', '')])
 
798
        self.assertEqual([('/b/', '')],
 
799
                         self.my_location_config._get_matching_sections())
1414
800
 
1415
801
    def test__get_matching_sections_subdir_child(self):
1416
802
        self.get_branch_config('/a/foo')
1417
 
        self.assertLocationMatching([('/a/*', ''), ('/a/', 'foo')])
 
803
        self.assertEqual([('/a/*', ''), ('/a/', 'foo')],
 
804
                         self.my_location_config._get_matching_sections())
1418
805
 
1419
806
    def test__get_matching_sections_subdir_child_child(self):
1420
807
        self.get_branch_config('/a/foo/bar')
1421
 
        self.assertLocationMatching([('/a/*', 'bar'), ('/a/', 'foo/bar')])
 
808
        self.assertEqual([('/a/*', 'bar'), ('/a/', 'foo/bar')],
 
809
                         self.my_location_config._get_matching_sections())
1422
810
 
1423
811
    def test__get_matching_sections_trailing_slash_with_children(self):
1424
812
        self.get_branch_config('/a/')
1425
 
        self.assertLocationMatching([('/a/', '')])
 
813
        self.assertEqual([('/a/', '')],
 
814
                         self.my_location_config._get_matching_sections())
1426
815
 
1427
816
    def test__get_matching_sections_explicit_over_glob(self):
1428
817
        # XXX: 2006-09-08 jamesh
1430
819
        # was a config section for '/a/?', it would get precedence
1431
820
        # over '/a/c'.
1432
821
        self.get_branch_config('/a/c')
1433
 
        self.assertLocationMatching([('/a/c', ''), ('/a/*', ''), ('/a/', 'c')])
 
822
        self.assertEqual([('/a/c', ''), ('/a/*', ''), ('/a/', 'c')],
 
823
                         self.my_location_config._get_matching_sections())
1434
824
 
1435
825
    def test__get_option_policy_normal(self):
1436
826
        self.get_branch_config('http://www.example.com')
1458
848
            'http://www.example.com', 'appendpath_option'),
1459
849
            config.POLICY_APPENDPATH)
1460
850
 
1461
 
    def test__get_options_with_policy(self):
1462
 
        self.get_branch_config('/dir/subdir',
1463
 
                               location_config="""\
1464
 
[/dir]
1465
 
other_url = /other-dir
1466
 
other_url:policy = appendpath
1467
 
[/dir/subdir]
1468
 
other_url = /other-subdir
1469
 
""")
1470
 
        self.assertOptions(
1471
 
            [(u'other_url', u'/other-subdir', u'/dir/subdir', 'locations'),
1472
 
             (u'other_url', u'/other-dir', u'/dir', 'locations'),
1473
 
             (u'other_url:policy', u'appendpath', u'/dir', 'locations')],
1474
 
            self.my_location_config)
1475
 
 
1476
851
    def test_location_without_username(self):
1477
852
        self.get_branch_config('http://www.example.com/ignoreparent')
1478
853
        self.assertEqual(u'Erik B\u00e5gfors <erik@bagfors.nu>',
1614
989
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1615
990
                         self.my_config.post_commit())
1616
991
 
1617
 
    def get_branch_config(self, location, global_config=None,
1618
 
                          location_config=None):
1619
 
        my_branch = FakeBranch(location)
 
992
    def get_branch_config(self, location, global_config=None):
1620
993
        if global_config is None:
1621
 
            global_config = sample_config_text
1622
 
        if location_config is None:
1623
 
            location_config = sample_branches_text
1624
 
 
1625
 
        my_global_config = config.GlobalConfig.from_string(global_config,
1626
 
                                                           save=True)
1627
 
        my_location_config = config.LocationConfig.from_string(
1628
 
            location_config, my_branch.base, save=True)
1629
 
        my_config = config.BranchConfig(my_branch)
1630
 
        self.my_config = my_config
1631
 
        self.my_location_config = my_config._get_location_config()
 
994
            global_file = StringIO(sample_config_text.encode('utf-8'))
 
995
        else:
 
996
            global_file = StringIO(global_config.encode('utf-8'))
 
997
        branches_file = StringIO(sample_branches_text.encode('utf-8'))
 
998
        self.my_config = config.BranchConfig(FakeBranch(location))
 
999
        # Force location config to use specified file
 
1000
        self.my_location_config = self.my_config._get_location_config()
 
1001
        self.my_location_config._get_parser(branches_file)
 
1002
        # Force global config to use specified file
 
1003
        self.my_config._get_global_config()._get_parser(global_file)
1632
1004
 
1633
1005
    def test_set_user_setting_sets_and_saves(self):
1634
1006
        self.get_branch_config('/a/c')
1635
1007
        record = InstrumentedConfigObj("foo")
1636
1008
        self.my_location_config._parser = record
1637
1009
 
1638
 
        self.callDeprecated(['The recurse option is deprecated as of '
1639
 
                             '0.14.  The section "/a/c" has been '
1640
 
                             'converted to use policies.'],
1641
 
                            self.my_config.set_user_option,
1642
 
                            'foo', 'bar', store=config.STORE_LOCATION)
1643
 
        self.assertEqual([('reload',),
1644
 
                          ('__contains__', '/a/c'),
 
1010
        real_mkdir = os.mkdir
 
1011
        self.created = False
 
1012
        def checked_mkdir(path, mode=0777):
 
1013
            self.log('making directory: %s', path)
 
1014
            real_mkdir(path, mode)
 
1015
            self.created = True
 
1016
 
 
1017
        os.mkdir = checked_mkdir
 
1018
        try:
 
1019
            self.callDeprecated(['The recurse option is deprecated as of '
 
1020
                                 '0.14.  The section "/a/c" has been '
 
1021
                                 'converted to use policies.'],
 
1022
                                self.my_config.set_user_option,
 
1023
                                'foo', 'bar', store=config.STORE_LOCATION)
 
1024
        finally:
 
1025
            os.mkdir = real_mkdir
 
1026
 
 
1027
        self.failUnless(self.created, 'Failed to create ~/.bazaar')
 
1028
        self.assertEqual([('__contains__', '/a/c'),
1645
1029
                          ('__contains__', '/a/c/'),
1646
1030
                          ('__setitem__', '/a/c', {}),
1647
1031
                          ('__getitem__', '/a/c'),
1676
1060
        self.assertEqual('bzr', my_config.get_bzr_remote_path())
1677
1061
        my_config.set_user_option('bzr_remote_path', '/path-bzr')
1678
1062
        self.assertEqual('/path-bzr', my_config.get_bzr_remote_path())
1679
 
        self.overrideEnv('BZR_REMOTE_PATH', '/environ-bzr')
 
1063
        os.environ['BZR_REMOTE_PATH'] = '/environ-bzr'
1680
1064
        self.assertEqual('/environ-bzr', my_config.get_bzr_remote_path())
1681
1065
 
1682
1066
 
1690
1074
option = exact
1691
1075
"""
1692
1076
 
 
1077
 
1693
1078
class TestBranchConfigItems(tests.TestCaseInTempDir):
1694
1079
 
1695
1080
    def get_branch_config(self, global_config=None, location=None,
1696
1081
                          location_config=None, branch_data_config=None):
1697
 
        my_branch = FakeBranch(location)
 
1082
        my_config = config.BranchConfig(FakeBranch(location))
1698
1083
        if global_config is not None:
1699
 
            my_global_config = config.GlobalConfig.from_string(global_config,
1700
 
                                                               save=True)
 
1084
            global_file = StringIO(global_config.encode('utf-8'))
 
1085
            my_config._get_global_config()._get_parser(global_file)
 
1086
        self.my_location_config = my_config._get_location_config()
1701
1087
        if location_config is not None:
1702
 
            my_location_config = config.LocationConfig.from_string(
1703
 
                location_config, my_branch.base, save=True)
1704
 
        my_config = config.BranchConfig(my_branch)
 
1088
            location_file = StringIO(location_config.encode('utf-8'))
 
1089
            self.my_location_config._get_parser(location_file)
1705
1090
        if branch_data_config is not None:
1706
1091
            my_config.branch.control_files.files['branch.conf'] = \
1707
1092
                branch_data_config
1721
1106
                         my_config.username())
1722
1107
 
1723
1108
    def test_not_set_in_branch(self):
1724
 
        my_config = self.get_branch_config(global_config=sample_config_text)
 
1109
        my_config = self.get_branch_config(sample_config_text)
1725
1110
        self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
1726
1111
                         my_config._get_user_id())
1727
1112
        my_config.branch.control_files.files['email'] = "John"
1728
1113
        self.assertEqual("John", my_config._get_user_id())
1729
1114
 
1730
1115
    def test_BZR_EMAIL_OVERRIDES(self):
1731
 
        self.overrideEnv('BZR_EMAIL', "Robert Collins <robertc@example.org>")
 
1116
        os.environ['BZR_EMAIL'] = "Robert Collins <robertc@example.org>"
1732
1117
        branch = FakeBranch()
1733
1118
        my_config = config.BranchConfig(branch)
1734
1119
        self.assertEqual("Robert Collins <robertc@example.org>",
1751
1136
 
1752
1137
    def test_gpg_signing_command(self):
1753
1138
        my_config = self.get_branch_config(
1754
 
            global_config=sample_config_text,
1755
1139
            # branch data cannot set gpg_signing_command
1756
1140
            branch_data_config="gpg_signing_command=pgp")
 
1141
        config_file = StringIO(sample_config_text.encode('utf-8'))
 
1142
        my_config._get_global_config()._get_parser(config_file)
1757
1143
        self.assertEqual('gnome-gpg', my_config.gpg_signing_command())
1758
1144
 
1759
1145
    def test_get_user_option_global(self):
1760
 
        my_config = self.get_branch_config(global_config=sample_config_text)
 
1146
        branch = FakeBranch()
 
1147
        my_config = config.BranchConfig(branch)
 
1148
        config_file = StringIO(sample_config_text.encode('utf-8'))
 
1149
        (my_config._get_global_config()._get_parser(config_file))
1761
1150
        self.assertEqual('something',
1762
1151
                         my_config.get_user_option('user_global_option'))
1763
1152
 
1764
1153
    def test_post_commit_default(self):
1765
 
        my_config = self.get_branch_config(global_config=sample_config_text,
1766
 
                                      location='/a/c',
1767
 
                                      location_config=sample_branches_text)
 
1154
        branch = FakeBranch()
 
1155
        my_config = self.get_branch_config(sample_config_text, '/a/c',
 
1156
                                           sample_branches_text)
1768
1157
        self.assertEqual(my_config.branch.base, '/a/c')
1769
1158
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1770
1159
                         my_config.post_commit())
1771
1160
        my_config.set_user_option('post_commit', 'rmtree_root')
1772
 
        # post-commit is ignored when present in branch data
 
1161
        # post-commit is ignored when bresent in branch data
1773
1162
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1774
1163
                         my_config.post_commit())
1775
1164
        my_config.set_user_option('post_commit', 'rmtree_root',
1777
1166
        self.assertEqual('rmtree_root', my_config.post_commit())
1778
1167
 
1779
1168
    def test_config_precedence(self):
1780
 
        # FIXME: eager test, luckily no persitent config file makes it fail
1781
 
        # -- vila 20100716
1782
1169
        my_config = self.get_branch_config(global_config=precedence_global)
1783
1170
        self.assertEqual(my_config.get_user_option('option'), 'global')
1784
1171
        my_config = self.get_branch_config(global_config=precedence_global,
1785
 
                                           branch_data_config=precedence_branch)
 
1172
                                      branch_data_config=precedence_branch)
1786
1173
        self.assertEqual(my_config.get_user_option('option'), 'branch')
1787
 
        my_config = self.get_branch_config(
1788
 
            global_config=precedence_global,
1789
 
            branch_data_config=precedence_branch,
1790
 
            location_config=precedence_location)
 
1174
        my_config = self.get_branch_config(global_config=precedence_global,
 
1175
                                      branch_data_config=precedence_branch,
 
1176
                                      location_config=precedence_location)
1791
1177
        self.assertEqual(my_config.get_user_option('option'), 'recurse')
1792
 
        my_config = self.get_branch_config(
1793
 
            global_config=precedence_global,
1794
 
            branch_data_config=precedence_branch,
1795
 
            location_config=precedence_location,
1796
 
            location='http://example.com/specific')
 
1178
        my_config = self.get_branch_config(global_config=precedence_global,
 
1179
                                      branch_data_config=precedence_branch,
 
1180
                                      location_config=precedence_location,
 
1181
                                      location='http://example.com/specific')
1797
1182
        self.assertEqual(my_config.get_user_option('option'), 'exact')
1798
1183
 
1799
1184
    def test_get_mail_client(self):
1889
1274
 
1890
1275
class TestTransportConfig(tests.TestCaseWithTransport):
1891
1276
 
1892
 
    def test_load_utf8(self):
1893
 
        """Ensure we can load an utf8-encoded file."""
1894
 
        t = self.get_transport()
1895
 
        unicode_user = u'b\N{Euro Sign}ar'
1896
 
        unicode_content = u'user=%s' % (unicode_user,)
1897
 
        utf8_content = unicode_content.encode('utf8')
1898
 
        # Store the raw content in the config file
1899
 
        t.put_bytes('foo.conf', utf8_content)
1900
 
        conf = config.TransportConfig(t, 'foo.conf')
1901
 
        self.assertEquals(unicode_user, conf.get_option('user'))
1902
 
 
1903
 
    def test_load_non_ascii(self):
1904
 
        """Ensure we display a proper error on non-ascii, non utf-8 content."""
1905
 
        t = self.get_transport()
1906
 
        t.put_bytes('foo.conf', 'user=foo\n#\xff\n')
1907
 
        conf = config.TransportConfig(t, 'foo.conf')
1908
 
        self.assertRaises(errors.ConfigContentError, conf._get_configobj)
1909
 
 
1910
 
    def test_load_erroneous_content(self):
1911
 
        """Ensure we display a proper error on content that can't be parsed."""
1912
 
        t = self.get_transport()
1913
 
        t.put_bytes('foo.conf', '[open_section\n')
1914
 
        conf = config.TransportConfig(t, 'foo.conf')
1915
 
        self.assertRaises(errors.ParseConfigError, conf._get_configobj)
1916
 
 
1917
1277
    def test_get_value(self):
1918
1278
        """Test that retreiving a value from a section is possible"""
1919
 
        bzrdir_config = config.TransportConfig(self.get_transport('.'),
 
1279
        bzrdir_config = config.TransportConfig(transport.get_transport('.'),
1920
1280
                                               'control.conf')
1921
1281
        bzrdir_config.set_option('value', 'key', 'SECTION')
1922
1282
        bzrdir_config.set_option('value2', 'key2')
1952
1312
        self.assertIs(None, bzrdir_config.get_default_stack_on())
1953
1313
 
1954
1314
 
1955
 
class TestOldConfigHooks(tests.TestCaseWithTransport):
1956
 
 
1957
 
    def setUp(self):
1958
 
        super(TestOldConfigHooks, self).setUp()
1959
 
        create_configs_with_file_option(self)
1960
 
 
1961
 
    def assertGetHook(self, conf, name, value):
1962
 
        calls = []
1963
 
        def hook(*args):
1964
 
            calls.append(args)
1965
 
        config.OldConfigHooks.install_named_hook('get', hook, None)
1966
 
        self.addCleanup(
1967
 
            config.OldConfigHooks.uninstall_named_hook, 'get', None)
1968
 
        self.assertLength(0, calls)
1969
 
        actual_value = conf.get_user_option(name)
1970
 
        self.assertEquals(value, actual_value)
1971
 
        self.assertLength(1, calls)
1972
 
        self.assertEquals((conf, name, value), calls[0])
1973
 
 
1974
 
    def test_get_hook_bazaar(self):
1975
 
        self.assertGetHook(self.bazaar_config, 'file', 'bazaar')
1976
 
 
1977
 
    def test_get_hook_locations(self):
1978
 
        self.assertGetHook(self.locations_config, 'file', 'locations')
1979
 
 
1980
 
    def test_get_hook_branch(self):
1981
 
        # Since locations masks branch, we define a different option
1982
 
        self.branch_config.set_user_option('file2', 'branch')
1983
 
        self.assertGetHook(self.branch_config, 'file2', 'branch')
1984
 
 
1985
 
    def assertSetHook(self, conf, name, value):
1986
 
        calls = []
1987
 
        def hook(*args):
1988
 
            calls.append(args)
1989
 
        config.OldConfigHooks.install_named_hook('set', hook, None)
1990
 
        self.addCleanup(
1991
 
            config.OldConfigHooks.uninstall_named_hook, 'set', None)
1992
 
        self.assertLength(0, calls)
1993
 
        conf.set_user_option(name, value)
1994
 
        self.assertLength(1, calls)
1995
 
        # We can't assert the conf object below as different configs use
1996
 
        # different means to implement set_user_option and we care only about
1997
 
        # coverage here.
1998
 
        self.assertEquals((name, value), calls[0][1:])
1999
 
 
2000
 
    def test_set_hook_bazaar(self):
2001
 
        self.assertSetHook(self.bazaar_config, 'foo', 'bazaar')
2002
 
 
2003
 
    def test_set_hook_locations(self):
2004
 
        self.assertSetHook(self.locations_config, 'foo', 'locations')
2005
 
 
2006
 
    def test_set_hook_branch(self):
2007
 
        self.assertSetHook(self.branch_config, 'foo', 'branch')
2008
 
 
2009
 
    def assertRemoveHook(self, conf, name, section_name=None):
2010
 
        calls = []
2011
 
        def hook(*args):
2012
 
            calls.append(args)
2013
 
        config.OldConfigHooks.install_named_hook('remove', hook, None)
2014
 
        self.addCleanup(
2015
 
            config.OldConfigHooks.uninstall_named_hook, 'remove', None)
2016
 
        self.assertLength(0, calls)
2017
 
        conf.remove_user_option(name, section_name)
2018
 
        self.assertLength(1, calls)
2019
 
        # We can't assert the conf object below as different configs use
2020
 
        # different means to implement remove_user_option and we care only about
2021
 
        # coverage here.
2022
 
        self.assertEquals((name,), calls[0][1:])
2023
 
 
2024
 
    def test_remove_hook_bazaar(self):
2025
 
        self.assertRemoveHook(self.bazaar_config, 'file')
2026
 
 
2027
 
    def test_remove_hook_locations(self):
2028
 
        self.assertRemoveHook(self.locations_config, 'file',
2029
 
                              self.locations_config.location)
2030
 
 
2031
 
    def test_remove_hook_branch(self):
2032
 
        self.assertRemoveHook(self.branch_config, 'file')
2033
 
 
2034
 
    def assertLoadHook(self, name, conf_class, *conf_args):
2035
 
        calls = []
2036
 
        def hook(*args):
2037
 
            calls.append(args)
2038
 
        config.OldConfigHooks.install_named_hook('load', hook, None)
2039
 
        self.addCleanup(
2040
 
            config.OldConfigHooks.uninstall_named_hook, 'load', None)
2041
 
        self.assertLength(0, calls)
2042
 
        # Build a config
2043
 
        conf = conf_class(*conf_args)
2044
 
        # Access an option to trigger a load
2045
 
        conf.get_user_option(name)
2046
 
        self.assertLength(1, calls)
2047
 
        # Since we can't assert about conf, we just use the number of calls ;-/
2048
 
 
2049
 
    def test_load_hook_bazaar(self):
2050
 
        self.assertLoadHook('file', config.GlobalConfig)
2051
 
 
2052
 
    def test_load_hook_locations(self):
2053
 
        self.assertLoadHook('file', config.LocationConfig, self.tree.basedir)
2054
 
 
2055
 
    def test_load_hook_branch(self):
2056
 
        self.assertLoadHook('file', config.BranchConfig, self.tree.branch)
2057
 
 
2058
 
    def assertSaveHook(self, conf):
2059
 
        calls = []
2060
 
        def hook(*args):
2061
 
            calls.append(args)
2062
 
        config.OldConfigHooks.install_named_hook('save', hook, None)
2063
 
        self.addCleanup(
2064
 
            config.OldConfigHooks.uninstall_named_hook, 'save', None)
2065
 
        self.assertLength(0, calls)
2066
 
        # Setting an option triggers a save
2067
 
        conf.set_user_option('foo', 'bar')
2068
 
        self.assertLength(1, calls)
2069
 
        # Since we can't assert about conf, we just use the number of calls ;-/
2070
 
 
2071
 
    def test_save_hook_bazaar(self):
2072
 
        self.assertSaveHook(self.bazaar_config)
2073
 
 
2074
 
    def test_save_hook_locations(self):
2075
 
        self.assertSaveHook(self.locations_config)
2076
 
 
2077
 
    def test_save_hook_branch(self):
2078
 
        self.assertSaveHook(self.branch_config)
2079
 
 
2080
 
 
2081
 
class TestOldConfigHooksForRemote(tests.TestCaseWithTransport):
2082
 
    """Tests config hooks for remote configs.
2083
 
 
2084
 
    No tests for the remove hook as this is not implemented there.
2085
 
    """
2086
 
 
2087
 
    def setUp(self):
2088
 
        super(TestOldConfigHooksForRemote, self).setUp()
2089
 
        self.transport_server = test_server.SmartTCPServer_for_testing
2090
 
        create_configs_with_file_option(self)
2091
 
 
2092
 
    def assertGetHook(self, conf, name, value):
2093
 
        calls = []
2094
 
        def hook(*args):
2095
 
            calls.append(args)
2096
 
        config.OldConfigHooks.install_named_hook('get', hook, None)
2097
 
        self.addCleanup(
2098
 
            config.OldConfigHooks.uninstall_named_hook, 'get', None)
2099
 
        self.assertLength(0, calls)
2100
 
        actual_value = conf.get_option(name)
2101
 
        self.assertEquals(value, actual_value)
2102
 
        self.assertLength(1, calls)
2103
 
        self.assertEquals((conf, name, value), calls[0])
2104
 
 
2105
 
    def test_get_hook_remote_branch(self):
2106
 
        remote_branch = branch.Branch.open(self.get_url('tree'))
2107
 
        self.assertGetHook(remote_branch._get_config(), 'file', 'branch')
2108
 
 
2109
 
    def test_get_hook_remote_bzrdir(self):
2110
 
        remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2111
 
        conf = remote_bzrdir._get_config()
2112
 
        conf.set_option('remotedir', 'file')
2113
 
        self.assertGetHook(conf, 'file', 'remotedir')
2114
 
 
2115
 
    def assertSetHook(self, conf, name, value):
2116
 
        calls = []
2117
 
        def hook(*args):
2118
 
            calls.append(args)
2119
 
        config.OldConfigHooks.install_named_hook('set', hook, None)
2120
 
        self.addCleanup(
2121
 
            config.OldConfigHooks.uninstall_named_hook, 'set', None)
2122
 
        self.assertLength(0, calls)
2123
 
        conf.set_option(value, name)
2124
 
        self.assertLength(1, calls)
2125
 
        # We can't assert the conf object below as different configs use
2126
 
        # different means to implement set_user_option and we care only about
2127
 
        # coverage here.
2128
 
        self.assertEquals((name, value), calls[0][1:])
2129
 
 
2130
 
    def test_set_hook_remote_branch(self):
2131
 
        remote_branch = branch.Branch.open(self.get_url('tree'))
2132
 
        self.addCleanup(remote_branch.lock_write().unlock)
2133
 
        self.assertSetHook(remote_branch._get_config(), 'file', 'remote')
2134
 
 
2135
 
    def test_set_hook_remote_bzrdir(self):
2136
 
        remote_branch = branch.Branch.open(self.get_url('tree'))
2137
 
        self.addCleanup(remote_branch.lock_write().unlock)
2138
 
        remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2139
 
        self.assertSetHook(remote_bzrdir._get_config(), 'file', 'remotedir')
2140
 
 
2141
 
    def assertLoadHook(self, expected_nb_calls, name, conf_class, *conf_args):
2142
 
        calls = []
2143
 
        def hook(*args):
2144
 
            calls.append(args)
2145
 
        config.OldConfigHooks.install_named_hook('load', hook, None)
2146
 
        self.addCleanup(
2147
 
            config.OldConfigHooks.uninstall_named_hook, 'load', None)
2148
 
        self.assertLength(0, calls)
2149
 
        # Build a config
2150
 
        conf = conf_class(*conf_args)
2151
 
        # Access an option to trigger a load
2152
 
        conf.get_option(name)
2153
 
        self.assertLength(expected_nb_calls, calls)
2154
 
        # Since we can't assert about conf, we just use the number of calls ;-/
2155
 
 
2156
 
    def test_load_hook_remote_branch(self):
2157
 
        remote_branch = branch.Branch.open(self.get_url('tree'))
2158
 
        self.assertLoadHook(1, 'file', remote.RemoteBranchConfig, remote_branch)
2159
 
 
2160
 
    def test_load_hook_remote_bzrdir(self):
2161
 
        remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2162
 
        # The config file doesn't exist, set an option to force its creation
2163
 
        conf = remote_bzrdir._get_config()
2164
 
        conf.set_option('remotedir', 'file')
2165
 
        # We get one call for the server and one call for the client, this is
2166
 
        # caused by the differences in implementations betwen
2167
 
        # SmartServerBzrDirRequestConfigFile (in smart/bzrdir.py) and
2168
 
        # SmartServerBranchGetConfigFile (in smart/branch.py)
2169
 
        self.assertLoadHook(2 ,'file', remote.RemoteBzrDirConfig, remote_bzrdir)
2170
 
 
2171
 
    def assertSaveHook(self, conf):
2172
 
        calls = []
2173
 
        def hook(*args):
2174
 
            calls.append(args)
2175
 
        config.OldConfigHooks.install_named_hook('save', hook, None)
2176
 
        self.addCleanup(
2177
 
            config.OldConfigHooks.uninstall_named_hook, 'save', None)
2178
 
        self.assertLength(0, calls)
2179
 
        # Setting an option triggers a save
2180
 
        conf.set_option('foo', 'bar')
2181
 
        self.assertLength(1, calls)
2182
 
        # Since we can't assert about conf, we just use the number of calls ;-/
2183
 
 
2184
 
    def test_save_hook_remote_branch(self):
2185
 
        remote_branch = branch.Branch.open(self.get_url('tree'))
2186
 
        self.addCleanup(remote_branch.lock_write().unlock)
2187
 
        self.assertSaveHook(remote_branch._get_config())
2188
 
 
2189
 
    def test_save_hook_remote_bzrdir(self):
2190
 
        remote_branch = branch.Branch.open(self.get_url('tree'))
2191
 
        self.addCleanup(remote_branch.lock_write().unlock)
2192
 
        remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2193
 
        self.assertSaveHook(remote_bzrdir._get_config())
2194
 
 
2195
 
 
2196
 
class TestOption(tests.TestCase):
2197
 
 
2198
 
    def test_default_value(self):
2199
 
        opt = config.Option('foo', default='bar')
2200
 
        self.assertEquals('bar', opt.get_default())
2201
 
 
2202
 
 
2203
 
class TestOptionRegistry(tests.TestCase):
2204
 
 
2205
 
    def setUp(self):
2206
 
        super(TestOptionRegistry, self).setUp()
2207
 
        # Always start with an empty registry
2208
 
        self.overrideAttr(config, 'option_registry', registry.Registry())
2209
 
        self.registry = config.option_registry
2210
 
 
2211
 
    def test_register(self):
2212
 
        opt = config.Option('foo')
2213
 
        self.registry.register('foo', opt)
2214
 
        self.assertIs(opt, self.registry.get('foo'))
2215
 
 
2216
 
    lazy_option = config.Option('lazy_foo')
2217
 
 
2218
 
    def test_register_lazy(self):
2219
 
        self.registry.register_lazy('foo', self.__module__,
2220
 
                                    'TestOptionRegistry.lazy_option')
2221
 
        self.assertIs(self.lazy_option, self.registry.get('foo'))
2222
 
 
2223
 
    def test_registered_help(self):
2224
 
        opt = config.Option('foo')
2225
 
        self.registry.register('foo', opt, help='A simple option')
2226
 
        self.assertEquals('A simple option', self.registry.get_help('foo'))
2227
 
 
2228
 
 
2229
 
class TestRegisteredOptions(tests.TestCase):
2230
 
    """All registered options should verify some constraints."""
2231
 
 
2232
 
    scenarios = [(key, {'option_name': key, 'option': option}) for key, option
2233
 
                 in config.option_registry.iteritems()]
2234
 
 
2235
 
    def setUp(self):
2236
 
        super(TestRegisteredOptions, self).setUp()
2237
 
        self.registry = config.option_registry
2238
 
 
2239
 
    def test_proper_name(self):
2240
 
        # An option should be registered under its own name, this can't be
2241
 
        # checked at registration time for the lazy ones.
2242
 
        self.assertEquals(self.option_name, self.option.name)
2243
 
 
2244
 
    def test_help_is_set(self):
2245
 
        option_help = self.registry.get_help(self.option_name)
2246
 
        self.assertNotEquals(None, option_help)
2247
 
        # Come on, think about the user, he really wants to know whst the
2248
 
        # option is about
2249
 
        self.assertNotEquals('', option_help)
2250
 
 
2251
 
 
2252
 
class TestSection(tests.TestCase):
2253
 
 
2254
 
    # FIXME: Parametrize so that all sections produced by Stores run these
2255
 
    # tests -- vila 2011-04-01
2256
 
 
2257
 
    def test_get_a_value(self):
2258
 
        a_dict = dict(foo='bar')
2259
 
        section = config.Section('myID', a_dict)
2260
 
        self.assertEquals('bar', section.get('foo'))
2261
 
 
2262
 
    def test_get_unknown_option(self):
2263
 
        a_dict = dict()
2264
 
        section = config.Section(None, a_dict)
2265
 
        self.assertEquals('out of thin air',
2266
 
                          section.get('foo', 'out of thin air'))
2267
 
 
2268
 
    def test_options_is_shared(self):
2269
 
        a_dict = dict()
2270
 
        section = config.Section(None, a_dict)
2271
 
        self.assertIs(a_dict, section.options)
2272
 
 
2273
 
 
2274
 
class TestMutableSection(tests.TestCase):
2275
 
 
2276
 
    # FIXME: Parametrize so that all sections (including os.environ and the
2277
 
    # ones produced by Stores) run these tests -- vila 2011-04-01
2278
 
 
2279
 
    def test_set(self):
2280
 
        a_dict = dict(foo='bar')
2281
 
        section = config.MutableSection('myID', a_dict)
2282
 
        section.set('foo', 'new_value')
2283
 
        self.assertEquals('new_value', section.get('foo'))
2284
 
        # The change appears in the shared section
2285
 
        self.assertEquals('new_value', a_dict.get('foo'))
2286
 
        # We keep track of the change
2287
 
        self.assertTrue('foo' in section.orig)
2288
 
        self.assertEquals('bar', section.orig.get('foo'))
2289
 
 
2290
 
    def test_set_preserve_original_once(self):
2291
 
        a_dict = dict(foo='bar')
2292
 
        section = config.MutableSection('myID', a_dict)
2293
 
        section.set('foo', 'first_value')
2294
 
        section.set('foo', 'second_value')
2295
 
        # We keep track of the original value
2296
 
        self.assertTrue('foo' in section.orig)
2297
 
        self.assertEquals('bar', section.orig.get('foo'))
2298
 
 
2299
 
    def test_remove(self):
2300
 
        a_dict = dict(foo='bar')
2301
 
        section = config.MutableSection('myID', a_dict)
2302
 
        section.remove('foo')
2303
 
        # We get None for unknown options via the default value
2304
 
        self.assertEquals(None, section.get('foo'))
2305
 
        # Or we just get the default value
2306
 
        self.assertEquals('unknown', section.get('foo', 'unknown'))
2307
 
        self.assertFalse('foo' in section.options)
2308
 
        # We keep track of the deletion
2309
 
        self.assertTrue('foo' in section.orig)
2310
 
        self.assertEquals('bar', section.orig.get('foo'))
2311
 
 
2312
 
    def test_remove_new_option(self):
2313
 
        a_dict = dict()
2314
 
        section = config.MutableSection('myID', a_dict)
2315
 
        section.set('foo', 'bar')
2316
 
        section.remove('foo')
2317
 
        self.assertFalse('foo' in section.options)
2318
 
        # The option didn't exist initially so it we need to keep track of it
2319
 
        # with a special value
2320
 
        self.assertTrue('foo' in section.orig)
2321
 
        self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
2322
 
 
2323
 
 
2324
 
class TestStore(tests.TestCaseWithTransport):
2325
 
 
2326
 
    def assertSectionContent(self, expected, section):
2327
 
        """Assert that some options have the proper values in a section."""
2328
 
        expected_name, expected_options = expected
2329
 
        self.assertEquals(expected_name, section.id)
2330
 
        self.assertEquals(
2331
 
            expected_options,
2332
 
            dict([(k, section.get(k)) for k in expected_options.keys()]))
2333
 
 
2334
 
 
2335
 
class TestReadonlyStore(TestStore):
2336
 
 
2337
 
    scenarios = [(key, {'get_store': builder}) for key, builder
2338
 
                 in config.test_store_builder_registry.iteritems()]
2339
 
 
2340
 
    def setUp(self):
2341
 
        super(TestReadonlyStore, self).setUp()
2342
 
 
2343
 
    def test_building_delays_load(self):
2344
 
        store = self.get_store(self)
2345
 
        self.assertEquals(False, store.is_loaded())
2346
 
        store._load_from_string('')
2347
 
        self.assertEquals(True, store.is_loaded())
2348
 
 
2349
 
    def test_get_no_sections_for_empty(self):
2350
 
        store = self.get_store(self)
2351
 
        store._load_from_string('')
2352
 
        self.assertEquals([], list(store.get_sections()))
2353
 
 
2354
 
    def test_get_default_section(self):
2355
 
        store = self.get_store(self)
2356
 
        store._load_from_string('foo=bar')
2357
 
        sections = list(store.get_sections())
2358
 
        self.assertLength(1, sections)
2359
 
        self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2360
 
 
2361
 
    def test_get_named_section(self):
2362
 
        store = self.get_store(self)
2363
 
        store._load_from_string('[baz]\nfoo=bar')
2364
 
        sections = list(store.get_sections())
2365
 
        self.assertLength(1, sections)
2366
 
        self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2367
 
 
2368
 
    def test_load_from_string_fails_for_non_empty_store(self):
2369
 
        store = self.get_store(self)
2370
 
        store._load_from_string('foo=bar')
2371
 
        self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2372
 
 
2373
 
 
2374
 
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2375
 
    """Simulate loading a config store without content of various encodings.
2376
 
 
2377
 
    All files produced by bzr are in utf8 content.
2378
 
 
2379
 
    Users may modify them manually and end up with a file that can't be
2380
 
    loaded. We need to issue proper error messages in this case.
2381
 
    """
2382
 
 
2383
 
    invalid_utf8_char = '\xff'
2384
 
 
2385
 
    def test_load_utf8(self):
2386
 
        """Ensure we can load an utf8-encoded file."""
2387
 
        t = self.get_transport()
2388
 
        # From http://pad.lv/799212
2389
 
        unicode_user = u'b\N{Euro Sign}ar'
2390
 
        unicode_content = u'user=%s' % (unicode_user,)
2391
 
        utf8_content = unicode_content.encode('utf8')
2392
 
        # Store the raw content in the config file
2393
 
        t.put_bytes('foo.conf', utf8_content)
2394
 
        store = config.IniFileStore(t, 'foo.conf')
2395
 
        store.load()
2396
 
        stack = config.Stack([store.get_sections], store)
2397
 
        self.assertEquals(unicode_user, stack.get('user'))
2398
 
 
2399
 
    def test_load_non_ascii(self):
2400
 
        """Ensure we display a proper error on non-ascii, non utf-8 content."""
2401
 
        t = self.get_transport()
2402
 
        t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2403
 
        store = config.IniFileStore(t, 'foo.conf')
2404
 
        self.assertRaises(errors.ConfigContentError, store.load)
2405
 
 
2406
 
    def test_load_erroneous_content(self):
2407
 
        """Ensure we display a proper error on content that can't be parsed."""
2408
 
        t = self.get_transport()
2409
 
        t.put_bytes('foo.conf', '[open_section\n')
2410
 
        store = config.IniFileStore(t, 'foo.conf')
2411
 
        self.assertRaises(errors.ParseConfigError, store.load)
2412
 
 
2413
 
 
2414
 
class TestIniConfigContent(tests.TestCaseWithTransport):
2415
 
    """Simulate loading a IniBasedConfig without content of various encodings.
2416
 
 
2417
 
    All files produced by bzr are in utf8 content.
2418
 
 
2419
 
    Users may modify them manually and end up with a file that can't be
2420
 
    loaded. We need to issue proper error messages in this case.
2421
 
    """
2422
 
 
2423
 
    invalid_utf8_char = '\xff'
2424
 
 
2425
 
    def test_load_utf8(self):
2426
 
        """Ensure we can load an utf8-encoded file."""
2427
 
        # From http://pad.lv/799212
2428
 
        unicode_user = u'b\N{Euro Sign}ar'
2429
 
        unicode_content = u'user=%s' % (unicode_user,)
2430
 
        utf8_content = unicode_content.encode('utf8')
2431
 
        # Store the raw content in the config file
2432
 
        with open('foo.conf', 'wb') as f:
2433
 
            f.write(utf8_content)
2434
 
        conf = config.IniBasedConfig(file_name='foo.conf')
2435
 
        self.assertEquals(unicode_user, conf.get_user_option('user'))
2436
 
 
2437
 
    def test_load_badly_encoded_content(self):
2438
 
        """Ensure we display a proper error on non-ascii, non utf-8 content."""
2439
 
        with open('foo.conf', 'wb') as f:
2440
 
            f.write('user=foo\n#%s\n' % (self.invalid_utf8_char,))
2441
 
        conf = config.IniBasedConfig(file_name='foo.conf')
2442
 
        self.assertRaises(errors.ConfigContentError, conf._get_parser)
2443
 
 
2444
 
    def test_load_erroneous_content(self):
2445
 
        """Ensure we display a proper error on content that can't be parsed."""
2446
 
        with open('foo.conf', 'wb') as f:
2447
 
            f.write('[open_section\n')
2448
 
        conf = config.IniBasedConfig(file_name='foo.conf')
2449
 
        self.assertRaises(errors.ParseConfigError, conf._get_parser)
2450
 
 
2451
 
 
2452
 
class TestMutableStore(TestStore):
2453
 
 
2454
 
    scenarios = [(key, {'store_id': key, 'get_store': builder}) for key, builder
2455
 
                 in config.test_store_builder_registry.iteritems()]
2456
 
 
2457
 
    def setUp(self):
2458
 
        super(TestMutableStore, self).setUp()
2459
 
        self.transport = self.get_transport()
2460
 
 
2461
 
    def has_store(self, store):
2462
 
        store_basename = urlutils.relative_url(self.transport.external_url(),
2463
 
                                               store.external_url())
2464
 
        return self.transport.has(store_basename)
2465
 
 
2466
 
    def test_save_empty_creates_no_file(self):
2467
 
        # FIXME: There should be a better way than relying on the test
2468
 
        # parametrization to identify branch.conf -- vila 2011-0526
2469
 
        if self.store_id in ('branch', 'remote_branch'):
2470
 
            raise tests.TestNotApplicable(
2471
 
                'branch.conf is *always* created when a branch is initialized')
2472
 
        store = self.get_store(self)
2473
 
        store.save()
2474
 
        self.assertEquals(False, self.has_store(store))
2475
 
 
2476
 
    def test_save_emptied_succeeds(self):
2477
 
        store = self.get_store(self)
2478
 
        store._load_from_string('foo=bar\n')
2479
 
        section = store.get_mutable_section(None)
2480
 
        section.remove('foo')
2481
 
        store.save()
2482
 
        self.assertEquals(True, self.has_store(store))
2483
 
        modified_store = self.get_store(self)
2484
 
        sections = list(modified_store.get_sections())
2485
 
        self.assertLength(0, sections)
2486
 
 
2487
 
    def test_save_with_content_succeeds(self):
2488
 
        # FIXME: There should be a better way than relying on the test
2489
 
        # parametrization to identify branch.conf -- vila 2011-0526
2490
 
        if self.store_id in ('branch', 'remote_branch'):
2491
 
            raise tests.TestNotApplicable(
2492
 
                'branch.conf is *always* created when a branch is initialized')
2493
 
        store = self.get_store(self)
2494
 
        store._load_from_string('foo=bar\n')
2495
 
        self.assertEquals(False, self.has_store(store))
2496
 
        store.save()
2497
 
        self.assertEquals(True, self.has_store(store))
2498
 
        modified_store = self.get_store(self)
2499
 
        sections = list(modified_store.get_sections())
2500
 
        self.assertLength(1, sections)
2501
 
        self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2502
 
 
2503
 
    def test_set_option_in_empty_store(self):
2504
 
        store = self.get_store(self)
2505
 
        section = store.get_mutable_section(None)
2506
 
        section.set('foo', 'bar')
2507
 
        store.save()
2508
 
        modified_store = self.get_store(self)
2509
 
        sections = list(modified_store.get_sections())
2510
 
        self.assertLength(1, sections)
2511
 
        self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2512
 
 
2513
 
    def test_set_option_in_default_section(self):
2514
 
        store = self.get_store(self)
2515
 
        store._load_from_string('')
2516
 
        section = store.get_mutable_section(None)
2517
 
        section.set('foo', 'bar')
2518
 
        store.save()
2519
 
        modified_store = self.get_store(self)
2520
 
        sections = list(modified_store.get_sections())
2521
 
        self.assertLength(1, sections)
2522
 
        self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2523
 
 
2524
 
    def test_set_option_in_named_section(self):
2525
 
        store = self.get_store(self)
2526
 
        store._load_from_string('')
2527
 
        section = store.get_mutable_section('baz')
2528
 
        section.set('foo', 'bar')
2529
 
        store.save()
2530
 
        modified_store = self.get_store(self)
2531
 
        sections = list(modified_store.get_sections())
2532
 
        self.assertLength(1, sections)
2533
 
        self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2534
 
 
2535
 
    def test_load_hook(self):
2536
 
        # We first needs to ensure that the store exists
2537
 
        store = self.get_store(self)
2538
 
        section = store.get_mutable_section('baz')
2539
 
        section.set('foo', 'bar')
2540
 
        store.save()
2541
 
        # Now we can try to load it
2542
 
        store = self.get_store(self)
2543
 
        calls = []
2544
 
        def hook(*args):
2545
 
            calls.append(args)
2546
 
        config.ConfigHooks.install_named_hook('load', hook, None)
2547
 
        self.assertLength(0, calls)
2548
 
        store.load()
2549
 
        self.assertLength(1, calls)
2550
 
        self.assertEquals((store,), calls[0])
2551
 
 
2552
 
    def test_save_hook(self):
2553
 
        calls = []
2554
 
        def hook(*args):
2555
 
            calls.append(args)
2556
 
        config.ConfigHooks.install_named_hook('save', hook, None)
2557
 
        self.assertLength(0, calls)
2558
 
        store = self.get_store(self)
2559
 
        section = store.get_mutable_section('baz')
2560
 
        section.set('foo', 'bar')
2561
 
        store.save()
2562
 
        self.assertLength(1, calls)
2563
 
        self.assertEquals((store,), calls[0])
2564
 
 
2565
 
 
2566
 
class TestIniFileStore(TestStore):
2567
 
 
2568
 
    def test_loading_unknown_file_fails(self):
2569
 
        store = config.IniFileStore(self.get_transport(), 'I-do-not-exist')
2570
 
        self.assertRaises(errors.NoSuchFile, store.load)
2571
 
 
2572
 
    def test_invalid_content(self):
2573
 
        store = config.IniFileStore(self.get_transport(), 'foo.conf', )
2574
 
        self.assertEquals(False, store.is_loaded())
2575
 
        exc = self.assertRaises(
2576
 
            errors.ParseConfigError, store._load_from_string,
2577
 
            'this is invalid !')
2578
 
        self.assertEndsWith(exc.filename, 'foo.conf')
2579
 
        # And the load failed
2580
 
        self.assertEquals(False, store.is_loaded())
2581
 
 
2582
 
    def test_get_embedded_sections(self):
2583
 
        # A more complicated example (which also shows that section names and
2584
 
        # option names share the same name space...)
2585
 
        # FIXME: This should be fixed by forbidding dicts as values ?
2586
 
        # -- vila 2011-04-05
2587
 
        store = config.IniFileStore(self.get_transport(), 'foo.conf', )
2588
 
        store._load_from_string('''
2589
 
foo=bar
2590
 
l=1,2
2591
 
[DEFAULT]
2592
 
foo_in_DEFAULT=foo_DEFAULT
2593
 
[bar]
2594
 
foo_in_bar=barbar
2595
 
[baz]
2596
 
foo_in_baz=barbaz
2597
 
[[qux]]
2598
 
foo_in_qux=quux
2599
 
''')
2600
 
        sections = list(store.get_sections())
2601
 
        self.assertLength(4, sections)
2602
 
        # The default section has no name.
2603
 
        # List values are provided as lists
2604
 
        self.assertSectionContent((None, {'foo': 'bar', 'l': ['1', '2']}),
2605
 
                                  sections[0])
2606
 
        self.assertSectionContent(
2607
 
            ('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
2608
 
        self.assertSectionContent(
2609
 
            ('bar', {'foo_in_bar': 'barbar'}), sections[2])
2610
 
        # sub sections are provided as embedded dicts.
2611
 
        self.assertSectionContent(
2612
 
            ('baz', {'foo_in_baz': 'barbaz', 'qux': {'foo_in_qux': 'quux'}}),
2613
 
            sections[3])
2614
 
 
2615
 
 
2616
 
class TestLockableIniFileStore(TestStore):
2617
 
 
2618
 
    def test_create_store_in_created_dir(self):
2619
 
        self.assertPathDoesNotExist('dir')
2620
 
        t = self.get_transport('dir/subdir')
2621
 
        store = config.LockableIniFileStore(t, 'foo.conf')
2622
 
        store.get_mutable_section(None).set('foo', 'bar')
2623
 
        store.save()
2624
 
        self.assertPathExists('dir/subdir')
2625
 
 
2626
 
 
2627
 
class TestConcurrentStoreUpdates(TestStore):
2628
 
    """Test that Stores properly handle conccurent updates.
2629
 
 
2630
 
    New Store implementation may fail some of these tests but until such
2631
 
    implementations exist it's hard to properly filter them from the scenarios
2632
 
    applied here. If you encounter such a case, contact the bzr devs.
2633
 
    """
2634
 
 
2635
 
    scenarios = [(key, {'get_stack': builder}) for key, builder
2636
 
                 in config.test_stack_builder_registry.iteritems()]
2637
 
 
2638
 
    def setUp(self):
2639
 
        super(TestConcurrentStoreUpdates, self).setUp()
2640
 
        self._content = 'one=1\ntwo=2\n'
2641
 
        self.stack = self.get_stack(self)
2642
 
        if not isinstance(self.stack, config._CompatibleStack):
2643
 
            raise tests.TestNotApplicable(
2644
 
                '%s is not meant to be compatible with the old config design'
2645
 
                % (self.stack,))
2646
 
        self.stack.store._load_from_string(self._content)
2647
 
        # Flush the store
2648
 
        self.stack.store.save()
2649
 
 
2650
 
    def test_simple_read_access(self):
2651
 
        self.assertEquals('1', self.stack.get('one'))
2652
 
 
2653
 
    def test_simple_write_access(self):
2654
 
        self.stack.set('one', 'one')
2655
 
        self.assertEquals('one', self.stack.get('one'))
2656
 
 
2657
 
    def test_listen_to_the_last_speaker(self):
2658
 
        c1 = self.stack
2659
 
        c2 = self.get_stack(self)
2660
 
        c1.set('one', 'ONE')
2661
 
        c2.set('two', 'TWO')
2662
 
        self.assertEquals('ONE', c1.get('one'))
2663
 
        self.assertEquals('TWO', c2.get('two'))
2664
 
        # The second update respect the first one
2665
 
        self.assertEquals('ONE', c2.get('one'))
2666
 
 
2667
 
    def test_last_speaker_wins(self):
2668
 
        # If the same config is not shared, the same variable modified twice
2669
 
        # can only see a single result.
2670
 
        c1 = self.stack
2671
 
        c2 = self.get_stack(self)
2672
 
        c1.set('one', 'c1')
2673
 
        c2.set('one', 'c2')
2674
 
        self.assertEquals('c2', c2.get('one'))
2675
 
        # The first modification is still available until another refresh
2676
 
        # occur
2677
 
        self.assertEquals('c1', c1.get('one'))
2678
 
        c1.set('two', 'done')
2679
 
        self.assertEquals('c2', c1.get('one'))
2680
 
 
2681
 
    def test_writes_are_serialized(self):
2682
 
        c1 = self.stack
2683
 
        c2 = self.get_stack(self)
2684
 
 
2685
 
        # We spawn a thread that will pause *during* the config saving.
2686
 
        before_writing = threading.Event()
2687
 
        after_writing = threading.Event()
2688
 
        writing_done = threading.Event()
2689
 
        c1_save_without_locking_orig = c1.store.save_without_locking
2690
 
        def c1_save_without_locking():
2691
 
            before_writing.set()
2692
 
            c1_save_without_locking_orig()
2693
 
            # The lock is held. We wait for the main thread to decide when to
2694
 
            # continue
2695
 
            after_writing.wait()
2696
 
        c1.store.save_without_locking = c1_save_without_locking
2697
 
        def c1_set():
2698
 
            c1.set('one', 'c1')
2699
 
            writing_done.set()
2700
 
        t1 = threading.Thread(target=c1_set)
2701
 
        # Collect the thread after the test
2702
 
        self.addCleanup(t1.join)
2703
 
        # Be ready to unblock the thread if the test goes wrong
2704
 
        self.addCleanup(after_writing.set)
2705
 
        t1.start()
2706
 
        before_writing.wait()
2707
 
        self.assertRaises(errors.LockContention,
2708
 
                          c2.set, 'one', 'c2')
2709
 
        self.assertEquals('c1', c1.get('one'))
2710
 
        # Let the lock be released
2711
 
        after_writing.set()
2712
 
        writing_done.wait()
2713
 
        c2.set('one', 'c2')
2714
 
        self.assertEquals('c2', c2.get('one'))
2715
 
 
2716
 
    def test_read_while_writing(self):
2717
 
       c1 = self.stack
2718
 
       # We spawn a thread that will pause *during* the write
2719
 
       ready_to_write = threading.Event()
2720
 
       do_writing = threading.Event()
2721
 
       writing_done = threading.Event()
2722
 
       # We override the _save implementation so we know the store is locked
2723
 
       c1_save_without_locking_orig = c1.store.save_without_locking
2724
 
       def c1_save_without_locking():
2725
 
           ready_to_write.set()
2726
 
           # The lock is held. We wait for the main thread to decide when to
2727
 
           # continue
2728
 
           do_writing.wait()
2729
 
           c1_save_without_locking_orig()
2730
 
           writing_done.set()
2731
 
       c1.store.save_without_locking = c1_save_without_locking
2732
 
       def c1_set():
2733
 
           c1.set('one', 'c1')
2734
 
       t1 = threading.Thread(target=c1_set)
2735
 
       # Collect the thread after the test
2736
 
       self.addCleanup(t1.join)
2737
 
       # Be ready to unblock the thread if the test goes wrong
2738
 
       self.addCleanup(do_writing.set)
2739
 
       t1.start()
2740
 
       # Ensure the thread is ready to write
2741
 
       ready_to_write.wait()
2742
 
       self.assertEquals('c1', c1.get('one'))
2743
 
       # If we read during the write, we get the old value
2744
 
       c2 = self.get_stack(self)
2745
 
       self.assertEquals('1', c2.get('one'))
2746
 
       # Let the writing occur and ensure it occurred
2747
 
       do_writing.set()
2748
 
       writing_done.wait()
2749
 
       # Now we get the updated value
2750
 
       c3 = self.get_stack(self)
2751
 
       self.assertEquals('c1', c3.get('one'))
2752
 
 
2753
 
    # FIXME: It may be worth looking into removing the lock dir when it's not
2754
 
    # needed anymore and look at possible fallouts for concurrent lockers. This
2755
 
    # will matter if/when we use config files outside of bazaar directories
2756
 
    # (.bazaar or .bzr) -- vila 20110-04-11
2757
 
 
2758
 
 
2759
 
class TestSectionMatcher(TestStore):
2760
 
 
2761
 
    scenarios = [('location', {'matcher': config.LocationMatcher})]
2762
 
 
2763
 
    def get_store(self, file_name):
2764
 
        return config.IniFileStore(self.get_readonly_transport(), file_name)
2765
 
 
2766
 
    def test_no_matches_for_empty_stores(self):
2767
 
        store = self.get_store('foo.conf')
2768
 
        store._load_from_string('')
2769
 
        matcher = self.matcher(store, '/bar')
2770
 
        self.assertEquals([], list(matcher.get_sections()))
2771
 
 
2772
 
    def test_build_doesnt_load_store(self):
2773
 
        store = self.get_store('foo.conf')
2774
 
        matcher = self.matcher(store, '/bar')
2775
 
        self.assertFalse(store.is_loaded())
2776
 
 
2777
 
 
2778
 
class TestLocationSection(tests.TestCase):
2779
 
 
2780
 
    def get_section(self, options, extra_path):
2781
 
        section = config.Section('foo', options)
2782
 
        # We don't care about the length so we use '0'
2783
 
        return config.LocationSection(section, 0, extra_path)
2784
 
 
2785
 
    def test_simple_option(self):
2786
 
        section = self.get_section({'foo': 'bar'}, '')
2787
 
        self.assertEquals('bar', section.get('foo'))
2788
 
 
2789
 
    def test_option_with_extra_path(self):
2790
 
        section = self.get_section({'foo': 'bar', 'foo:policy': 'appendpath'},
2791
 
                                   'baz')
2792
 
        self.assertEquals('bar/baz', section.get('foo'))
2793
 
 
2794
 
    def test_invalid_policy(self):
2795
 
        section = self.get_section({'foo': 'bar', 'foo:policy': 'die'},
2796
 
                                   'baz')
2797
 
        # invalid policies are ignored
2798
 
        self.assertEquals('bar', section.get('foo'))
2799
 
 
2800
 
 
2801
 
class TestLocationMatcher(TestStore):
2802
 
 
2803
 
    def get_store(self, file_name):
2804
 
        return config.IniFileStore(self.get_readonly_transport(), file_name)
2805
 
 
2806
 
    def test_more_specific_sections_first(self):
2807
 
        store = self.get_store('foo.conf')
2808
 
        store._load_from_string('''
2809
 
[/foo]
2810
 
section=/foo
2811
 
[/foo/bar]
2812
 
section=/foo/bar
2813
 
''')
2814
 
        self.assertEquals(['/foo', '/foo/bar'],
2815
 
                          [section.id for section in store.get_sections()])
2816
 
        matcher = config.LocationMatcher(store, '/foo/bar/baz')
2817
 
        sections = list(matcher.get_sections())
2818
 
        self.assertEquals([3, 2],
2819
 
                          [section.length for section in sections])
2820
 
        self.assertEquals(['/foo/bar', '/foo'],
2821
 
                          [section.id for section in sections])
2822
 
        self.assertEquals(['baz', 'bar/baz'],
2823
 
                          [section.extra_path for section in sections])
2824
 
 
2825
 
    def test_appendpath_in_no_name_section(self):
2826
 
        # It's a bit weird to allow appendpath in a no-name section, but
2827
 
        # someone may found a use for it
2828
 
        store = self.get_store('foo.conf')
2829
 
        store._load_from_string('''
2830
 
foo=bar
2831
 
foo:policy = appendpath
2832
 
''')
2833
 
        matcher = config.LocationMatcher(store, 'dir/subdir')
2834
 
        sections = list(matcher.get_sections())
2835
 
        self.assertLength(1, sections)
2836
 
        self.assertEquals('bar/dir/subdir', sections[0].get('foo'))
2837
 
 
2838
 
    def test_file_urls_are_normalized(self):
2839
 
        store = self.get_store('foo.conf')
2840
 
        if sys.platform == 'win32':
2841
 
            expected_url = 'file:///C:/dir/subdir'
2842
 
            expected_location = 'C:/dir/subdir'
2843
 
        else:
2844
 
            expected_url = 'file:///dir/subdir'
2845
 
            expected_location = '/dir/subdir'
2846
 
        matcher = config.LocationMatcher(store, expected_url)
2847
 
        self.assertEquals(expected_location, matcher.location)
2848
 
 
2849
 
 
2850
 
class TestStackGet(tests.TestCase):
2851
 
 
2852
 
    # FIXME: This should be parametrized for all known Stack or dedicated
2853
 
    # paramerized tests created to avoid bloating -- vila 2011-03-31
2854
 
 
2855
 
    def test_single_config_get(self):
2856
 
        conf = dict(foo='bar')
2857
 
        conf_stack = config.Stack([conf])
2858
 
        self.assertEquals('bar', conf_stack.get('foo'))
2859
 
 
2860
 
    def test_get_with_registered_default_value(self):
2861
 
        conf_stack = config.Stack([dict()])
2862
 
        opt = config.Option('foo', default='bar')
2863
 
        self.overrideAttr(config, 'option_registry', registry.Registry())
2864
 
        config.option_registry.register('foo', opt)
2865
 
        self.assertEquals('bar', conf_stack.get('foo'))
2866
 
 
2867
 
    def test_get_without_registered_default_value(self):
2868
 
        conf_stack = config.Stack([dict()])
2869
 
        opt = config.Option('foo')
2870
 
        self.overrideAttr(config, 'option_registry', registry.Registry())
2871
 
        config.option_registry.register('foo', opt)
2872
 
        self.assertEquals(None, conf_stack.get('foo'))
2873
 
 
2874
 
    def test_get_without_default_value_for_not_registered(self):
2875
 
        conf_stack = config.Stack([dict()])
2876
 
        opt = config.Option('foo')
2877
 
        self.overrideAttr(config, 'option_registry', registry.Registry())
2878
 
        self.assertEquals(None, conf_stack.get('foo'))
2879
 
 
2880
 
    def test_get_first_definition(self):
2881
 
        conf1 = dict(foo='bar')
2882
 
        conf2 = dict(foo='baz')
2883
 
        conf_stack = config.Stack([conf1, conf2])
2884
 
        self.assertEquals('bar', conf_stack.get('foo'))
2885
 
 
2886
 
    def test_get_embedded_definition(self):
2887
 
        conf1 = dict(yy='12')
2888
 
        conf2 = config.Stack([dict(xx='42'), dict(foo='baz')])
2889
 
        conf_stack = config.Stack([conf1, conf2])
2890
 
        self.assertEquals('baz', conf_stack.get('foo'))
2891
 
 
2892
 
    def test_get_for_empty_section_callable(self):
2893
 
        conf_stack = config.Stack([lambda : []])
2894
 
        self.assertEquals(None, conf_stack.get('foo'))
2895
 
 
2896
 
    def test_get_for_broken_callable(self):
2897
 
        # Trying to use and invalid callable raises an exception on first use
2898
 
        conf_stack = config.Stack([lambda : object()])
2899
 
        self.assertRaises(TypeError, conf_stack.get, 'foo')
2900
 
 
2901
 
 
2902
 
class TestStackWithTransport(tests.TestCaseWithTransport):
2903
 
 
2904
 
    scenarios = [(key, {'get_stack': builder}) for key, builder
2905
 
                 in config.test_stack_builder_registry.iteritems()]
2906
 
 
2907
 
 
2908
 
class TestConcreteStacks(TestStackWithTransport):
2909
 
 
2910
 
    def test_build_stack(self):
2911
 
        # Just a smoke test to help debug builders
2912
 
        stack = self.get_stack(self)
2913
 
 
2914
 
 
2915
 
class TestStackGet(TestStackWithTransport):
2916
 
 
2917
 
    def test_get_for_empty_stack(self):
2918
 
        conf = self.get_stack(self)
2919
 
        self.assertEquals(None, conf.get('foo'))
2920
 
 
2921
 
    def test_get_hook(self):
2922
 
        conf = self.get_stack(self)
2923
 
        conf.store._load_from_string('foo=bar')
2924
 
        calls = []
2925
 
        def hook(*args):
2926
 
            calls.append(args)
2927
 
        config.ConfigHooks.install_named_hook('get', hook, None)
2928
 
        self.assertLength(0, calls)
2929
 
        value = conf.get('foo')
2930
 
        self.assertEquals('bar', value)
2931
 
        self.assertLength(1, calls)
2932
 
        self.assertEquals((conf, 'foo', 'bar'), calls[0])
2933
 
 
2934
 
 
2935
 
class TestStackSet(TestStackWithTransport):
2936
 
 
2937
 
    def test_simple_set(self):
2938
 
        conf = self.get_stack(self)
2939
 
        conf.store._load_from_string('foo=bar')
2940
 
        self.assertEquals('bar', conf.get('foo'))
2941
 
        conf.set('foo', 'baz')
2942
 
        # Did we get it back ?
2943
 
        self.assertEquals('baz', conf.get('foo'))
2944
 
 
2945
 
    def test_set_creates_a_new_section(self):
2946
 
        conf = self.get_stack(self)
2947
 
        conf.set('foo', 'baz')
2948
 
        self.assertEquals, 'baz', conf.get('foo')
2949
 
 
2950
 
    def test_set_hook(self):
2951
 
        calls = []
2952
 
        def hook(*args):
2953
 
            calls.append(args)
2954
 
        config.ConfigHooks.install_named_hook('set', hook, None)
2955
 
        self.assertLength(0, calls)
2956
 
        conf = self.get_stack(self)
2957
 
        conf.set('foo', 'bar')
2958
 
        self.assertLength(1, calls)
2959
 
        self.assertEquals((conf, 'foo', 'bar'), calls[0])
2960
 
 
2961
 
 
2962
 
class TestStackRemove(TestStackWithTransport):
2963
 
 
2964
 
    def test_remove_existing(self):
2965
 
        conf = self.get_stack(self)
2966
 
        conf.store._load_from_string('foo=bar')
2967
 
        self.assertEquals('bar', conf.get('foo'))
2968
 
        conf.remove('foo')
2969
 
        # Did we get it back ?
2970
 
        self.assertEquals(None, conf.get('foo'))
2971
 
 
2972
 
    def test_remove_unknown(self):
2973
 
        conf = self.get_stack(self)
2974
 
        self.assertRaises(KeyError, conf.remove, 'I_do_not_exist')
2975
 
 
2976
 
    def test_remove_hook(self):
2977
 
        calls = []
2978
 
        def hook(*args):
2979
 
            calls.append(args)
2980
 
        config.ConfigHooks.install_named_hook('remove', hook, None)
2981
 
        self.assertLength(0, calls)
2982
 
        conf = self.get_stack(self)
2983
 
        conf.store._load_from_string('foo=bar')
2984
 
        conf.remove('foo')
2985
 
        self.assertLength(1, calls)
2986
 
        self.assertEquals((conf, 'foo'), calls[0])
2987
 
 
2988
 
 
2989
 
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
2990
 
 
2991
 
    def setUp(self):
2992
 
        super(TestConfigGetOptions, self).setUp()
2993
 
        create_configs(self)
2994
 
 
2995
 
    def test_no_variable(self):
2996
 
        # Using branch should query branch, locations and bazaar
2997
 
        self.assertOptions([], self.branch_config)
2998
 
 
2999
 
    def test_option_in_bazaar(self):
3000
 
        self.bazaar_config.set_user_option('file', 'bazaar')
3001
 
        self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
3002
 
                           self.bazaar_config)
3003
 
 
3004
 
    def test_option_in_locations(self):
3005
 
        self.locations_config.set_user_option('file', 'locations')
3006
 
        self.assertOptions(
3007
 
            [('file', 'locations', self.tree.basedir, 'locations')],
3008
 
            self.locations_config)
3009
 
 
3010
 
    def test_option_in_branch(self):
3011
 
        self.branch_config.set_user_option('file', 'branch')
3012
 
        self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
3013
 
                           self.branch_config)
3014
 
 
3015
 
    def test_option_in_bazaar_and_branch(self):
3016
 
        self.bazaar_config.set_user_option('file', 'bazaar')
3017
 
        self.branch_config.set_user_option('file', 'branch')
3018
 
        self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
3019
 
                            ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3020
 
                           self.branch_config)
3021
 
 
3022
 
    def test_option_in_branch_and_locations(self):
3023
 
        # Hmm, locations override branch :-/
3024
 
        self.locations_config.set_user_option('file', 'locations')
3025
 
        self.branch_config.set_user_option('file', 'branch')
3026
 
        self.assertOptions(
3027
 
            [('file', 'locations', self.tree.basedir, 'locations'),
3028
 
             ('file', 'branch', 'DEFAULT', 'branch'),],
3029
 
            self.branch_config)
3030
 
 
3031
 
    def test_option_in_bazaar_locations_and_branch(self):
3032
 
        self.bazaar_config.set_user_option('file', 'bazaar')
3033
 
        self.locations_config.set_user_option('file', 'locations')
3034
 
        self.branch_config.set_user_option('file', 'branch')
3035
 
        self.assertOptions(
3036
 
            [('file', 'locations', self.tree.basedir, 'locations'),
3037
 
             ('file', 'branch', 'DEFAULT', 'branch'),
3038
 
             ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3039
 
            self.branch_config)
3040
 
 
3041
 
 
3042
 
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
3043
 
 
3044
 
    def setUp(self):
3045
 
        super(TestConfigRemoveOption, self).setUp()
3046
 
        create_configs_with_file_option(self)
3047
 
 
3048
 
    def test_remove_in_locations(self):
3049
 
        self.locations_config.remove_user_option('file', self.tree.basedir)
3050
 
        self.assertOptions(
3051
 
            [('file', 'branch', 'DEFAULT', 'branch'),
3052
 
             ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3053
 
            self.branch_config)
3054
 
 
3055
 
    def test_remove_in_branch(self):
3056
 
        self.branch_config.remove_user_option('file')
3057
 
        self.assertOptions(
3058
 
            [('file', 'locations', self.tree.basedir, 'locations'),
3059
 
             ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3060
 
            self.branch_config)
3061
 
 
3062
 
    def test_remove_in_bazaar(self):
3063
 
        self.bazaar_config.remove_user_option('file')
3064
 
        self.assertOptions(
3065
 
            [('file', 'locations', self.tree.basedir, 'locations'),
3066
 
             ('file', 'branch', 'DEFAULT', 'branch'),],
3067
 
            self.branch_config)
3068
 
 
3069
 
 
3070
 
class TestConfigGetSections(tests.TestCaseWithTransport):
3071
 
 
3072
 
    def setUp(self):
3073
 
        super(TestConfigGetSections, self).setUp()
3074
 
        create_configs(self)
3075
 
 
3076
 
    def assertSectionNames(self, expected, conf, name=None):
3077
 
        """Check which sections are returned for a given config.
3078
 
 
3079
 
        If fallback configurations exist their sections can be included.
3080
 
 
3081
 
        :param expected: A list of section names.
3082
 
 
3083
 
        :param conf: The configuration that will be queried.
3084
 
 
3085
 
        :param name: An optional section name that will be passed to
3086
 
            get_sections().
3087
 
        """
3088
 
        sections = list(conf._get_sections(name))
3089
 
        self.assertLength(len(expected), sections)
3090
 
        self.assertEqual(expected, [name for name, _, _ in sections])
3091
 
 
3092
 
    def test_bazaar_default_section(self):
3093
 
        self.assertSectionNames(['DEFAULT'], self.bazaar_config)
3094
 
 
3095
 
    def test_locations_default_section(self):
3096
 
        # No sections are defined in an empty file
3097
 
        self.assertSectionNames([], self.locations_config)
3098
 
 
3099
 
    def test_locations_named_section(self):
3100
 
        self.locations_config.set_user_option('file', 'locations')
3101
 
        self.assertSectionNames([self.tree.basedir], self.locations_config)
3102
 
 
3103
 
    def test_locations_matching_sections(self):
3104
 
        loc_config = self.locations_config
3105
 
        loc_config.set_user_option('file', 'locations')
3106
 
        # We need to cheat a bit here to create an option in sections above and
3107
 
        # below the 'location' one.
3108
 
        parser = loc_config._get_parser()
3109
 
        # locations.cong deals with '/' ignoring native os.sep
3110
 
        location_names = self.tree.basedir.split('/')
3111
 
        parent = '/'.join(location_names[:-1])
3112
 
        child = '/'.join(location_names + ['child'])
3113
 
        parser[parent] = {}
3114
 
        parser[parent]['file'] = 'parent'
3115
 
        parser[child] = {}
3116
 
        parser[child]['file'] = 'child'
3117
 
        self.assertSectionNames([self.tree.basedir, parent], loc_config)
3118
 
 
3119
 
    def test_branch_data_default_section(self):
3120
 
        self.assertSectionNames([None],
3121
 
                                self.branch_config._get_branch_data_config())
3122
 
 
3123
 
    def test_branch_default_sections(self):
3124
 
        # No sections are defined in an empty locations file
3125
 
        self.assertSectionNames([None, 'DEFAULT'],
3126
 
                                self.branch_config)
3127
 
        # Unless we define an option
3128
 
        self.branch_config._get_location_config().set_user_option(
3129
 
            'file', 'locations')
3130
 
        self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
3131
 
                                self.branch_config)
3132
 
 
3133
 
    def test_bazaar_named_section(self):
3134
 
        # We need to cheat as the API doesn't give direct access to sections
3135
 
        # other than DEFAULT.
3136
 
        self.bazaar_config.set_alias('bazaar', 'bzr')
3137
 
        self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
3138
 
 
3139
 
 
3140
1315
class TestAuthenticationConfigFile(tests.TestCase):
3141
1316
    """Test the authentication.conf file matching"""
3142
1317
 
3157
1332
        self.assertEquals({}, conf._get_config())
3158
1333
        self._got_user_passwd(None, None, conf, 'http', 'foo.net')
3159
1334
 
3160
 
    def test_non_utf8_config(self):
3161
 
        conf = config.AuthenticationConfig(_file=StringIO(
3162
 
                'foo = bar\xff'))
3163
 
        self.assertRaises(errors.ConfigContentError, conf._get_config)
3164
 
        
3165
1335
    def test_missing_auth_section_header(self):
3166
1336
        conf = config.AuthenticationConfig(_file=StringIO('foo = bar'))
3167
1337
        self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
3425
1595
 
3426
1596
    def test_username_defaults_prompts(self):
3427
1597
        # HTTP prompts can't be tested here, see test_http.py
3428
 
        self._check_default_username_prompt(u'FTP %(host)s username: ', 'ftp')
3429
 
        self._check_default_username_prompt(
3430
 
            u'FTP %(host)s:%(port)d username: ', 'ftp', port=10020)
3431
 
        self._check_default_username_prompt(
3432
 
            u'SSH %(host)s:%(port)d username: ', 'ssh', port=12345)
 
1598
        self._check_default_username_prompt('FTP %(host)s username: ', 'ftp')
 
1599
        self._check_default_username_prompt(
 
1600
            'FTP %(host)s:%(port)d username: ', 'ftp', port=10020)
 
1601
        self._check_default_username_prompt(
 
1602
            'SSH %(host)s:%(port)d username: ', 'ssh', port=12345)
3433
1603
 
3434
1604
    def test_username_default_no_prompt(self):
3435
1605
        conf = config.AuthenticationConfig()
3441
1611
    def test_password_default_prompts(self):
3442
1612
        # HTTP prompts can't be tested here, see test_http.py
3443
1613
        self._check_default_password_prompt(
3444
 
            u'FTP %(user)s@%(host)s password: ', 'ftp')
3445
 
        self._check_default_password_prompt(
3446
 
            u'FTP %(user)s@%(host)s:%(port)d password: ', 'ftp', port=10020)
3447
 
        self._check_default_password_prompt(
3448
 
            u'SSH %(user)s@%(host)s:%(port)d password: ', 'ssh', port=12345)
 
1614
            'FTP %(user)s@%(host)s password: ', 'ftp')
 
1615
        self._check_default_password_prompt(
 
1616
            'FTP %(user)s@%(host)s:%(port)d password: ', 'ftp', port=10020)
 
1617
        self._check_default_password_prompt(
 
1618
            'SSH %(user)s@%(host)s:%(port)d password: ', 'ssh', port=12345)
3449
1619
        # SMTP port handling is a bit special (it's handled if embedded in the
3450
1620
        # host too)
3451
1621
        # FIXME: should we: forbid that, extend it to other schemes, leave
3452
1622
        # things as they are that's fine thank you ?
3453
 
        self._check_default_password_prompt(
3454
 
            u'SMTP %(user)s@%(host)s password: ', 'smtp')
3455
 
        self._check_default_password_prompt(
3456
 
            u'SMTP %(user)s@%(host)s password: ', 'smtp', host='bar.org:10025')
3457
 
        self._check_default_password_prompt(
3458
 
            u'SMTP %(user)s@%(host)s:%(port)d password: ', 'smtp', port=10025)
 
1623
        self._check_default_password_prompt('SMTP %(user)s@%(host)s password: ',
 
1624
                                            'smtp')
 
1625
        self._check_default_password_prompt('SMTP %(user)s@%(host)s password: ',
 
1626
                                            'smtp', host='bar.org:10025')
 
1627
        self._check_default_password_prompt(
 
1628
            'SMTP %(user)s@%(host)s:%(port)d password: ',
 
1629
            'smtp', port=10025)
3459
1630
 
3460
1631
    def test_ssh_password_emits_warning(self):
3461
1632
        conf = config.AuthenticationConfig(_file=StringIO(
3641
1812
# test_user_prompted ?
3642
1813
class TestAuthenticationRing(tests.TestCaseWithTransport):
3643
1814
    pass
3644
 
 
3645
 
 
3646
 
class TestAutoUserId(tests.TestCase):
3647
 
    """Test inferring an automatic user name."""
3648
 
 
3649
 
    def test_auto_user_id(self):
3650
 
        """Automatic inference of user name.
3651
 
        
3652
 
        This is a bit hard to test in an isolated way, because it depends on
3653
 
        system functions that go direct to /etc or perhaps somewhere else.
3654
 
        But it's reasonable to say that on Unix, with an /etc/mailname, we ought
3655
 
        to be able to choose a user name with no configuration.
3656
 
        """
3657
 
        if sys.platform == 'win32':
3658
 
            raise tests.TestSkipped(
3659
 
                "User name inference not implemented on win32")
3660
 
        realname, address = config._auto_user_id()
3661
 
        if os.path.exists('/etc/mailname'):
3662
 
            self.assertIsNot(None, realname)
3663
 
            self.assertIsNot(None, address)
3664
 
        else:
3665
 
            self.assertEquals((None, None), (realname, address))
3666