~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_config.py

  • Committer: John Arbash Meinel
  • Date: 2009-06-19 19:26:32 UTC
  • mto: This revision was merged to the branch mainline in revision 4466.
  • Revision ID: john@arbash-meinel.com-20090619192632-1a4ntoq61fkhlp2x
Make a note of the 'worst case' for heads.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 by Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2008 Canonical Ltd
2
2
#   Authors: Robert Collins <robert.collins@canonical.com>
3
3
#
4
4
# This program is free software; you can redistribute it and/or modify
13
13
#
14
14
# You should have received a copy of the GNU General Public License
15
15
# along with this program; if not, write to the Free Software
16
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
17
 
18
18
"""Tests for finding and reading the bzr config file[s]."""
19
19
# import system imports here
20
 
from bzrlib.util.configobj.configobj import ConfigObj, ConfigObjError
21
20
from cStringIO import StringIO
 
21
import getpass
22
22
import os
23
23
import sys
24
24
 
25
25
#import bzrlib specific imports here
26
26
from bzrlib import (
 
27
    branch,
 
28
    bzrdir,
27
29
    config,
28
30
    errors,
29
31
    osutils,
 
32
    mail_client,
 
33
    ui,
30
34
    urlutils,
 
35
    tests,
 
36
    trace,
 
37
    transport,
31
38
    )
32
 
from bzrlib.branch import Branch
33
 
from bzrlib.bzrdir import BzrDir
34
 
from bzrlib.tests import TestCase, TestCaseInTempDir, TestCaseWithTransport
 
39
from bzrlib.util.configobj import configobj
35
40
 
36
41
 
37
42
sample_long_alias="log -r-15..-1 --line"
38
 
sample_config_text = ("[DEFAULT]\n"
39
 
                      u"email=Erik B\u00e5gfors <erik@bagfors.nu>\n"
40
 
                      "editor=vim\n"
41
 
                      "gpg_signing_command=gnome-gpg\n"
42
 
                      "log_format=short\n"
43
 
                      "user_global_option=something\n"
44
 
                      "[ALIASES]\n"
45
 
                      "h=help\n"
46
 
                      "ll=" + sample_long_alias + "\n")
47
 
 
48
 
 
49
 
sample_always_signatures = ("[DEFAULT]\n"
50
 
                            "check_signatures=ignore\n"
51
 
                            "create_signatures=always")
52
 
 
53
 
 
54
 
sample_ignore_signatures = ("[DEFAULT]\n"
55
 
                            "check_signatures=require\n"
56
 
                            "create_signatures=never")
57
 
 
58
 
 
59
 
sample_maybe_signatures = ("[DEFAULT]\n"
60
 
                            "check_signatures=ignore\n"
61
 
                            "create_signatures=when-required")
62
 
 
63
 
 
64
 
sample_branches_text = ("[http://www.example.com]\n"
65
 
                        "# Top level policy\n"
66
 
                        "email=Robert Collins <robertc@example.org>\n"
67
 
                        "[http://www.example.com/useglobal]\n"
68
 
                        "# different project, forces global lookup\n"
69
 
                        "recurse=false\n"
70
 
                        "[/b/]\n"
71
 
                        "check_signatures=require\n"
72
 
                        "# test trailing / matching with no children\n"
73
 
                        "[/a/]\n"
74
 
                        "check_signatures=check-available\n"
75
 
                        "gpg_signing_command=false\n"
76
 
                        "user_local_option=local\n"
77
 
                        "# test trailing / matching\n"
78
 
                        "[/a/*]\n"
79
 
                        "#subdirs will match but not the parent\n"
80
 
                        "recurse=False\n"
81
 
                        "[/a/c]\n"
82
 
                        "check_signatures=ignore\n"
83
 
                        "post_commit=bzrlib.tests.test_config.post_commit\n"
84
 
                        "#testing explicit beats globs\n")
85
 
 
 
43
sample_config_text = u"""
 
44
[DEFAULT]
 
45
email=Erik B\u00e5gfors <erik@bagfors.nu>
 
46
editor=vim
 
47
gpg_signing_command=gnome-gpg
 
48
log_format=short
 
49
user_global_option=something
 
50
[ALIASES]
 
51
h=help
 
52
ll=""" + sample_long_alias + "\n"
 
53
 
 
54
 
 
55
sample_always_signatures = """
 
56
[DEFAULT]
 
57
check_signatures=ignore
 
58
create_signatures=always
 
59
"""
 
60
 
 
61
sample_ignore_signatures = """
 
62
[DEFAULT]
 
63
check_signatures=require
 
64
create_signatures=never
 
65
"""
 
66
 
 
67
sample_maybe_signatures = """
 
68
[DEFAULT]
 
69
check_signatures=ignore
 
70
create_signatures=when-required
 
71
"""
 
72
 
 
73
sample_branches_text = """
 
74
[http://www.example.com]
 
75
# Top level policy
 
76
email=Robert Collins <robertc@example.org>
 
77
normal_option = normal
 
78
appendpath_option = append
 
79
appendpath_option:policy = appendpath
 
80
norecurse_option = norecurse
 
81
norecurse_option:policy = norecurse
 
82
[http://www.example.com/ignoreparent]
 
83
# different project: ignore parent dir config
 
84
ignore_parents=true
 
85
[http://www.example.com/norecurse]
 
86
# configuration items that only apply to this dir
 
87
recurse=false
 
88
normal_option = norecurse
 
89
[http://www.example.com/dir]
 
90
appendpath_option = normal
 
91
[/b/]
 
92
check_signatures=require
 
93
# test trailing / matching with no children
 
94
[/a/]
 
95
check_signatures=check-available
 
96
gpg_signing_command=false
 
97
user_local_option=local
 
98
# test trailing / matching
 
99
[/a/*]
 
100
#subdirs will match but not the parent
 
101
[/a/c]
 
102
check_signatures=ignore
 
103
post_commit=bzrlib.tests.test_config.post_commit
 
104
#testing explicit beats globs
 
105
"""
86
106
 
87
107
 
88
108
class InstrumentedConfigObj(object):
102
122
    def __setitem__(self, key, value):
103
123
        self._calls.append(('__setitem__', key, value))
104
124
 
 
125
    def __delitem__(self, key):
 
126
        self._calls.append(('__delitem__', key))
 
127
 
 
128
    def keys(self):
 
129
        self._calls.append(('keys',))
 
130
        return []
 
131
 
105
132
    def write(self, arg):
106
133
        self._calls.append(('write',))
107
134
 
 
135
    def as_bool(self, value):
 
136
        self._calls.append(('as_bool', value))
 
137
        return False
 
138
 
 
139
    def get_value(self, section, name):
 
140
        self._calls.append(('get_value', section, name))
 
141
        return None
 
142
 
108
143
 
109
144
class FakeBranch(object):
110
145
 
113
148
            self.base = "http://example.com/branches/demo"
114
149
        else:
115
150
            self.base = base
116
 
        self.control_files = FakeControlFiles(user_id=user_id)
 
151
        self._transport = self.control_files = \
 
152
            FakeControlFilesAndTransport(user_id=user_id)
 
153
 
 
154
    def _get_config(self):
 
155
        return config.TransportConfig(self._transport, 'branch.conf')
117
156
 
118
157
    def lock_write(self):
119
158
        pass
122
161
        pass
123
162
 
124
163
 
125
 
class FakeControlFiles(object):
 
164
class FakeControlFilesAndTransport(object):
126
165
 
127
166
    def __init__(self, user_id=None):
128
 
        self.email = user_id
129
167
        self.files = {}
 
168
        if user_id:
 
169
            self.files['email'] = user_id
 
170
        self._transport = self
130
171
 
131
172
    def get_utf8(self, filename):
132
 
        if filename != 'email':
133
 
            raise NotImplementedError
134
 
        if self.email is not None:
135
 
            return StringIO(self.email)
136
 
        raise errors.NoSuchFile(filename)
 
173
        # from LockableFiles
 
174
        raise AssertionError("get_utf8 should no longer be used")
137
175
 
138
176
    def get(self, filename):
 
177
        # from Transport
139
178
        try:
140
179
            return StringIO(self.files[filename])
141
180
        except KeyError:
142
181
            raise errors.NoSuchFile(filename)
143
182
 
 
183
    def get_bytes(self, filename):
 
184
        # from Transport
 
185
        try:
 
186
            return self.files[filename]
 
187
        except KeyError:
 
188
            raise errors.NoSuchFile(filename)
 
189
 
144
190
    def put(self, filename, fileobj):
145
191
        self.files[filename] = fileobj.read()
146
192
 
 
193
    def put_file(self, filename, fileobj):
 
194
        return self.put(filename, fileobj)
 
195
 
147
196
 
148
197
class InstrumentedConfig(config.Config):
149
198
    """An instrumented config that supplies stubs for template methods."""
150
 
    
 
199
 
151
200
    def __init__(self):
152
201
        super(InstrumentedConfig, self).__init__()
153
202
        self._calls = []
