/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: mernst at mit
  • Date: 2008-10-16 10:57:16 UTC
  • mto: This revision was merged to the branch mainline in revision 3799.
  • Revision ID: mernst@csail.mit.edu-20081016105716-v8x8n5t2pf7f6uds
Improved documentation of stacked and lightweight branches

These patches improve the User Guide's documentation of stacked and
lightweight branches.

Section "1.2.6 Putting the concepts together" should mention stacked
branches and the difference between them and lightweight branches.  It
should also contain links to further details of the common scenarios.

Section "5.3.4 Getting a lightweight checkout" should mention stacked
branches as an option, and should link to all the options, not just some of
them.  It should also clarify that lightweight only applies to checkouts,
not to arbitrary branches.

Show diffs side-by-side

added added

removed removed

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