/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_config.py

  • Committer: Matthew Fuller
  • Date: 2009-07-25 15:21:25 UTC
  • mto: (4772.1.1 integration)
  • mto: This revision was merged to the branch mainline in revision 4773.
  • Revision ID: fullermd@over-yonder.net-20090725152125-2mzil0setr85g0no
Adjust documentation in the user guide a bit to correspond to DWIM
revspecs.

This is untested, as I don't have the tools to build the docs.  It's
also really not as clean as it could be, and there are probably much
better ways to put this info in here.  But it's something.

Show diffs side-by-side

added added

removed removed

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