169
218
active = True
170
219
nonactive = False
171
220
"""
172
 
class TestConfigObj(TestCase):
 
221
 
 
222
 
 
223
class TestConfigObj(tests.TestCase):
 
224
 
173
225
    def test_get_bool(self):
174
 
        from bzrlib.config import ConfigObj
175
 
        co = ConfigObj(StringIO(bool_config))
 
226
        co = config.ConfigObj(StringIO(bool_config))
176
227
        self.assertIs(co.get_bool('DEFAULT', 'active'), True)
177
228
        self.assertIs(co.get_bool('DEFAULT', 'inactive'), False)
178
229
        self.assertIs(co.get_bool('UPPERCASE', 'active'), True)
179
230
        self.assertIs(co.get_bool('UPPERCASE', 'nonactive'), False)
180
231
 
181
 
 
182
 
class TestConfig(TestCase):
 
232
    def test_hash_sign_in_value(self):
 
233
        """
 
234
        Before 4.5.0, ConfigObj did not quote # signs in values, so they'd be
 
235
        treated as comments when read in again. (#86838)
 
236
        """
 
237
        co = config.ConfigObj()
 
238
        co['test'] = 'foo#bar'
 
239
        lines = co.write()
 
240
        self.assertEqual(lines, ['test = "foo#bar"'])
 
241
        co2 = config.ConfigObj(lines)
 
242
        self.assertEqual(co2['test'], 'foo#bar')
 
243
 
 
244
 
 
245
erroneous_config = """[section] # line 1
 
246
good=good # line 2
 
247
[section] # line 3
 
248
whocares=notme # line 4
 
249
"""
 
250
 
 
251
 
 
252
class TestConfigObjErrors(tests.TestCase):
 
253
 
 
254
    def test_duplicate_section_name_error_line(self):
 
255
        try:
 
256
            co = configobj.ConfigObj(StringIO(erroneous_config),
 
257
                                     raise_errors=True)
 
258
        except config.configobj.DuplicateError, e:
 
259
            self.assertEqual(3, e.line_number)
 
260
        else:
 
261
            self.fail('Error in config file not detected')
 
262
 
 
263
 
 
264
class TestConfig(tests.TestCase):
183
265
 
184
266
    def test_constructs(self):
185
267
        config.Config()
186
 
 
 
268
 
187
269
    def test_no_default_editor(self):
188
270
        self.assertRaises(NotImplementedError, config.Config().get_editor)
189
271
 
235
317
        self.assertEqual('long', my_config.log_format())
236
318
 
237
319
 
238
 
class TestConfigPath(TestCase):
 
320
class TestConfigPath(tests.TestCase):
239
321
 
240
322
    def setUp(self):
241
323
        super(TestConfigPath, self).setUp()
242
 
        self.old_home = os.environ.get('HOME', None)
243
 
        self.old_appdata = os.environ.get('APPDATA', None)
244
324
        os.environ['HOME'] = '/home/bogus'
245
 
        os.environ['APPDATA'] = \
246
 
            r'C:\Documents and Settings\bogus\Application Data'
 
325
        if sys.platform == 'win32':
 
326
            os.environ['BZR_HOME'] = \
 
327
                r'C:\Documents and Settings\bogus\Application Data'
 
328
            self.bzr_home = \
 
329
                'C:/Documents and Settings/bogus/Application Data/bazaar/2.0'
 
330
        else:
 
331
            self.bzr_home = '/home/bogus/.bazaar'
247
332
 
248
 
    def tearDown(self):
249
 
        if self.old_home is None:
250
 
            del os.environ['HOME']
251
 
        else:
252
 
            os.environ['HOME'] = self.old_home
253
 
        if self.old_appdata is None:
254
 
            del os.environ['APPDATA']
255
 
        else:
256
 
            os.environ['APPDATA'] = self.old_appdata
257
 
        super(TestConfigPath, self).tearDown()
258
 
    
259
333
    def test_config_dir(self):
260
 
        if sys.platform == 'win32':
261
 
            self.assertEqual(config.config_dir(), 
262
 
                'C:/Documents and Settings/bogus/Application Data/bazaar/2.0')
263
 
        else:
264
 
            self.assertEqual(config.config_dir(), '/home/bogus/.bazaar')
 
334
        self.assertEqual(config.config_dir(), self.bzr_home)
265
335
 
266
336
    def test_config_filename(self):
267
 
        if sys.platform == 'win32':
268
 
            self.assertEqual(config.config_filename(), 
269
 
                'C:/Documents and Settings/bogus/Application Data/bazaar/2.0/bazaar.conf')
270
 
        else:
271
 
            self.assertEqual(config.config_filename(),
272
 
                             '/home/bogus/.bazaar/bazaar.conf')
 
337
        self.assertEqual(config.config_filename(),
 
338
                         self.bzr_home + '/bazaar.conf')
273
339
 
274
340
    def test_branches_config_filename(self):
275
 
        if sys.platform == 'win32':
276
 
            self.assertEqual(config.branches_config_filename(), 
277
 
                'C:/Documents and Settings/bogus/Application Data/bazaar/2.0/branches.conf')
278
 
        else:
279
 
            self.assertEqual(config.branches_config_filename(),
280
 
                             '/home/bogus/.bazaar/branches.conf')
 
341
        self.assertEqual(config.branches_config_filename(),
 
342
                         self.bzr_home + '/branches.conf')
281
343
 
282
344
    def test_locations_config_filename(self):
283
 
        if sys.platform == 'win32':
284
 
            self.assertEqual(config.locations_config_filename(), 
285
 
                'C:/Documents and Settings/bogus/Application Data/bazaar/2.0/locations.conf')
286
 
        else:
287
 
            self.assertEqual(config.locations_config_filename(),
288
 
                             '/home/bogus/.bazaar/locations.conf')
289
 
 
290
 
class TestIniConfig(TestCase):
 
345
        self.assertEqual(config.locations_config_filename(),
 
346
                         self.bzr_home + '/locations.conf')
 
347
 
 
348
    def test_authentication_config_filename(self):
 
349
        self.assertEqual(config.authentication_config_filename(),
 
350
                         self.bzr_home + '/authentication.conf')
 
351
 
 
352
 
 
353
class TestIniConfig(tests.TestCase):
291
354
 
292
355
    def test_contructs(self):
293
356
        my_config = config.IniBasedConfig("nothing")
297
360
        my_config = config.IniBasedConfig(None)
298
361
        self.failUnless(
299
362
            isinstance(my_config._get_parser(file=config_file),
300
 
                        ConfigObj))
 
363
                        configobj.ConfigObj))
301
364
 
302
365
    def test_cached(self):
303
366
        config_file = StringIO(sample_config_text.encode('utf-8'))
306
369
        self.failUnless(my_config._get_parser() is parser)
307
370
 
308
371
 
309
 
class TestGetConfig(TestCase):
 
372
class TestGetConfig(tests.TestCase):
310
373
 
311
374
    def test_constructs(self):
312
375
        my_config = config.GlobalConfig()
313
376
 
314
377
    def test_calls_read_filenames(self):
315
 
        # replace the class that is constructured, to check its parameters
 
378
        # replace the class that is constructed, to check its parameters
316
379
        oldparserclass = config.ConfigObj
317
380
        config.ConfigObj = InstrumentedConfigObj
318
381
        my_config = config.GlobalConfig()
325
388
                                          'utf-8')])
326
389
 
327
390
 
328
 
class TestBranchConfig(TestCaseWithTransport):
 
391
class TestBranchConfig(tests.TestCaseWithTransport):
329
392
 
330
393
    def test_constructs(self):
331
394
        branch = FakeBranch()
341
404
 
342
405
    def test_get_config(self):
343
406
        """The Branch.get_config method works properly"""
344
 
        b = BzrDir.create_standalone_workingtree('.').branch
 
407
        b = bzrdir.BzrDir.create_standalone_workingtree('.').branch
345
408
        my_config = b.get_config()
346
409
        self.assertIs(my_config.get_user_option('wacky'), None)
347
410
        my_config.set_user_option('wacky', 'unlikely')
348
411
        self.assertEqual(my_config.get_user_option('wacky'), 'unlikely')
349
412
 
350
413
        # Ensure we get the same thing if we start again
351
 
        b2 = Branch.open('.')
 
414
        b2 = branch.Branch.open('.')
352
415
        my_config2 = b2.get_config()
353
416
        self.assertEqual(my_config2.get_user_option('wacky'), 'unlikely')
354
417
 
366
429
        locations = config.locations_config_filename()
367
430
        config.ensure_config_dir_exists()
368
431
        local_url = urlutils.local_path_to_url('branch')
369
 
        open(locations, 'wb').write('[%s]\nnickname = foobar' 
 
432
        open(locations, 'wb').write('[%s]\nnickname = foobar'
370
433
                                    % (local_url,))
371
434
        self.assertEqual('foobar', branch.nick)
372
435
 
377
440
 
378
441
        locations = config.locations_config_filename()
379
442
        config.ensure_config_dir_exists()
380
 
        open(locations, 'wb').write('[%s/branch]\nnickname = barry' 
 
443
        open(locations, 'wb').write('[%s/branch]\nnickname = barry'
381
444
                                    % (osutils.getcwd().encode('utf8'),))
382
445
        self.assertEqual('barry', branch.nick)
383
446
 
384
447
    def test_config_creates_local(self):
385
448
        """Creating a new entry in config uses a local path."""
386
 
        branch = self.make_branch('branch')
 
449
        branch = self.make_branch('branch', format='knit')
387
450
        branch.set_push_location('http://foobar')
388
451
        locations = config.locations_config_filename()
389
452
        local_path = osutils.getcwd().encode('utf8')
390
453
        # Surprisingly ConfigObj doesn't create a trailing newline
391
454
        self.check_file_contents(locations,
392
 
            '[%s/branch]\npush_location = http://foobar' % (local_path,))
393
 
 
394
 
 
395
 
class TestGlobalConfigItems(TestCase):
 
455
                                 '[%s/branch]\n'
 
456
                                 'push_location = http://foobar\n'
 
457
                                 'push_location:policy = norecurse\n'
 
458
                                 % (local_path,))
 
459
 
 
460
    def test_autonick_urlencoded(self):
 
461
        b = self.make_branch('!repo')
 
462
        self.assertEqual('!repo', b.get_config().get_nickname())
 
463
 
 
464
    def test_warn_if_masked(self):
 
465
        _warning = trace.warning
 
466
        warnings = []
 
467
        def warning(*args):
 
468
            warnings.append(args[0] % args[1:])
 
469
 
 
470
        def set_option(store, warn_masked=True):
 
471
            warnings[:] = []
 
472
            conf.set_user_option('example_option', repr(store), store=store,
 
473
                                 warn_masked=warn_masked)
 
474
        def assertWarning(warning):
 
475
            if warning is None:
 
476
                self.assertEqual(0, len(warnings))
 
477
            else:
 
478
                self.assertEqual(1, len(warnings))
 
479
                self.assertEqual(warning, warnings[0])
 
480
        trace.warning = warning
 
481
        try:
 
482
            branch = self.make_branch('.')
 
483
            conf = branch.get_config()
 
484
            set_option(config.STORE_GLOBAL)
 
485
            assertWarning(None)
 
486
            set_option(config.STORE_BRANCH)
 
487
            assertWarning(None)
 
488
            set_option(config.STORE_GLOBAL)
 
489
            assertWarning('Value "4" is masked by "3" from branch.conf')
 
490
            set_option(config.STORE_GLOBAL, warn_masked=False)
 
491
            assertWarning(None)
 
492
            set_option(config.STORE_LOCATION)
 
493
            assertWarning(None)
 
494
            set_option(config.STORE_BRANCH)
 
495
            assertWarning('Value "3" is masked by "0" from locations.conf')
 
496
            set_option(config.STORE_BRANCH, warn_masked=False)
 
497
            assertWarning(None)
 
498
        finally:
 
499
            trace.warning = _warning
 
500
 
 
501
 
 
502
class TestGlobalConfigItems(tests.TestCase):
396
503
 
397
504
    def test_user_id(self):
398
505
        config_file = StringIO(sample_config_text.encode('utf-8'))
472
579
        my_config = self._get_sample_config()
473
580
        self.assertEqual("something",
474
581
                         my_config.get_user_option('user_global_option'))
475
 
        
 
582
 
476
583
    def test_post_commit_default(self):
477
584
        my_config = self._get_sample_config()
478
585
        self.assertEqual(None, my_config.post_commit())
485
592
        my_config = self._get_sample_config()
486
593
        self.assertEqual('help', my_config.get_alias('h'))
487
594
 
 
595
    def test_get_aliases(self):
 
596
        my_config = self._get_sample_config()
 
597
        aliases = my_config.get_aliases()
 
598
        self.assertEqual(2, len(aliases))
 
599
        sorted_keys = sorted(aliases)
 
600
        self.assertEqual('help', aliases[sorted_keys[0]])
 
601
        self.assertEqual(sample_long_alias, aliases[sorted_keys[1]])
 
602
 
488
603
    def test_get_no_alias(self):
489
604
        my_config = self._get_sample_config()
490
605
        self.assertEqual(None, my_config.get_alias('foo'))
494
609
        self.assertEqual(sample_long_alias, my_config.get_alias('ll'))
495
610
 
496
611
 
497
 
class TestLocationConfig(TestCaseInTempDir):
 
612
class TestGlobalConfigSavingOptions(tests.TestCaseInTempDir):
 
613
 
 
614
    def test_empty(self):
 
615
        my_config = config.GlobalConfig()
 
616
        self.assertEqual(0, len(my_config.get_aliases()))
 
617
 
 
618
    def test_set_alias(self):
 
619
        my_config = config.GlobalConfig()
 
620
        alias_value = 'commit --strict'
 
621
        my_config.set_alias('commit', alias_value)
 
622
        new_config = config.GlobalConfig()
 
623
        self.assertEqual(alias_value, new_config.get_alias('commit'))
 
624
 
 
625
    def test_remove_alias(self):
 
626
        my_config = config.GlobalConfig()
 
627
        my_config.set_alias('commit', 'commit --strict')
 
628
        # Now remove the alias again.
 
629
        my_config.unset_alias('commit')
 
630
        new_config = config.GlobalConfig()
 
631
        self.assertIs(None, new_config.get_alias('commit'))
 
632
 
 
633
 
 
634
class TestLocationConfig(tests.TestCaseInTempDir):
498
635
 
499
636
    def test_constructs(self):
500
637
        my_config = config.LocationConfig('http://example.com')
504
641
        # This is testing the correct file names are provided.
505
642
        # TODO: consolidate with the test for GlobalConfigs filename checks.
506
643
        #
507
 
        # replace the class that is constructured, to check its parameters
 
644
        # replace the class that is constructed, to check its parameters
508
645
        oldparserclass = config.ConfigObj
509
646
        config.ConfigObj = InstrumentedConfigObj
510
647
        try:
535
672
        self.failUnless(isinstance(global_config, config.GlobalConfig))
536
673
        self.failUnless(global_config is my_config._get_global_config())
537
674
 
538
 
    def test__get_section_no_match(self):
 
675
    def test__get_matching_sections_no_match(self):
539
676
        self.get_branch_config('/')
540
 
        self.assertEqual(None, self.my_location_config._get_section())
541
 
        
542
 
    def test__get_section_exact(self):
 
677
        self.assertEqual([], self.my_location_config._get_matching_sections())
 
678
 
 
679
    def test__get_matching_sections_exact(self):
543
680
        self.get_branch_config('http://www.example.com')
544
 
        self.assertEqual('http://www.example.com',
545
 
                         self.my_location_config._get_section())
546
 
   
547
 
    def test__get_section_suffix_does_not(self):
 
681
        self.assertEqual([('http://www.example.com', '')],
 
682
                         self.my_location_config._get_matching_sections())
 
683
 
 
684
    def test__get_matching_sections_suffix_does_not(self):
548
685
        self.get_branch_config('http://www.example.com-com')
549
 
        self.assertEqual(None, self.my_location_config._get_section())
 
686
        self.assertEqual([], self.my_location_config._get_matching_sections())
550
687
 
551
 
    def test__get_section_subdir_recursive(self):
 
688
    def test__get_matching_sections_subdir_recursive(self):
552
689
        self.get_branch_config('http://www.example.com/com')
553
 
        self.assertEqual('http://www.example.com',
554
 
                         self.my_location_config._get_section())
555
 
 
556
 
    def test__get_section_subdir_matches(self):
557
 
        self.get_branch_config('http://www.example.com/useglobal')
558
 
        self.assertEqual('http://www.example.com/useglobal',
559
 
                         self.my_location_config._get_section())
560
 
 
561
 
    def test__get_section_subdir_nonrecursive(self):
 
690
        self.assertEqual([('http://www.example.com', 'com')],
 
691
                         self.my_location_config._get_matching_sections())
 
692
 
 
693
    def test__get_matching_sections_ignoreparent(self):
 
694
        self.get_branch_config('http://www.example.com/ignoreparent')
 
695
        self.assertEqual([('http://www.example.com/ignoreparent', '')],
 
696
                         self.my_location_config._get_matching_sections())
 
697
 
 
698
    def test__get_matching_sections_ignoreparent_subdir(self):
562
699
        self.get_branch_config(
563
 
            'http://www.example.com/useglobal/childbranch')
564
 
        self.assertEqual('http://www.example.com',
565
 
                         self.my_location_config._get_section())
 
700
            'http://www.example.com/ignoreparent/childbranch')
 
701
        self.assertEqual([('http://www.example.com/ignoreparent',
 
702
                           'childbranch')],
 
703
                         self.my_location_config._get_matching_sections())
566
704
 
567
 
    def test__get_section_subdir_trailing_slash(self):
 
705
    def test__get_matching_sections_subdir_trailing_slash(self):
568
706
        self.get_branch_config('/b')
569
 
        self.assertEqual('/b/', self.my_location_config._get_section())
 
707
        self.assertEqual([('/b/', '')],
 
708
                         self.my_location_config._get_matching_sections())
570
709
 
571
 
    def test__get_section_subdir_child(self):
 
710
    def test__get_matching_sections_subdir_child(self):
572
711
        self.get_branch_config('/a/foo')
573
 
        self.assertEqual('/a/*', self.my_location_config._get_section())
 
712
        self.assertEqual([('/a/*', ''), ('/a/', 'foo')],
 
713
                         self.my_location_config._get_matching_sections())
574
714
 
575
 
    def test__get_section_subdir_child_child(self):
 
715
    def test__get_matching_sections_subdir_child_child(self):
576
716
        self.get_branch_config('/a/foo/bar')
577
 
        self.assertEqual('/a/', self.my_location_config._get_section())
 
717
        self.assertEqual([('/a/*', 'bar'), ('/a/', 'foo/bar')],
 
718
                         self.my_location_config._get_matching_sections())
578
719
 
579
 
    def test__get_section_trailing_slash_with_children(self):
 
720
    def test__get_matching_sections_trailing_slash_with_children(self):
580
721
        self.get_branch_config('/a/')
581
 
        self.assertEqual('/a/', self.my_location_config._get_section())
 
722
        self.assertEqual([('/a/', '')],
 
723
                         self.my_location_config._get_matching_sections())
582
724
 
583
 
    def test__get_section_explicit_over_glob(self):
 
725
    def test__get_matching_sections_explicit_over_glob(self):
 
726
        # XXX: 2006-09-08 jamesh
 
727
        # This test only passes because ord('c') > ord('*').  If there
 
728
        # was a config section for '/a/?', it would get precedence
 
729
        # over '/a/c'.
584
730
        self.get_branch_config('/a/c')
585
 
        self.assertEqual('/a/c', self.my_location_config._get_section())
586
 
 
 
731
        self.assertEqual([('/a/c', ''), ('/a/*', ''), ('/a/', 'c')],
 
732
                         self.my_location_config._get_matching_sections())
 
733
 
 
734
    def test__get_option_policy_normal(self):
 
735
        self.get_branch_config('http://www.example.com')
 
736
        self.assertEqual(
 
737
            self.my_location_config._get_config_policy(
 
738
            'http://www.example.com', 'normal_option'),
 
739
            config.POLICY_NONE)
 
740
 
 
741
    def test__get_option_policy_norecurse(self):
 
742
        self.get_branch_config('http://www.example.com')
 
743
        self.assertEqual(
 
744
            self.my_location_config._get_option_policy(
 
745
            'http://www.example.com', 'norecurse_option'),
 
746
            config.POLICY_NORECURSE)
 
747
        # Test old recurse=False setting:
 
748
        self.assertEqual(
 
749
            self.my_location_config._get_option_policy(
 
750
            'http://www.example.com/norecurse', 'normal_option'),
 
751
            config.POLICY_NORECURSE)
 
752
 
 
753
    def test__get_option_policy_normal(self):
 
754
        self.get_branch_config('http://www.example.com')
 
755
        self.assertEqual(
 
756
            self.my_location_config._get_option_policy(
 
757
            'http://www.example.com', 'appendpath_option'),
 
758
            config.POLICY_APPENDPATH)
587
759
 
588
760
    def test_location_without_username(self):
589
 
        self.get_branch_config('http://www.example.com/useglobal')
 
761
        self.get_branch_config('http://www.example.com/ignoreparent')
590
762
        self.assertEqual(u'Erik B\u00e5gfors <erik@bagfors.nu>',
591
763
                         self.my_config.username())
592
764
 
613
785
        self.get_branch_config('/a/c')
614
786
        self.assertEqual(config.CHECK_NEVER,
615
787
                         self.my_config.signature_checking())
616
 
        
 
788
 
617
789
    def test_signatures_when_available(self):
618
790
        self.get_branch_config('/a/', global_config=sample_ignore_signatures)
619
791
        self.assertEqual(config.CHECK_IF_POSSIBLE,
620
792
                         self.my_config.signature_checking())
621
 
        
 
793
 
622
794
    def test_signatures_always(self):
623
795
        self.get_branch_config('/b')
624
796
        self.assertEqual(config.CHECK_ALWAYS,
625
797
                         self.my_config.signature_checking())
626
 
        
 
798
 
627
799
    def test_gpg_signing_command(self):
628
800
        self.get_branch_config('/b')
629
801
        self.assertEqual("gnome-gpg", self.my_config.gpg_signing_command())
641
813
        self.get_branch_config('/a')
642
814
        self.assertEqual('local',
643
815
                         self.my_config.get_user_option('user_local_option'))
644
 
        
 
816
 
 
817
    def test_get_user_option_appendpath(self):
 
818
        # returned as is for the base path:
 
819
        self.get_branch_config('http://www.example.com')
 
820
        self.assertEqual('append',
 
821
                         self.my_config.get_user_option('appendpath_option'))
 
822
        # Extra path components get appended:
 
823
        self.get_branch_config('http://www.example.com/a/b/c')
 
824
        self.assertEqual('append/a/b/c',
 
825
                         self.my_config.get_user_option('appendpath_option'))
 
826
        # Overriden for http://www.example.com/dir, where it is a
 
827
        # normal option:
 
828
        self.get_branch_config('http://www.example.com/dir/a/b/c')
 
829
        self.assertEqual('normal',
 
830
                         self.my_config.get_user_option('appendpath_option'))
 
831
 
 
832
    def test_get_user_option_norecurse(self):
 
833
        self.get_branch_config('http://www.example.com')
 
834
        self.assertEqual('norecurse',
 
835
                         self.my_config.get_user_option('norecurse_option'))
 
836
        self.get_branch_config('http://www.example.com/dir')
 
837
        self.assertEqual(None,
 
838
                         self.my_config.get_user_option('norecurse_option'))
 
839
        # http://www.example.com/norecurse is a recurse=False section
 
840
        # that redefines normal_option.  Subdirectories do not pick up
 
841
        # this redefinition.
 
842
        self.get_branch_config('http://www.example.com/norecurse')
 
843
        self.assertEqual('norecurse',
 
844
                         self.my_config.get_user_option('normal_option'))
 
845
        self.get_branch_config('http://www.example.com/norecurse/subdir')
 
846
        self.assertEqual('normal',
 
847
                         self.my_config.get_user_option('normal_option'))
 
848
 
 
849
    def test_set_user_option_norecurse(self):
 
850
        self.get_branch_config('http://www.example.com')
 
851
        self.my_config.set_user_option('foo', 'bar',
 
852
                                       store=config.STORE_LOCATION_NORECURSE)
 
853
        self.assertEqual(
 
854
            self.my_location_config._get_option_policy(
 
855
            'http://www.example.com', 'foo'),
 
856
            config.POLICY_NORECURSE)
 
857
 
 
858
    def test_set_user_option_appendpath(self):
 
859
        self.get_branch_config('http://www.example.com')
 
860
        self.my_config.set_user_option('foo', 'bar',
 
861
                                       store=config.STORE_LOCATION_APPENDPATH)
 
862
        self.assertEqual(
 
863
            self.my_location_config._get_option_policy(
 
864
            'http://www.example.com', 'foo'),
 
865
            config.POLICY_APPENDPATH)
 
866
 
 
867
    def test_set_user_option_change_policy(self):
 
868
        self.get_branch_config('http://www.example.com')
 
869
        self.my_config.set_user_option('norecurse_option', 'normal',
 
870
                                       store=config.STORE_LOCATION)
 
871
        self.assertEqual(
 
872
            self.my_location_config._get_option_policy(
 
873
            'http://www.example.com', 'norecurse_option'),
 
874
            config.POLICY_NONE)
 
875
 
 
876
    def test_set_user_option_recurse_false_section(self):
 
877
        # The following section has recurse=False set.  The test is to
 
878
        # make sure that a normal option can be added to the section,
 
879
        # converting recurse=False to the norecurse policy.
 
880
        self.get_branch_config('http://www.example.com/norecurse')
 
881
        self.callDeprecated(['The recurse option is deprecated as of 0.14.  '
 
882
                             'The section "http://www.example.com/norecurse" '
 
883
                             'has been converted to use policies.'],
 
884
                            self.my_config.set_user_option,
 
885
                            'foo', 'bar', store=config.STORE_LOCATION)
 
886
        self.assertEqual(
 
887
            self.my_location_config._get_option_policy(
 
888
            'http://www.example.com/norecurse', 'foo'),
 
889
            config.POLICY_NONE)
 
890
        # The previously existing option is still norecurse:
 
891
        self.assertEqual(
 
892
            self.my_location_config._get_option_policy(
 
893
            'http://www.example.com/norecurse', 'normal_option'),
 
894
            config.POLICY_NORECURSE)
 
895
 
645
896
    def test_post_commit_default(self):
646
897
        self.get_branch_config('/a/c')
647
898
        self.assertEqual('bzrlib.tests.test_config.post_commit',
674
925
 
675
926
        os.mkdir = checked_mkdir
676
927
        try:
677
 
            self.my_config.set_user_option('foo', 'bar', local=True)
 
928
            self.callDeprecated(['The recurse option is deprecated as of '
 
929
                                 '0.14.  The section "/a/c" has been '
 
930
                                 'converted to use policies.'],
 
931
                                self.my_config.set_user_option,
 
932
                                'foo', 'bar', store=config.STORE_LOCATION)
678
933
        finally:
679
934
            os.mkdir = real_mkdir
680
935
 
684
939
                          ('__setitem__', '/a/c', {}),
685
940
                          ('__getitem__', '/a/c'),
686
941
                          ('__setitem__', 'foo', 'bar'),
 
942
                          ('__getitem__', '/a/c'),
 
943
                          ('as_bool', 'recurse'),
 
944
                          ('__getitem__', '/a/c'),
 
945
                          ('__delitem__', 'recurse'),
 
946
                          ('__getitem__', '/a/c'),
 
947
                          ('keys',),
 
948
                          ('__getitem__', '/a/c'),
 
949
                          ('__contains__', 'foo:policy'),
687
950
                          ('write',)],
688
951
                         record._calls[1:])
689
952
 
692
955
        self.assertIs(self.my_config.get_user_option('foo'), None)
693
956
        self.my_config.set_user_option('foo', 'bar')
694
957
        self.assertEqual(
695
 
            self.my_config.branch.control_files.files['branch.conf'], 
 
958
            self.my_config.branch.control_files.files['branch.conf'].strip(),
696
959
            'foo = bar')
697
960
        self.assertEqual(self.my_config.get_user_option('foo'), 'bar')
698
 
        self.my_config.set_user_option('foo', 'baz', local=True)
 
961
        self.my_config.set_user_option('foo', 'baz',
 
962
                                       store=config.STORE_LOCATION)
699
963
        self.assertEqual(self.my_config.get_user_option('foo'), 'baz')
700
964
        self.my_config.set_user_option('foo', 'qux')
701
965
        self.assertEqual(self.my_config.get_user_option('foo'), 'baz')
702
 
        
 
966
 
 
967
    def test_get_bzr_remote_path(self):
 
968
        my_config = config.LocationConfig('/a/c')
 
969
        self.assertEqual('bzr', my_config.get_bzr_remote_path())
 
970
        my_config.set_user_option('bzr_remote_path', '/path-bzr')
 
971
        self.assertEqual('/path-bzr', my_config.get_bzr_remote_path())
 
972
        os.environ['BZR_REMOTE_PATH'] = '/environ-bzr'
 
973
        self.assertEqual('/environ-bzr', my_config.get_bzr_remote_path())
 
974
 
703
975
 
704
976
precedence_global = 'option = global'
705
977
precedence_branch = 'option = branch'
712
984
"""
713
985
 
714
986
 
715
 
class TestBranchConfigItems(TestCaseInTempDir):
 
987
class TestBranchConfigItems(tests.TestCaseInTempDir):
716
988
 
717
 
    def get_branch_config(self, global_config=None, location=None, 
 
989
    def get_branch_config(self, global_config=None, location=None,
718
990
                          location_config=None, branch_data_config=None):
719
991
        my_config = config.BranchConfig(FakeBranch(location))
720
992
        if global_config is not None:
734
1006
        my_config = config.BranchConfig(branch)
735
1007
        self.assertEqual("Robert Collins <robertc@example.net>",
736
1008
                         my_config.username())
737
 
        branch.control_files.email = "John"
738
 
        my_config.set_user_option('email', 
 
1009
        my_config.branch.control_files.files['email'] = "John"
 
1010
        my_config.set_user_option('email',
739
1011
                                  "Robert Collins <robertc@example.org>")
740
1012
        self.assertEqual("John", my_config.username())
741
 
        branch.control_files.email = None
 
1013
        del my_config.branch.control_files.files['email']
742
1014
        self.assertEqual("Robert Collins <robertc@example.org>",
743
1015
                         my_config.username())
744
1016
 
745
1017
    def test_not_set_in_branch(self):
746
1018
        my_config = self.get_branch_config(sample_config_text)
747
 
        my_config.branch.control_files.email = None
748
1019
        self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
749
1020
                         my_config._get_user_id())
750
 
        my_config.branch.control_files.email = "John"
 
1021
        my_config.branch.control_files.files['email'] = "John"
751
1022
        self.assertEqual("John", my_config._get_user_id())
752
1023
 
753
1024
    def test_BZR_EMAIL_OVERRIDES(self):
756
1027
        my_config = config.BranchConfig(branch)
757
1028
        self.assertEqual("Robert Collins <robertc@example.org>",
758
1029
                         my_config.username())
759
 
    
 
1030
 
760
1031
    def test_signatures_forced(self):
761
1032
        my_config = self.get_branch_config(
762
1033
            global_config=sample_always_signatures)
799
1070
        # post-commit is ignored when bresent in branch data
800
1071
        self.assertEqual('bzrlib.tests.test_config.post_commit',
801
1072
                         my_config.post_commit())
802
 
        my_config.set_user_option('post_commit', 'rmtree_root', local=True)
 
1073
        my_config.set_user_option('post_commit', 'rmtree_root',
 
1074
                                  store=config.STORE_LOCATION)
803
1075
        self.assertEqual('rmtree_root', my_config.post_commit())
804
1076
 
805
1077
    def test_config_precedence(self):
806
1078
        my_config = self.get_branch_config(global_config=precedence_global)
807
1079
        self.assertEqual(my_config.get_user_option('option'), 'global')
808
 
        my_config = self.get_branch_config(global_config=precedence_global, 
 
1080
        my_config = self.get_branch_config(global_config=precedence_global,
809
1081
                                      branch_data_config=precedence_branch)
810
1082
        self.assertEqual(my_config.get_user_option('option'), 'branch')
811
 
        my_config = self.get_branch_config(global_config=precedence_global, 
 
1083
        my_config = self.get_branch_config(global_config=precedence_global,
812
1084
                                      branch_data_config=precedence_branch,
813
1085
                                      location_config=precedence_location)
814
1086
        self.assertEqual(my_config.get_user_option('option'), 'recurse')
815
 
        my_config = self.get_branch_config(global_config=precedence_global, 
 
1087
        my_config = self.get_branch_config(global_config=precedence_global,
816
1088
                                      branch_data_config=precedence_branch,
817
1089
                                      location_config=precedence_location,
818
1090
                                      location='http://example.com/specific')
819
1091
        self.assertEqual(my_config.get_user_option('option'), 'exact')
820
1092
 
821
 
 
822
 
class TestMailAddressExtraction(TestCase):
 
1093
    def test_get_mail_client(self):
 
1094
        config = self.get_branch_config()
 
1095
        client = config.get_mail_client()
 
1096
        self.assertIsInstance(client, mail_client.DefaultMail)
 
1097
 
 
1098
        # Specific clients
 
1099
        config.set_user_option('mail_client', 'evolution')
 
1100
        client = config.get_mail_client()
 
1101
        self.assertIsInstance(client, mail_client.Evolution)
 
1102
 
 
1103
        config.set_user_option('mail_client', 'kmail')
 
1104
        client = config.get_mail_client()
 
1105
        self.assertIsInstance(client, mail_client.KMail)
 
1106
 
 
1107
        config.set_user_option('mail_client', 'mutt')
 
1108
        client = config.get_mail_client()
 
1109
        self.assertIsInstance(client, mail_client.Mutt)
 
1110
 
 
1111
        config.set_user_option('mail_client', 'thunderbird')
 
1112
        client = config.get_mail_client()
 
1113
        self.assertIsInstance(client, mail_client.Thunderbird)
 
1114
 
 
1115
        # Generic options
 
1116
        config.set_user_option('mail_client', 'default')
 
1117
        client = config.get_mail_client()
 
1118
        self.assertIsInstance(client, mail_client.DefaultMail)
 
1119
 
 
1120
        config.set_user_option('mail_client', 'editor')
 
1121
        client = config.get_mail_client()
 
1122
        self.assertIsInstance(client, mail_client.Editor)
 
1123
 
 
1124
        config.set_user_option('mail_client', 'mapi')
 
1125
        client = config.get_mail_client()
 
1126
        self.assertIsInstance(client, mail_client.MAPIClient)
 
1127
 
 
1128
        config.set_user_option('mail_client', 'xdg-email')
 
1129
        client = config.get_mail_client()
 
1130
        self.assertIsInstance(client, mail_client.XDGEmail)
 
1131
 
 
1132
        config.set_user_option('mail_client', 'firebird')
 
1133
        self.assertRaises(errors.UnknownMailClient, config.get_mail_client)
 
1134
 
 
1135
 
 
1136
class TestMailAddressExtraction(tests.TestCase):
823
1137
 
824
1138
    def test_extract_email_address(self):
825
1139
        self.assertEqual('jane@test.com',
826
1140
                         config.extract_email_address('Jane <jane@test.com>'))
827
 
        self.assertRaises(errors.BzrError,
 
1141
        self.assertRaises(errors.NoEmailInUsername,
828
1142
                          config.extract_email_address, 'Jane Tester')
 
1143
 
 
1144
    def test_parse_username(self):
 
1145
        self.assertEqual(('', 'jdoe@example.com'),
 
1146
                         config.parse_username('jdoe@example.com'))
 
1147
        self.assertEqual(('', 'jdoe@example.com'),
 
1148
                         config.parse_username('<jdoe@example.com>'))
 
1149
        self.assertEqual(('John Doe', 'jdoe@example.com'),
 
1150
                         config.parse_username('John Doe <jdoe@example.com>'))
 
1151
        self.assertEqual(('John Doe', ''),
 
1152
                         config.parse_username('John Doe'))
 
1153
        self.assertEqual(('John Doe', 'jdoe@example.com'),
 
1154
                         config.parse_username('John Doe jdoe@example.com'))
 
1155
 
 
1156
class TestTreeConfig(tests.TestCaseWithTransport):
 
1157
 
 
1158
    def test_get_value(self):
 
1159
        """Test that retreiving a value from a section is possible"""
 
1160
        branch = self.make_branch('.')
 
1161
        tree_config = config.TreeConfig(branch)
 
1162
        tree_config.set_option('value', 'key', 'SECTION')
 
1163
        tree_config.set_option('value2', 'key2')
 
1164
        tree_config.set_option('value3-top', 'key3')
 
1165
        tree_config.set_option('value3-section', 'key3', 'SECTION')
 
1166
        value = tree_config.get_option('key', 'SECTION')
 
1167
        self.assertEqual(value, 'value')
 
1168
        value = tree_config.get_option('key2')
 
1169
        self.assertEqual(value, 'value2')
 
1170
        self.assertEqual(tree_config.get_option('non-existant'), None)
 
1171
        value = tree_config.get_option('non-existant', 'SECTION')
 
1172
        self.assertEqual(value, None)
 
1173
        value = tree_config.get_option('non-existant', default='default')
 
1174
        self.assertEqual(value, 'default')
 
1175
        self.assertEqual(tree_config.get_option('key2', 'NOSECTION'), None)
 
1176
        value = tree_config.get_option('key2', 'NOSECTION', default='default')
 
1177
        self.assertEqual(value, 'default')
 
1178
        value = tree_config.get_option('key3')
 
1179
        self.assertEqual(value, 'value3-top')
 
1180
        value = tree_config.get_option('key3', 'SECTION')
 
1181
        self.assertEqual(value, 'value3-section')
 
1182
 
 
1183
 
 
1184
class TestTransportConfig(tests.TestCaseWithTransport):
 
1185
 
 
1186
    def test_get_value(self):
 
1187
        """Test that retreiving a value from a section is possible"""
 
1188
        bzrdir_config = config.TransportConfig(transport.get_transport('.'),
 
1189
                                               'control.conf')
 
1190
        bzrdir_config.set_option('value', 'key', 'SECTION')
 
1191
        bzrdir_config.set_option('value2', 'key2')
 
1192
        bzrdir_config.set_option('value3-top', 'key3')
 
1193
        bzrdir_config.set_option('value3-section', 'key3', 'SECTION')
 
1194
        value = bzrdir_config.get_option('key', 'SECTION')
 
1195
        self.assertEqual(value, 'value')
 
1196
        value = bzrdir_config.get_option('key2')
 
1197
        self.assertEqual(value, 'value2')
 
1198
        self.assertEqual(bzrdir_config.get_option('non-existant'), None)
 
1199
        value = bzrdir_config.get_option('non-existant', 'SECTION')
 
1200
        self.assertEqual(value, None)
 
1201
        value = bzrdir_config.get_option('non-existant', default='default')
 
1202
        self.assertEqual(value, 'default')
 
1203
        self.assertEqual(bzrdir_config.get_option('key2', 'NOSECTION'), None)
 
1204
        value = bzrdir_config.get_option('key2', 'NOSECTION',
 
1205
                                         default='default')
 
1206
        self.assertEqual(value, 'default')
 
1207
        value = bzrdir_config.get_option('key3')
 
1208
        self.assertEqual(value, 'value3-top')
 
1209
        value = bzrdir_config.get_option('key3', 'SECTION')
 
1210
        self.assertEqual(value, 'value3-section')
 
1211
 
 
1212
    def test_set_unset_default_stack_on(self):
 
1213
        my_dir = self.make_bzrdir('.')
 
1214
        bzrdir_config = config.BzrDirConfig(my_dir)
 
1215
        self.assertIs(None, bzrdir_config.get_default_stack_on())
 
1216
        bzrdir_config.set_default_stack_on('Foo')
 
1217
        self.assertEqual('Foo', bzrdir_config._config.get_option(
 
1218
                         'default_stack_on'))
 
1219
        self.assertEqual('Foo', bzrdir_config.get_default_stack_on())
 
1220
        bzrdir_config.set_default_stack_on(None)
 
1221
        self.assertIs(None, bzrdir_config.get_default_stack_on())
 
1222
 
 
1223
 
 
1224
class TestAuthenticationConfigFile(tests.TestCase):
 
1225
    """Test the authentication.conf file matching"""
 
1226
 
 
1227
    def _got_user_passwd(self, expected_user, expected_password,
 
1228
                         config, *args, **kwargs):
 
1229
        credentials = config.get_credentials(*args, **kwargs)
 
1230
        if credentials is None:
 
1231
            user = None
 
1232
            password = None
 
1233
        else:
 
1234
            user = credentials['user']
 
1235
            password = credentials['password']
 
1236
        self.assertEquals(expected_user, user)
 
1237
        self.assertEquals(expected_password, password)
 
1238
 
 
1239
    def test_empty_config(self):
 
1240
        conf = config.AuthenticationConfig(_file=StringIO())
 
1241
        self.assertEquals({}, conf._get_config())
 
1242
        self._got_user_passwd(None, None, conf, 'http', 'foo.net')
 
1243
 
 
1244
    def test_missing_auth_section_header(self):
 
1245
        conf = config.AuthenticationConfig(_file=StringIO('foo = bar'))
 
1246
        self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
 
1247
 
 
1248
    def test_auth_section_header_not_closed(self):
 
1249
        conf = config.AuthenticationConfig(_file=StringIO('[DEF'))
 
1250
        self.assertRaises(errors.ParseConfigError, conf._get_config)
 
1251
 
 
1252
    def test_auth_value_not_boolean(self):
 
1253
        conf = config.AuthenticationConfig(_file=StringIO(
 
1254
                """[broken]
 
1255
scheme=ftp
 
1256
user=joe
 
1257
verify_certificates=askme # Error: Not a boolean
 
1258
"""))
 
1259
        self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
 
1260
 
 
1261
    def test_auth_value_not_int(self):
 
1262
        conf = config.AuthenticationConfig(_file=StringIO(
 
1263
                """[broken]
 
1264
scheme=ftp
 
1265
user=joe
 
1266
port=port # Error: Not an int
 
1267
"""))
 
1268
        self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
 
1269
 
 
1270
    def test_unknown_password_encoding(self):
 
1271
        conf = config.AuthenticationConfig(_file=StringIO(
 
1272
                """[broken]
 
1273
scheme=ftp
 
1274
user=joe
 
1275
password_encoding=unknown
 
1276
"""))
 
1277
        self.assertRaises(ValueError, conf.get_password,
 
1278
                          'ftp', 'foo.net', 'joe')
 
1279
 
 
1280
    def test_credentials_for_scheme_host(self):
 
1281
        conf = config.AuthenticationConfig(_file=StringIO(
 
1282
                """# Identity on foo.net
 
1283
[ftp definition]
 
1284
scheme=ftp
 
1285
host=foo.net
 
1286
user=joe
 
1287
password=secret-pass
 
1288
"""))
 
1289
        # Basic matching
 
1290
        self._got_user_passwd('joe', 'secret-pass', conf, 'ftp', 'foo.net')
 
1291
        # different scheme
 
1292
        self._got_user_passwd(None, None, conf, 'http', 'foo.net')
 
1293
        # different host
 
1294
        self._got_user_passwd(None, None, conf, 'ftp', 'bar.net')
 
1295
 
 
1296
    def test_credentials_for_host_port(self):
 
1297
        conf = config.AuthenticationConfig(_file=StringIO(
 
1298
                """# Identity on foo.net
 
1299
[ftp definition]
 
1300
scheme=ftp
 
1301
port=10021
 
1302
host=foo.net
 
1303
user=joe
 
1304
password=secret-pass
 
1305
"""))
 
1306
        # No port
 
1307
        self._got_user_passwd('joe', 'secret-pass',
 
1308
                              conf, 'ftp', 'foo.net', port=10021)
 
1309
        # different port
 
1310
        self._got_user_passwd(None, None, conf, 'ftp', 'foo.net')
 
1311
 
 
1312
    def test_for_matching_host(self):
 
1313
        conf = config.AuthenticationConfig(_file=StringIO(
 
1314
                """# Identity on foo.net
 
1315
[sourceforge]
 
1316
scheme=bzr
 
1317
host=bzr.sf.net
 
1318
user=joe
 
1319
password=joepass
 
1320
[sourceforge domain]
 
1321
scheme=bzr
 
1322
host=.bzr.sf.net
 
1323
user=georges
 
1324
password=bendover
 
1325
"""))
 
1326
        # matching domain
 
1327
        self._got_user_passwd('georges', 'bendover',
 
1328
                              conf, 'bzr', 'foo.bzr.sf.net')
 
1329
        # phishing attempt
 
1330
        self._got_user_passwd(None, None,
 
1331
                              conf, 'bzr', 'bbzr.sf.net')
 
1332
 
 
1333
    def test_for_matching_host_None(self):
 
1334
        conf = config.AuthenticationConfig(_file=StringIO(
 
1335
                """# Identity on foo.net
 
1336
[catchup bzr]
 
1337
scheme=bzr
 
1338
user=joe
 
1339
password=joepass
 
1340
[DEFAULT]
 
1341
user=georges
 
1342
password=bendover
 
1343
"""))
 
1344
        # match no host
 
1345
        self._got_user_passwd('joe', 'joepass',
 
1346
                              conf, 'bzr', 'quux.net')
 
1347
        # no host but different scheme
 
1348
        self._got_user_passwd('georges', 'bendover',
 
1349
                              conf, 'ftp', 'quux.net')
 
1350
 
 
1351
    def test_credentials_for_path(self):
 
1352
        conf = config.AuthenticationConfig(_file=StringIO(
 
1353
                """
 
1354
[http dir1]
 
1355
scheme=http
 
1356
host=bar.org
 
1357
path=/dir1
 
1358
user=jim
 
1359
password=jimpass
 
1360
[http dir2]
 
1361
scheme=http
 
1362
host=bar.org
 
1363
path=/dir2
 
1364
user=georges
 
1365
password=bendover
 
1366
"""))
 
1367
        # no path no dice
 
1368
        self._got_user_passwd(None, None,
 
1369
                              conf, 'http', host='bar.org', path='/dir3')
 
1370
        # matching path
 
1371
        self._got_user_passwd('georges', 'bendover',
 
1372
                              conf, 'http', host='bar.org', path='/dir2')
 
1373
        # matching subdir
 
1374
        self._got_user_passwd('jim', 'jimpass',
 
1375
                              conf, 'http', host='bar.org',path='/dir1/subdir')
 
1376
 
 
1377
    def test_credentials_for_user(self):
 
1378
        conf = config.AuthenticationConfig(_file=StringIO(
 
1379
                """
 
1380
[with user]
 
1381
scheme=http
 
1382
host=bar.org
 
1383
user=jim
 
1384
password=jimpass
 
1385
"""))
 
1386
        # Get user
 
1387
        self._got_user_passwd('jim', 'jimpass',
 
1388
                              conf, 'http', 'bar.org')
 
1389
        # Get same user
 
1390
        self._got_user_passwd('jim', 'jimpass',
 
1391
                              conf, 'http', 'bar.org', user='jim')
 
1392
        # Don't get a different user if one is specified
 
1393
        self._got_user_passwd(None, None,
 
1394
                              conf, 'http', 'bar.org', user='georges')
 
1395
 
 
1396
    def test_credentials_for_user_without_password(self):
 
1397
        conf = config.AuthenticationConfig(_file=StringIO(
 
1398
                """
 
1399
[without password]
 
1400
scheme=http
 
1401
host=bar.org
 
1402
user=jim
 
1403
"""))
 
1404
        # Get user but no password
 
1405
        self._got_user_passwd('jim', None,
 
1406
                              conf, 'http', 'bar.org')
 
1407
 
 
1408
    def test_verify_certificates(self):
 
1409
        conf = config.AuthenticationConfig(_file=StringIO(
 
1410
                """
 
1411
[self-signed]
 
1412
scheme=https
 
1413
host=bar.org
 
1414
user=jim
 
1415
password=jimpass
 
1416
verify_certificates=False
 
1417
[normal]
 
1418
scheme=https
 
1419
host=foo.net
 
1420
user=georges
 
1421
password=bendover
 
1422
"""))
 
1423
        credentials = conf.get_credentials('https', 'bar.org')
 
1424
        self.assertEquals(False, credentials.get('verify_certificates'))
 
1425
        credentials = conf.get_credentials('https', 'foo.net')
 
1426
        self.assertEquals(True, credentials.get('verify_certificates'))
 
1427
 
 
1428
 
 
1429
class TestAuthenticationStorage(tests.TestCaseInTempDir):
 
1430
 
 
1431
    def test_set_credentials(self):
 
1432
        conf = config.AuthenticationConfig()
 
1433
        conf.set_credentials('name', 'host', 'user', 'scheme', 'password',
 
1434
        99, path='/foo', verify_certificates=False, realm='realm')
 
1435
        credentials = conf.get_credentials(host='host', scheme='scheme',
 
1436
                                           port=99, path='/foo',
 
1437
                                           realm='realm')
 
1438
        CREDENTIALS = {'name': 'name', 'user': 'user', 'password': 'password',
 
1439
                       'verify_certificates': False, 'scheme': 'scheme', 
 
1440
                       'host': 'host', 'port': 99, 'path': '/foo', 
 
1441
                       'realm': 'realm'}
 
1442
        self.assertEqual(CREDENTIALS, credentials)
 
1443
        credentials_from_disk = config.AuthenticationConfig().get_credentials(
 
1444
            host='host', scheme='scheme', port=99, path='/foo', realm='realm')
 
1445
        self.assertEqual(CREDENTIALS, credentials_from_disk)
 
1446
 
 
1447
    def test_reset_credentials_different_name(self):
 
1448
        conf = config.AuthenticationConfig()
 
1449
        conf.set_credentials('name', 'host', 'user', 'scheme', 'password'),
 
1450
        conf.set_credentials('name2', 'host', 'user2', 'scheme', 'password'),
 
1451
        self.assertIs(None, conf._get_config().get('name'))
 
1452
        credentials = conf.get_credentials(host='host', scheme='scheme')
 
1453
        CREDENTIALS = {'name': 'name2', 'user': 'user2', 'password':
 
1454
                       'password', 'verify_certificates': True, 
 
1455
                       'scheme': 'scheme', 'host': 'host', 'port': None, 
 
1456
                       'path': None, 'realm': None}
 
1457
        self.assertEqual(CREDENTIALS, credentials)
 
1458
 
 
1459
 
 
1460
class TestAuthenticationConfig(tests.TestCase):
 
1461
    """Test AuthenticationConfig behaviour"""
 
1462
 
 
1463
    def _check_default_password_prompt(self, expected_prompt_format, scheme,
 
1464
                                       host=None, port=None, realm=None,
 
1465
                                       path=None):
 
1466
        if host is None:
 
1467
            host = 'bar.org'
 
1468
        user, password = 'jim', 'precious'
 
1469
        expected_prompt = expected_prompt_format % {
 
1470
            'scheme': scheme, 'host': host, 'port': port,
 
1471
            'user': user, 'realm': realm}
 
1472
 
 
1473
        stdout = tests.StringIOWrapper()
 
1474
        stderr = tests.StringIOWrapper()
 
1475
        ui.ui_factory = tests.TestUIFactory(stdin=password + '\n',
 
1476
                                            stdout=stdout, stderr=stderr)
 
1477
        # We use an empty conf so that the user is always prompted
 
1478
        conf = config.AuthenticationConfig()
 
1479
        self.assertEquals(password,
 
1480
                          conf.get_password(scheme, host, user, port=port,
 
1481
                                            realm=realm, path=path))
 
1482
        self.assertEquals(expected_prompt, stderr.getvalue())
 
1483
        self.assertEquals('', stdout.getvalue())
 
1484
 
 
1485
    def _check_default_username_prompt(self, expected_prompt_format, scheme,
 
1486
                                       host=None, port=None, realm=None,
 
1487
                                       path=None):
 
1488
        if host is None:
 
1489
            host = 'bar.org'
 
1490
        username = 'jim'
 
1491
        expected_prompt = expected_prompt_format % {
 
1492
            'scheme': scheme, 'host': host, 'port': port,
 
1493
            'realm': realm}
 
1494
        stdout = tests.StringIOWrapper()
 
1495
        stderr = tests.StringIOWrapper()
 
1496
        ui.ui_factory = tests.TestUIFactory(stdin=username+ '\n',
 
1497
                                            stdout=stdout, stderr=stderr)
 
1498
        # We use an empty conf so that the user is always prompted
 
1499
        conf = config.AuthenticationConfig()
 
1500
        self.assertEquals(username, conf.get_user(scheme, host, port=port,
 
1501
                          realm=realm, path=path, ask=True))
 
1502
        self.assertEquals(expected_prompt, stderr.getvalue())
 
1503
        self.assertEquals('', stdout.getvalue())
 
1504
 
 
1505
    def test_username_defaults_prompts(self):
 
1506
        # HTTP prompts can't be tested here, see test_http.py
 
1507
        self._check_default_username_prompt('FTP %(host)s username: ', 'ftp')
 
1508
        self._check_default_username_prompt(
 
1509
            'FTP %(host)s:%(port)d username: ', 'ftp', port=10020)
 
1510
        self._check_default_username_prompt(
 
1511
            'SSH %(host)s:%(port)d username: ', 'ssh', port=12345)
 
1512
 
 
1513
    def test_username_default_no_prompt(self):
 
1514
        conf = config.AuthenticationConfig()
 
1515
        self.assertEquals(None,
 
1516
            conf.get_user('ftp', 'example.com'))
 
1517
        self.assertEquals("explicitdefault",
 
1518
            conf.get_user('ftp', 'example.com', default="explicitdefault"))
 
1519
 
 
1520
    def test_password_default_prompts(self):
 
1521
        # HTTP prompts can't be tested here, see test_http.py
 
1522
        self._check_default_password_prompt(
 
1523
            'FTP %(user)s@%(host)s password: ', 'ftp')
 
1524
        self._check_default_password_prompt(
 
1525
            'FTP %(user)s@%(host)s:%(port)d password: ', 'ftp', port=10020)
 
1526
        self._check_default_password_prompt(
 
1527
            'SSH %(user)s@%(host)s:%(port)d password: ', 'ssh', port=12345)
 
1528
        # SMTP port handling is a bit special (it's handled if embedded in the
 
1529
        # host too)
 
1530
        # FIXME: should we: forbid that, extend it to other schemes, leave
 
1531
        # things as they are that's fine thank you ?
 
1532
        self._check_default_password_prompt('SMTP %(user)s@%(host)s password: ',
 
1533
                                            'smtp')
 
1534
        self._check_default_password_prompt('SMTP %(user)s@%(host)s password: ',
 
1535
                                            'smtp', host='bar.org:10025')
 
1536
        self._check_default_password_prompt(
 
1537
            'SMTP %(user)s@%(host)s:%(port)d password: ',
 
1538
            'smtp', port=10025)
 
1539
 
 
1540
    def test_ssh_password_emits_warning(self):
 
1541
        conf = config.AuthenticationConfig(_file=StringIO(
 
1542
                """
 
1543
[ssh with password]
 
1544
scheme=ssh
 
1545
host=bar.org
 
1546
user=jim
 
1547
password=jimpass
 
1548
"""))
 
1549
        entered_password = 'typed-by-hand'
 
1550
        stdout = tests.StringIOWrapper()
 
1551
        ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
 
1552
                                            stdout=stdout)
 
1553
 
 
1554
        # Since the password defined in the authentication config is ignored,
 
1555
        # the user is prompted
 
1556
        self.assertEquals(entered_password,
 
1557
                          conf.get_password('ssh', 'bar.org', user='jim'))
 
1558
        self.assertContainsRe(
 
1559
            self._get_log(keep_log_file=True),
 
1560
            'password ignored in section \[ssh with password\]')
 
1561
 
 
1562
    def test_ssh_without_password_doesnt_emit_warning(self):
 
1563
        conf = config.AuthenticationConfig(_file=StringIO(
 
1564
                """
 
1565
[ssh with password]
 
1566
scheme=ssh
 
1567
host=bar.org
 
1568
user=jim
 
1569
"""))
 
1570
        entered_password = 'typed-by-hand'
 
1571
        stdout = tests.StringIOWrapper()
 
1572
        ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
 
1573
                                            stdout=stdout)
 
1574
 
 
1575
        # Since the password defined in the authentication config is ignored,
 
1576
        # the user is prompted
 
1577
        self.assertEquals(entered_password,
 
1578
                          conf.get_password('ssh', 'bar.org', user='jim'))
 
1579
        # No warning shoud be emitted since there is no password. We are only
 
1580
        # providing "user".
 
1581
        self.assertNotContainsRe(
 
1582
            self._get_log(keep_log_file=True),
 
1583
            'password ignored in section \[ssh with password\]')
 
1584
 
 
1585
    def test_uses_fallback_stores(self):
 
1586
        self._old_cs_registry = config.credential_store_registry
 
1587
        def restore():
 
1588
            config.credential_store_registry = self._old_cs_registry
 
1589
        self.addCleanup(restore)
 
1590
        config.credential_store_registry = config.CredentialStoreRegistry()
 
1591
        store = StubCredentialStore()
 
1592
        store.add_credentials("http", "example.com", "joe", "secret")
 
1593
        config.credential_store_registry.register("stub", store, fallback=True)
 
1594
        conf = config.AuthenticationConfig(_file=StringIO())
 
1595
        creds = conf.get_credentials("http", "example.com")
 
1596
        self.assertEquals("joe", creds["user"])
 
1597
        self.assertEquals("secret", creds["password"])
 
1598
 
 
1599
 
 
1600
class StubCredentialStore(config.CredentialStore):
 
1601
 
 
1602
    def __init__(self):
 
1603
        self._username = {}
 
1604
        self._password = {}
 
1605
 
 
1606
    def add_credentials(self, scheme, host, user, password=None):
 
1607
        self._username[(scheme, host)] = user
 
1608
        self._password[(scheme, host)] = password
 
1609
 
 
1610
    def get_credentials(self, scheme, host, port=None, user=None,
 
1611
        path=None, realm=None):
 
1612
        key = (scheme, host)
 
1613
        if not key in self._username:
 
1614
            return None
 
1615
        return { "scheme": scheme, "host": host, "port": port,
 
1616
                "user": self._username[key], "password": self._password[key]}
 
1617
 
 
1618
 
 
1619
class CountingCredentialStore(config.CredentialStore):
 
1620
 
 
1621
    def __init__(self):
 
1622
        self._calls = 0
 
1623
 
 
1624
    def get_credentials(self, scheme, host, port=None, user=None,
 
1625
        path=None, realm=None):
 
1626
        self._calls += 1
 
1627
        return None
 
1628
 
 
1629
 
 
1630
class TestCredentialStoreRegistry(tests.TestCase):
 
1631
 
 
1632
    def _get_cs_registry(self):
 
1633
        return config.credential_store_registry
 
1634
 
 
1635
    def test_default_credential_store(self):
 
1636
        r = self._get_cs_registry()
 
1637
        default = r.get_credential_store(None)
 
1638
        self.assertIsInstance(default, config.PlainTextCredentialStore)
 
1639
 
 
1640
    def test_unknown_credential_store(self):
 
1641
        r = self._get_cs_registry()
 
1642
        # It's hard to imagine someone creating a credential store named
 
1643
        # 'unknown' so we use that as an never registered key.
 
1644
        self.assertRaises(KeyError, r.get_credential_store, 'unknown')
 
1645
 
 
1646
    def test_fallback_none_registered(self):
 
1647
        r = config.CredentialStoreRegistry()
 
1648
        self.assertEquals(None,
 
1649
                          r.get_fallback_credentials("http", "example.com"))
 
1650
 
 
1651
    def test_register(self):
 
1652
        r = config.CredentialStoreRegistry()
 
1653
        r.register("stub", StubCredentialStore(), fallback=False)
 
1654
        r.register("another", StubCredentialStore(), fallback=True)
 
1655
        self.assertEquals(["another", "stub"], r.keys())
 
1656
 
 
1657
    def test_register_lazy(self):
 
1658
        r = config.CredentialStoreRegistry()
 
1659
        r.register_lazy("stub", "bzrlib.tests.test_config",
 
1660
                        "StubCredentialStore", fallback=False)
 
1661
        self.assertEquals(["stub"], r.keys())
 
1662
        self.assertIsInstance(r.get_credential_store("stub"),
 
1663
                              StubCredentialStore)
 
1664
 
 
1665
    def test_is_fallback(self):
 
1666
        r = config.CredentialStoreRegistry()
 
1667
        r.register("stub1", None, fallback=False)
 
1668
        r.register("stub2", None, fallback=True)
 
1669
        self.assertEquals(False, r.is_fallback("stub1"))
 
1670
        self.assertEquals(True, r.is_fallback("stub2"))
 
1671
 
 
1672
    def test_no_fallback(self):
 
1673
        r = config.CredentialStoreRegistry()
 
1674
        store = CountingCredentialStore()
 
1675
        r.register("count", store, fallback=False)
 
1676
        self.assertEquals(None,
 
1677
                          r.get_fallback_credentials("http", "example.com"))
 
1678
        self.assertEquals(0, store._calls)
 
1679
 
 
1680
    def test_fallback_credentials(self):
 
1681
        r = config.CredentialStoreRegistry()
 
1682
        store = StubCredentialStore()
 
1683
        store.add_credentials("http", "example.com",
 
1684
                              "somebody", "geheim")
 
1685
        r.register("stub", store, fallback=True)
 
1686
        creds = r.get_fallback_credentials("http", "example.com")
 
1687
        self.assertEquals("somebody", creds["user"])
 
1688
        self.assertEquals("geheim", creds["password"])
 
1689
 
 
1690
    def test_fallback_first_wins(self):
 
1691
        r = config.CredentialStoreRegistry()
 
1692
        stub1 = StubCredentialStore()
 
1693
        stub1.add_credentials("http", "example.com",
 
1694
                              "somebody", "stub1")
 
1695
        r.register("stub1", stub1, fallback=True)
 
1696
        stub2 = StubCredentialStore()
 
1697
        stub2.add_credentials("http", "example.com",
 
1698
                              "somebody", "stub2")
 
1699
        r.register("stub2", stub1, fallback=True)
 
1700
        creds = r.get_fallback_credentials("http", "example.com")
 
1701
        self.assertEquals("somebody", creds["user"])
 
1702
        self.assertEquals("stub1", creds["password"])
 
1703
 
 
1704
 
 
1705
class TestPlainTextCredentialStore(tests.TestCase):
 
1706
 
 
1707
    def test_decode_password(self):
 
1708
        r = config.credential_store_registry
 
1709
        plain_text = r.get_credential_store()
 
1710
        decoded = plain_text.decode_password(dict(password='secret'))
 
1711
        self.assertEquals('secret', decoded)
 
1712
 
 
1713
 
 
1714
# FIXME: Once we have a way to declare authentication to all test servers, we
 
1715
# can implement generic tests.
 
1716
# test_user_password_in_url
 
1717
# test_user_in_url_password_from_config
 
1718
# test_user_in_url_password_prompted
 
1719
# test_user_in_config
 
1720
# test_user_getpass.getuser
 
1721
# test_user_prompted ?
 
1722
class TestAuthenticationRing(tests.TestCaseWithTransport):
 
1723
    pass