~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_config.py

  • Committer: Martin Pool
  • Date: 2005-09-13 00:39:44 UTC
  • mto: (1185.8.2) (974.1.91)
  • mto: This revision was merged to the branch mainline in revision 1390.
  • Revision ID: mbp@sourcefrog.net-20050913003944-4890c9f8f04f37a5
- remove TestCase.run override which captures output

  this is *not* the way we want to do it now; it's a bug if the 
  library produces any output without permission

Show diffs side-by-side

added added

removed removed

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