/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: John Arbash Meinel
  • Date: 2009-06-18 18:18:36 UTC
  • mto: This revision was merged to the branch mainline in revision 4461.
  • Revision ID: john@arbash-meinel.com-20090618181836-biodfkat9a8eyzjz
The new add_inventory_by_delta is returning a CHKInventory when mapping from NULL
Which is completely valid, but 'broke' one of the tests.
So to fix it, changed the test to use CHKInventories on both sides, and add an __eq__
member. The nice thing is that CHKInventory.__eq__ is fairly cheap, since it only
has to check the root keys.

Show diffs side-by-side

added added

removed removed

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