~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_config.py

  • Committer: Vincent Ladeuil
  • Date: 2010-10-26 08:08:23 UTC
  • mfrom: (5514.1.1 665100-content-type)
  • mto: This revision was merged to the branch mainline in revision 5516.
  • Revision ID: v.ladeuil+lp@free.fr-20101026080823-3wggo03b7cpn9908
Correctly set the Content-Type header when POSTing http requests

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 by Canonical Ltd
2
 
#   Authors: Robert Collins <robert.collins@canonical.com>
 
1
# Copyright (C) 2005-2010 Canonical Ltd
3
2
#
4
3
# This program is free software; you can redistribute it and/or modify
5
4
# it under the terms of the GNU General Public License as published by
13
12
#
14
13
# You should have received a copy of the GNU General Public License
15
14
# along with this program; if not, write to the Free Software
16
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17
16
 
18
17
"""Tests for finding and reading the bzr config file[s]."""
19
18
# import system imports here
20
 
from bzrlib.util.configobj.configobj import ConfigObj, ConfigObjError
21
19
from cStringIO import StringIO
22
20
import os
23
21
import sys
 
22
import threading
24
23
 
25
24
#import bzrlib specific imports here
26
 
import bzrlib.config as config
27
 
from bzrlib.branch import Branch
28
 
from bzrlib.bzrdir import BzrDir
29
 
import bzrlib.errors as errors
30
 
from bzrlib.tests import TestCase, TestCaseInTempDir, TestCaseWithTransport
 
25
from bzrlib import (
 
26
    branch,
 
27
    bzrdir,
 
28
    config,
 
29
    diff,
 
30
    errors,
 
31
    osutils,
 
32
    mail_client,
 
33
    ui,
 
34
    urlutils,
 
35
    tests,
 
36
    trace,
 
37
    transport,
 
38
    )
 
39
from bzrlib.tests import features
 
40
from bzrlib.util.configobj import configobj
 
41
 
 
42
 
 
43
def lockable_config_scenarios():
 
44
    return [
 
45
        ('global',
 
46
         {'config_class': config.GlobalConfig,
 
47
          'config_args': [],
 
48
          'config_section': 'DEFAULT'}),
 
49
        ('locations',
 
50
         {'config_class': config.LocationConfig,
 
51
          'config_args': ['.'],
 
52
          'config_section': '.'}),]
 
53
 
 
54
 
 
55
def load_tests(standard_tests, module, loader):
 
56
    suite = loader.suiteClass()
 
57
 
 
58
    lc_tests, remaining_tests = tests.split_suite_by_condition(
 
59
        standard_tests, tests.condition_isinstance((
 
60
                TestLockableConfig,
 
61
                )))
 
62
    tests.multiply_tests(lc_tests, lockable_config_scenarios(), suite)
 
63
    suite.addTest(remaining_tests)
 
64
    return suite
31
65
 
32
66
 
33
67
sample_long_alias="log -r-15..-1 --line"
34
 
sample_config_text = ("[DEFAULT]\n"
35
 
                      u"email=Erik B\u00e5gfors <erik@bagfors.nu>\n"
36
 
                      "editor=vim\n"
37
 
                      "gpg_signing_command=gnome-gpg\n"
38
 
                      "log_format=short\n"
39
 
                      "user_global_option=something\n"
40
 
                      "[ALIASES]\n"
41
 
                      "h=help\n"
42
 
                      "ll=" + sample_long_alias + "\n")
43
 
 
44
 
 
45
 
sample_always_signatures = ("[DEFAULT]\n"
46
 
                            "check_signatures=ignore\n"
47
 
                            "create_signatures=always")
48
 
 
49
 
 
50
 
sample_ignore_signatures = ("[DEFAULT]\n"
51
 
                            "check_signatures=require\n"
52
 
                            "create_signatures=never")
53
 
 
54
 
 
55
 
sample_maybe_signatures = ("[DEFAULT]\n"
56
 
                            "check_signatures=ignore\n"
57
 
                            "create_signatures=when-required")
58
 
 
59
 
 
60
 
sample_branches_text = ("[http://www.example.com]\n"
61
 
                        "# Top level policy\n"
62
 
                        "email=Robert Collins <robertc@example.org>\n"
63
 
                        "[http://www.example.com/useglobal]\n"
64
 
                        "# different project, forces global lookup\n"
65
 
                        "recurse=false\n"
66
 
                        "[/b/]\n"
67
 
                        "check_signatures=require\n"
68
 
                        "# test trailing / matching with no children\n"
69
 
                        "[/a/]\n"
70
 
                        "check_signatures=check-available\n"
71
 
                        "gpg_signing_command=false\n"
72
 
                        "user_local_option=local\n"
73
 
                        "# test trailing / matching\n"
74
 
                        "[/a/*]\n"
75
 
                        "#subdirs will match but not the parent\n"
76
 
                        "recurse=False\n"
77
 
                        "[/a/c]\n"
78
 
                        "check_signatures=ignore\n"
79
 
                        "post_commit=bzrlib.tests.test_config.post_commit\n"
80
 
                        "#testing explicit beats globs\n")
81
 
 
 
68
sample_config_text = u"""
 
69
[DEFAULT]
 
70
email=Erik B\u00e5gfors <erik@bagfors.nu>
 
71
editor=vim
 
72
change_editor=vimdiff -of @new_path @old_path
 
73
gpg_signing_command=gnome-gpg
 
74
log_format=short
 
75
user_global_option=something
 
76
[ALIASES]
 
77
h=help
 
78
ll=""" + sample_long_alias + "\n"
 
79
 
 
80
 
 
81
sample_always_signatures = """
 
82
[DEFAULT]
 
83
check_signatures=ignore
 
84
create_signatures=always
 
85
"""
 
86
 
 
87
sample_ignore_signatures = """
 
88
[DEFAULT]
 
89
check_signatures=require
 
90
create_signatures=never
 
91
"""
 
92
 
 
93
sample_maybe_signatures = """
 
94
[DEFAULT]
 
95
check_signatures=ignore
 
96
create_signatures=when-required
 
97
"""
 
98
 
 
99
sample_branches_text = """
 
100
[http://www.example.com]
 
101
# Top level policy
 
102
email=Robert Collins <robertc@example.org>
 
103
normal_option = normal
 
104
appendpath_option = append
 
105
appendpath_option:policy = appendpath
 
106
norecurse_option = norecurse
 
107
norecurse_option:policy = norecurse
 
108
[http://www.example.com/ignoreparent]
 
109
# different project: ignore parent dir config
 
110
ignore_parents=true
 
111
[http://www.example.com/norecurse]
 
112
# configuration items that only apply to this dir
 
113
recurse=false
 
114
normal_option = norecurse
 
115
[http://www.example.com/dir]
 
116
appendpath_option = normal
 
117
[/b/]
 
118
check_signatures=require
 
119
# test trailing / matching with no children
 
120
[/a/]
 
121
check_signatures=check-available
 
122
gpg_signing_command=false
 
123
user_local_option=local
 
124
# test trailing / matching
 
125
[/a/*]
 
126
#subdirs will match but not the parent
 
127
[/a/c]
 
128
check_signatures=ignore
 
129
post_commit=bzrlib.tests.test_config.post_commit
 
130
#testing explicit beats globs
 
131
"""
82
132
 
83
133
 
84
134
class InstrumentedConfigObj(object):
98
148
    def __setitem__(self, key, value):
99
149
        self._calls.append(('__setitem__', key, value))
100
150
 
 
151
    def __delitem__(self, key):
 
152
        self._calls.append(('__delitem__', key))
 
153
 
 
154
    def keys(self):
 
155
        self._calls.append(('keys',))
 
156
        return []
 
157
 
 
158
    def reload(self):
 
159
        self._calls.append(('reload',))
 
160
 
101
161
    def write(self, arg):
102
162
        self._calls.append(('write',))
103
163
 
 
164
    def as_bool(self, value):
 
165
        self._calls.append(('as_bool', value))
 
166
        return False
 
167
 
 
168
    def get_value(self, section, name):
 
169
        self._calls.append(('get_value', section, name))
 
170
        return None
 
171
 
104
172
 
105
173
class FakeBranch(object):
106
174
 
109
177
            self.base = "http://example.com/branches/demo"
110
178
        else:
111
179
            self.base = base
112
 
        self.control_files = FakeControlFiles(user_id=user_id)
 
180
        self._transport = self.control_files = \
 
181
            FakeControlFilesAndTransport(user_id=user_id)
 
182
 
 
183
    def _get_config(self):
 
184
        return config.TransportConfig(self._transport, 'branch.conf')
113
185
 
114
186
    def lock_write(self):
115
187
        pass
118
190
        pass
119
191
 
120
192
 
121
 
class FakeControlFiles(object):
 
193
class FakeControlFilesAndTransport(object):
122
194
 
123
195
    def __init__(self, user_id=None):
124
 
        self.email = user_id
125
196
        self.files = {}
 
197
        if user_id:
 
198
            self.files['email'] = user_id
 
199
        self._transport = self
126
200
 
127
201
    def get_utf8(self, filename):
128
 
        if filename != 'email':
129
 
            raise NotImplementedError
130
 
        if self.email is not None:
131
 
            return StringIO(self.email)
132
 
        raise errors.NoSuchFile(filename)
 
202
        # from LockableFiles
 
203
        raise AssertionError("get_utf8 should no longer be used")
133
204
 
134
205
    def get(self, filename):
 
206
        # from Transport
135
207
        try:
136
208
            return StringIO(self.files[filename])
137
209
        except KeyError:
138
210
            raise errors.NoSuchFile(filename)
139
211
 
 
212
    def get_bytes(self, filename):
 
213
        # from Transport
 
214
        try:
 
215
            return self.files[filename]
 
216
        except KeyError:
 
217
            raise errors.NoSuchFile(filename)
 
218
 
140
219
    def put(self, filename, fileobj):
141
220
        self.files[filename] = fileobj.read()
142
221
 
 
222
    def put_file(self, filename, fileobj):
 
223
        return self.put(filename, fileobj)
 
224
 
143
225
 
144
226
class InstrumentedConfig(config.Config):
145
227
    """An instrumented config that supplies stubs for template methods."""
146
 
    
 
228
 
147
229
    def __init__(self):
148
230
        super(InstrumentedConfig, self).__init__()
149
231
        self._calls = []
157
239
        self._calls.append('_get_signature_checking')
158
240
        return self._signatures
159
241
 
 
242
    def _get_change_editor(self):
 
243
        self._calls.append('_get_change_editor')
 
244
        return 'vimdiff -fo @new_path @old_path'
 
245
 
160
246
 
161
247
bool_config = """[DEFAULT]
162
248
active = true
165
251
active = True
166
252
nonactive = False
167
253
"""
168
 
class TestConfigObj(TestCase):
 
254
 
 
255
 
 
256
class TestConfigObj(tests.TestCase):
 
257
 
169
258
    def test_get_bool(self):
170
 
        from bzrlib.config import ConfigObj
171
 
        co = ConfigObj(StringIO(bool_config))
 
259
        co = config.ConfigObj(StringIO(bool_config))
172
260
        self.assertIs(co.get_bool('DEFAULT', 'active'), True)
173
261
        self.assertIs(co.get_bool('DEFAULT', 'inactive'), False)
174
262
        self.assertIs(co.get_bool('UPPERCASE', 'active'), True)
175
263
        self.assertIs(co.get_bool('UPPERCASE', 'nonactive'), False)
176
264
 
177
 
 
178
 
class TestConfig(TestCase):
 
265
    def test_hash_sign_in_value(self):
 
266
        """
 
267
        Before 4.5.0, ConfigObj did not quote # signs in values, so they'd be
 
268
        treated as comments when read in again. (#86838)
 
269
        """
 
270
        co = config.ConfigObj()
 
271
        co['test'] = 'foo#bar'
 
272
        lines = co.write()
 
273
        self.assertEqual(lines, ['test = "foo#bar"'])
 
274
        co2 = config.ConfigObj(lines)
 
275
        self.assertEqual(co2['test'], 'foo#bar')
 
276
 
 
277
 
 
278
erroneous_config = """[section] # line 1
 
279
good=good # line 2
 
280
[section] # line 3
 
281
whocares=notme # line 4
 
282
"""
 
283
 
 
284
 
 
285
class TestConfigObjErrors(tests.TestCase):
 
286
 
 
287
    def test_duplicate_section_name_error_line(self):
 
288
        try:
 
289
            co = configobj.ConfigObj(StringIO(erroneous_config),
 
290
                                     raise_errors=True)
 
291
        except config.configobj.DuplicateError, e:
 
292
            self.assertEqual(3, e.line_number)
 
293
        else:
 
294
            self.fail('Error in config file not detected')
 
295
 
 
296
 
 
297
class TestConfig(tests.TestCase):
179
298
 
180
299
    def test_constructs(self):
181
300
        config.Config()
182
 
 
 
301
 
183
302
    def test_no_default_editor(self):
184
303
        self.assertRaises(NotImplementedError, config.Config().get_editor)
185
304
 
230
349
        my_config = config.Config()
231
350
        self.assertEqual('long', my_config.log_format())
232
351
 
233
 
 
234
 
class TestConfigPath(TestCase):
 
352
    def test_get_change_editor(self):
 
353
        my_config = InstrumentedConfig()
 
354
        change_editor = my_config.get_change_editor('old_tree', 'new_tree')
 
355
        self.assertEqual(['_get_change_editor'], my_config._calls)
 
356
        self.assertIs(diff.DiffFromTool, change_editor.__class__)
 
357
        self.assertEqual(['vimdiff', '-fo', '@new_path', '@old_path'],
 
358
                         change_editor.command_template)
 
359
 
 
360
 
 
361
class TestConfigPath(tests.TestCase):
235
362
 
236
363
    def setUp(self):
237
364
        super(TestConfigPath, self).setUp()
238
 
        self.old_home = os.environ.get('HOME', None)
239
 
        self.old_appdata = os.environ.get('APPDATA', None)
240
365
        os.environ['HOME'] = '/home/bogus'
241
 
        os.environ['APPDATA'] = \
242
 
            r'C:\Documents and Settings\bogus\Application Data'
 
366
        os.environ['XDG_CACHE_DIR'] = ''
 
367
        if sys.platform == 'win32':
 
368
            os.environ['BZR_HOME'] = \
 
369
                r'C:\Documents and Settings\bogus\Application Data'
 
370
            self.bzr_home = \
 
371
                'C:/Documents and Settings/bogus/Application Data/bazaar/2.0'
 
372
        else:
 
373
            self.bzr_home = '/home/bogus/.bazaar'
243
374
 
244
 
    def tearDown(self):
245
 
        if self.old_home is None:
246
 
            del os.environ['HOME']
247
 
        else:
248
 
            os.environ['HOME'] = self.old_home
249
 
        if self.old_appdata is None:
250
 
            del os.environ['APPDATA']
251
 
        else:
252
 
            os.environ['APPDATA'] = self.old_appdata
253
 
        super(TestConfigPath, self).tearDown()
254
 
    
255
375
    def test_config_dir(self):
256
 
        if sys.platform == 'win32':
257
 
            self.assertEqual(config.config_dir(), 
258
 
                'C:/Documents and Settings/bogus/Application Data/bazaar/2.0')
259
 
        else:
260
 
            self.assertEqual(config.config_dir(), '/home/bogus/.bazaar')
 
376
        self.assertEqual(config.config_dir(), self.bzr_home)
261
377
 
262
378
    def test_config_filename(self):
263
 
        if sys.platform == 'win32':
264
 
            self.assertEqual(config.config_filename(), 
265
 
                'C:/Documents and Settings/bogus/Application Data/bazaar/2.0/bazaar.conf')
266
 
        else:
267
 
            self.assertEqual(config.config_filename(),
268
 
                             '/home/bogus/.bazaar/bazaar.conf')
269
 
 
270
 
    def test_branches_config_filename(self):
271
 
        if sys.platform == 'win32':
272
 
            self.assertEqual(config.branches_config_filename(), 
273
 
                'C:/Documents and Settings/bogus/Application Data/bazaar/2.0/branches.conf')
274
 
        else:
275
 
            self.assertEqual(config.branches_config_filename(),
276
 
                             '/home/bogus/.bazaar/branches.conf')
 
379
        self.assertEqual(config.config_filename(),
 
380
                         self.bzr_home + '/bazaar.conf')
277
381
 
278
382
    def test_locations_config_filename(self):
279
 
        if sys.platform == 'win32':
280
 
            self.assertEqual(config.locations_config_filename(), 
281
 
                'C:/Documents and Settings/bogus/Application Data/bazaar/2.0/locations.conf')
282
 
        else:
283
 
            self.assertEqual(config.locations_config_filename(),
284
 
                             '/home/bogus/.bazaar/locations.conf')
285
 
 
286
 
class TestIniConfig(TestCase):
 
383
        self.assertEqual(config.locations_config_filename(),
 
384
                         self.bzr_home + '/locations.conf')
 
385
 
 
386
    def test_authentication_config_filename(self):
 
387
        self.assertEqual(config.authentication_config_filename(),
 
388
                         self.bzr_home + '/authentication.conf')
 
389
 
 
390
    def test_xdg_cache_dir(self):
 
391
        self.assertEqual(config.xdg_cache_dir(),
 
392
            '/home/bogus/.cache')
 
393
 
 
394
 
 
395
class TestIniConfig(tests.TestCaseInTempDir):
 
396
 
 
397
    def make_config_parser(self, s):
 
398
        conf = config.IniBasedConfig.from_string(s)
 
399
        return conf, conf._get_parser()
 
400
 
 
401
 
 
402
class TestIniConfigBuilding(TestIniConfig):
287
403
 
288
404
    def test_contructs(self):
289
 
        my_config = config.IniBasedConfig("nothing")
 
405
        my_config = config.IniBasedConfig()
290
406
 
291
407
    def test_from_fp(self):
292
 
        config_file = StringIO(sample_config_text.encode('utf-8'))
293
 
        my_config = config.IniBasedConfig(None)
294
 
        self.failUnless(
295
 
            isinstance(my_config._get_parser(file=config_file),
296
 
                        ConfigObj))
 
408
        my_config = config.IniBasedConfig.from_string(sample_config_text)
 
409
        self.assertIsInstance(my_config._get_parser(), configobj.ConfigObj)
297
410
 
298
411
    def test_cached(self):
299
 
        config_file = StringIO(sample_config_text.encode('utf-8'))
300
 
        my_config = config.IniBasedConfig(None)
301
 
        parser = my_config._get_parser(file=config_file)
 
412
        my_config = config.IniBasedConfig.from_string(sample_config_text)
 
413
        parser = my_config._get_parser()
302
414
        self.failUnless(my_config._get_parser() is parser)
303
415
 
304
 
 
305
 
class TestGetConfig(TestCase):
 
416
    def _dummy_chown(self, path, uid, gid):
 
417
        self.path, self.uid, self.gid = path, uid, gid
 
418
 
 
419
    def test_ini_config_ownership(self):
 
420
        """Ensure that chown is happening during _write_config_file"""
 
421
        self.requireFeature(features.chown_feature)
 
422
        self.overrideAttr(os, 'chown', self._dummy_chown)
 
423
        self.path = self.uid = self.gid = None
 
424
        conf = config.IniBasedConfig(file_name='./foo.conf')
 
425
        conf._write_config_file()
 
426
        self.assertEquals(self.path, './foo.conf')
 
427
        self.assertTrue(isinstance(self.uid, int))
 
428
        self.assertTrue(isinstance(self.gid, int))
 
429
 
 
430
    def test_get_filename_parameter_is_deprecated_(self):
 
431
        conf = self.callDeprecated([
 
432
            'IniBasedConfig.__init__(get_filename) was deprecated in 2.3.'
 
433
            ' Use file_name instead.'],
 
434
            config.IniBasedConfig, lambda: 'ini.conf')
 
435
        self.assertEqual('ini.conf', conf.file_name)
 
436
 
 
437
    def test_get_parser_file_parameter_is_deprecated_(self):
 
438
        config_file = StringIO(sample_config_text.encode('utf-8'))
 
439
        conf = config.IniBasedConfig.from_string(sample_config_text)
 
440
        conf = self.callDeprecated([
 
441
            'IniBasedConfig._get_parser(file=xxx) was deprecated in 2.3.'
 
442
            ' Use IniBasedConfig(_content=xxx) instead.'],
 
443
            conf._get_parser, file=config_file)
 
444
 
 
445
class TestIniConfigSaving(tests.TestCaseInTempDir):
 
446
 
 
447
    def test_cant_save_without_a_file_name(self):
 
448
        conf = config.IniBasedConfig()
 
449
        self.assertRaises(AssertionError, conf._write_config_file)
 
450
 
 
451
    def test_saved_with_content(self):
 
452
        content = 'foo = bar\n'
 
453
        conf = config.IniBasedConfig.from_string(
 
454
            content, file_name='./test.conf', save=True)
 
455
        self.assertFileEqual(content, 'test.conf')
 
456
 
 
457
 
 
458
class TestIniBaseConfigOnDisk(tests.TestCaseInTempDir):
 
459
 
 
460
    def test_cannot_reload_without_name(self):
 
461
        conf = config.IniBasedConfig.from_string(sample_config_text)
 
462
        self.assertRaises(AssertionError, conf.reload)
 
463
 
 
464
    def test_reload_see_new_value(self):
 
465
        c1 = config.IniBasedConfig.from_string('editor=vim\n',
 
466
                                               file_name='./test/conf')
 
467
        c1._write_config_file()
 
468
        c2 = config.IniBasedConfig.from_string('editor=emacs\n',
 
469
                                               file_name='./test/conf')
 
470
        c2._write_config_file()
 
471
        self.assertEqual('vim', c1.get_user_option('editor'))
 
472
        self.assertEqual('emacs', c2.get_user_option('editor'))
 
473
        # Make sure we get the Right value
 
474
        c1.reload()
 
475
        self.assertEqual('emacs', c1.get_user_option('editor'))
 
476
 
 
477
 
 
478
class TestLockableConfig(tests.TestCaseInTempDir):
 
479
 
 
480
    # Set by load_tests
 
481
    config_class = None
 
482
    config_args = None
 
483
    config_section = None
 
484
 
 
485
    def setUp(self):
 
486
        super(TestLockableConfig, self).setUp()
 
487
        self._content = '[%s]\none=1\ntwo=2\n' % (self.config_section,)
 
488
        self.config = self.create_config(self._content)
 
489
 
 
490
    def get_existing_config(self):
 
491
        return self.config_class(*self.config_args)
 
492
 
 
493
    def create_config(self, content):
 
494
        kwargs = dict(save=True)
 
495
        c = self.config_class.from_string(content, *self.config_args, **kwargs)
 
496
        return c
 
497
 
 
498
    def test_simple_read_access(self):
 
499
        self.assertEquals('1', self.config.get_user_option('one'))
 
500
 
 
501
    def test_simple_write_access(self):
 
502
        self.config.set_user_option('one', 'one')
 
503
        self.assertEquals('one', self.config.get_user_option('one'))
 
504
 
 
505
    def test_listen_to_the_last_speaker(self):
 
506
        c1 = self.config
 
507
        c2 = self.get_existing_config()
 
508
        c1.set_user_option('one', 'ONE')
 
509
        c2.set_user_option('two', 'TWO')
 
510
        self.assertEquals('ONE', c1.get_user_option('one'))
 
511
        self.assertEquals('TWO', c2.get_user_option('two'))
 
512
        # The second update respect the first one
 
513
        self.assertEquals('ONE', c2.get_user_option('one'))
 
514
 
 
515
    def test_last_speaker_wins(self):
 
516
        # If the same config is not shared, the same variable modified twice
 
517
        # can only see a single result.
 
518
        c1 = self.config
 
519
        c2 = self.get_existing_config()
 
520
        c1.set_user_option('one', 'c1')
 
521
        c2.set_user_option('one', 'c2')
 
522
        self.assertEquals('c2', c2._get_user_option('one'))
 
523
        # The first modification is still available until another refresh
 
524
        # occur
 
525
        self.assertEquals('c1', c1._get_user_option('one'))
 
526
        c1.set_user_option('two', 'done')
 
527
        self.assertEquals('c2', c1._get_user_option('one'))
 
528
 
 
529
    def test_writes_are_serialized(self):
 
530
        c1 = self.config
 
531
        c2 = self.get_existing_config()
 
532
 
 
533
        # We spawn a thread that will pause *during* the write
 
534
        before_writing = threading.Event()
 
535
        after_writing = threading.Event()
 
536
        writing_done = threading.Event()
 
537
        c1_orig = c1._write_config_file
 
538
        def c1_write_config_file():
 
539
            before_writing.set()
 
540
            c1_orig()
 
541
            # The lock is held we wait for the main thread to decide when to
 
542
            # continue
 
543
            after_writing.wait()
 
544
        c1._write_config_file = c1_write_config_file
 
545
        def c1_set_option():
 
546
            c1.set_user_option('one', 'c1')
 
547
            writing_done.set()
 
548
        t1 = threading.Thread(target=c1_set_option)
 
549
        # Collect the thread after the test
 
550
        self.addCleanup(t1.join)
 
551
        # Be ready to unblock the thread if the test goes wrong
 
552
        self.addCleanup(after_writing.set)
 
553
        t1.start()
 
554
        before_writing.wait()
 
555
        self.assertTrue(c1._lock.is_held)
 
556
        self.assertRaises(errors.LockContention,
 
557
                          c2.set_user_option, 'one', 'c2')
 
558
        self.assertEquals('c1', c1.get_user_option('one'))
 
559
        # Let the lock be released
 
560
        after_writing.set()
 
561
        writing_done.wait()
 
562
        c2.set_user_option('one', 'c2')
 
563
        self.assertEquals('c2', c2.get_user_option('one'))
 
564
 
 
565
    def test_read_while_writing(self):
 
566
       c1 = self.config
 
567
       # We spawn a thread that will pause *during* the write
 
568
       ready_to_write = threading.Event()
 
569
       do_writing = threading.Event()
 
570
       writing_done = threading.Event()
 
571
       c1_orig = c1._write_config_file
 
572
       def c1_write_config_file():
 
573
           ready_to_write.set()
 
574
           # The lock is held we wait for the main thread to decide when to
 
575
           # continue
 
576
           do_writing.wait()
 
577
           c1_orig()
 
578
           writing_done.set()
 
579
       c1._write_config_file = c1_write_config_file
 
580
       def c1_set_option():
 
581
           c1.set_user_option('one', 'c1')
 
582
       t1 = threading.Thread(target=c1_set_option)
 
583
       # Collect the thread after the test
 
584
       self.addCleanup(t1.join)
 
585
       # Be ready to unblock the thread if the test goes wrong
 
586
       self.addCleanup(do_writing.set)
 
587
       t1.start()
 
588
       # Ensure the thread is ready to write
 
589
       ready_to_write.wait()
 
590
       self.assertTrue(c1._lock.is_held)
 
591
       self.assertEquals('c1', c1.get_user_option('one'))
 
592
       # If we read during the write, we get the old value
 
593
       c2 = self.get_existing_config()
 
594
       self.assertEquals('1', c2.get_user_option('one'))
 
595
       # Let the writing occur and ensure it occurred
 
596
       do_writing.set()
 
597
       writing_done.wait()
 
598
       # Now we get the updated value
 
599
       c3 = self.get_existing_config()
 
600
       self.assertEquals('c1', c3.get_user_option('one'))
 
601
 
 
602
 
 
603
class TestGetUserOptionAs(TestIniConfig):
 
604
 
 
605
    def test_get_user_option_as_bool(self):
 
606
        conf, parser = self.make_config_parser("""
 
607
a_true_bool = true
 
608
a_false_bool = 0
 
609
an_invalid_bool = maybe
 
610
a_list = hmm, who knows ? # This is interpreted as a list !
 
611
""")
 
612
        get_bool = conf.get_user_option_as_bool
 
613
        self.assertEqual(True, get_bool('a_true_bool'))
 
614
        self.assertEqual(False, get_bool('a_false_bool'))
 
615
        warnings = []
 
616
        def warning(*args):
 
617
            warnings.append(args[0] % args[1:])
 
618
        self.overrideAttr(trace, 'warning', warning)
 
619
        msg = 'Value "%s" is not a boolean for "%s"'
 
620
        self.assertIs(None, get_bool('an_invalid_bool'))
 
621
        self.assertEquals(msg % ('maybe', 'an_invalid_bool'), warnings[0])
 
622
        warnings = []
 
623
        self.assertIs(None, get_bool('not_defined_in_this_config'))
 
624
        self.assertEquals([], warnings)
 
625
 
 
626
    def test_get_user_option_as_list(self):
 
627
        conf, parser = self.make_config_parser("""
 
628
a_list = a,b,c
 
629
length_1 = 1,
 
630
one_item = x
 
631
""")
 
632
        get_list = conf.get_user_option_as_list
 
633
        self.assertEqual(['a', 'b', 'c'], get_list('a_list'))
 
634
        self.assertEqual(['1'], get_list('length_1'))
 
635
        self.assertEqual('x', conf.get_user_option('one_item'))
 
636
        # automatically cast to list
 
637
        self.assertEqual(['x'], get_list('one_item'))
 
638
 
 
639
 
 
640
class TestSupressWarning(TestIniConfig):
 
641
 
 
642
    def make_warnings_config(self, s):
 
643
        conf, parser = self.make_config_parser(s)
 
644
        return conf.suppress_warning
 
645
 
 
646
    def test_suppress_warning_unknown(self):
 
647
        suppress_warning = self.make_warnings_config('')
 
648
        self.assertEqual(False, suppress_warning('unknown_warning'))
 
649
 
 
650
    def test_suppress_warning_known(self):
 
651
        suppress_warning = self.make_warnings_config('suppress_warnings=a,b')
 
652
        self.assertEqual(False, suppress_warning('c'))
 
653
        self.assertEqual(True, suppress_warning('a'))
 
654
        self.assertEqual(True, suppress_warning('b'))
 
655
 
 
656
 
 
657
class TestGetConfig(tests.TestCase):
306
658
 
307
659
    def test_constructs(self):
308
660
        my_config = config.GlobalConfig()
309
661
 
310
662
    def test_calls_read_filenames(self):
311
 
        # replace the class that is constructured, to check its parameters
 
663
        # replace the class that is constructed, to check its parameters
312
664
        oldparserclass = config.ConfigObj
313
665
        config.ConfigObj = InstrumentedConfigObj
314
666
        my_config = config.GlobalConfig()
321
673
                                          'utf-8')])
322
674
 
323
675
 
324
 
class TestBranchConfig(TestCaseWithTransport):
 
676
class TestBranchConfig(tests.TestCaseWithTransport):
325
677
 
326
678
    def test_constructs(self):
327
679
        branch = FakeBranch()
337
689
 
338
690
    def test_get_config(self):
339
691
        """The Branch.get_config method works properly"""
340
 
        b = BzrDir.create_standalone_workingtree('.').branch
 
692
        b = bzrdir.BzrDir.create_standalone_workingtree('.').branch
341
693
        my_config = b.get_config()
342
694
        self.assertIs(my_config.get_user_option('wacky'), None)
343
695
        my_config.set_user_option('wacky', 'unlikely')
344
696
        self.assertEqual(my_config.get_user_option('wacky'), 'unlikely')
345
697
 
346
698
        # Ensure we get the same thing if we start again
347
 
        b2 = Branch.open('.')
 
699
        b2 = branch.Branch.open('.')
348
700
        my_config2 = b2.get_config()
349
701
        self.assertEqual(my_config2.get_user_option('wacky'), 'unlikely')
350
702
 
354
706
        b.nick = 'foo'
355
707
        self.assertTrue(b.get_config().has_explicit_nickname())
356
708
 
357
 
 
358
 
class TestGlobalConfigItems(TestCase):
 
709
    def test_config_url(self):
 
710
        """The Branch.get_config will use section that uses a local url"""
 
711
        branch = self.make_branch('branch')
 
712
        self.assertEqual('branch', branch.nick)
 
713
 
 
714
        local_url = urlutils.local_path_to_url('branch')
 
715
        conf = config.LocationConfig.from_string(
 
716
            '[%s]\nnickname = foobar' % (local_url,),
 
717
            local_url, save=True)
 
718
        self.assertEqual('foobar', branch.nick)
 
719
 
 
720
    def test_config_local_path(self):
 
721
        """The Branch.get_config will use a local system path"""
 
722
        branch = self.make_branch('branch')
 
723
        self.assertEqual('branch', branch.nick)
 
724
 
 
725
        local_path = osutils.getcwd().encode('utf8')
 
726
        conf = config.LocationConfig.from_string(
 
727
            '[%s/branch]\nnickname = barry' % (local_path,),
 
728
            'branch',  save=True)
 
729
        self.assertEqual('barry', branch.nick)
 
730
 
 
731
    def test_config_creates_local(self):
 
732
        """Creating a new entry in config uses a local path."""
 
733
        branch = self.make_branch('branch', format='knit')
 
734
        branch.set_push_location('http://foobar')
 
735
        local_path = osutils.getcwd().encode('utf8')
 
736
        # Surprisingly ConfigObj doesn't create a trailing newline
 
737
        self.check_file_contents(config.locations_config_filename(),
 
738
                                 '[%s/branch]\n'
 
739
                                 'push_location = http://foobar\n'
 
740
                                 'push_location:policy = norecurse\n'
 
741
                                 % (local_path,))
 
742
 
 
743
    def test_autonick_urlencoded(self):
 
744
        b = self.make_branch('!repo')
 
745
        self.assertEqual('!repo', b.get_config().get_nickname())
 
746
 
 
747
    def test_warn_if_masked(self):
 
748
        warnings = []
 
749
        def warning(*args):
 
750
            warnings.append(args[0] % args[1:])
 
751
        self.overrideAttr(trace, 'warning', warning)
 
752
 
 
753
        def set_option(store, warn_masked=True):
 
754
            warnings[:] = []
 
755
            conf.set_user_option('example_option', repr(store), store=store,
 
756
                                 warn_masked=warn_masked)
 
757
        def assertWarning(warning):
 
758
            if warning is None:
 
759
                self.assertEqual(0, len(warnings))
 
760
            else:
 
761
                self.assertEqual(1, len(warnings))
 
762
                self.assertEqual(warning, warnings[0])
 
763
        branch = self.make_branch('.')
 
764
        conf = branch.get_config()
 
765
        set_option(config.STORE_GLOBAL)
 
766
        assertWarning(None)
 
767
        set_option(config.STORE_BRANCH)
 
768
        assertWarning(None)
 
769
        set_option(config.STORE_GLOBAL)
 
770
        assertWarning('Value "4" is masked by "3" from branch.conf')
 
771
        set_option(config.STORE_GLOBAL, warn_masked=False)
 
772
        assertWarning(None)
 
773
        set_option(config.STORE_LOCATION)
 
774
        assertWarning(None)
 
775
        set_option(config.STORE_BRANCH)
 
776
        assertWarning('Value "3" is masked by "0" from locations.conf')
 
777
        set_option(config.STORE_BRANCH, warn_masked=False)
 
778
        assertWarning(None)
 
779
 
 
780
 
 
781
class TestGlobalConfigItems(tests.TestCaseInTempDir):
359
782
 
360
783
    def test_user_id(self):
361
 
        config_file = StringIO(sample_config_text.encode('utf-8'))
362
 
        my_config = config.GlobalConfig()
363
 
        my_config._parser = my_config._get_parser(file=config_file)
 
784
        my_config = config.GlobalConfig.from_string(sample_config_text)
364
785
        self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
365
786
                         my_config._get_user_id())
366
787
 
367
788
    def test_absent_user_id(self):
368
 
        config_file = StringIO("")
369
789
        my_config = config.GlobalConfig()
370
 
        my_config._parser = my_config._get_parser(file=config_file)
371
790
        self.assertEqual(None, my_config._get_user_id())
372
791
 
373
792
    def test_configured_editor(self):
374
 
        config_file = StringIO(sample_config_text.encode('utf-8'))
375
 
        my_config = config.GlobalConfig()
376
 
        my_config._parser = my_config._get_parser(file=config_file)
 
793
        my_config = config.GlobalConfig.from_string(sample_config_text)
377
794
        self.assertEqual("vim", my_config.get_editor())
378
795
 
379
796
    def test_signatures_always(self):
380
 
        config_file = StringIO(sample_always_signatures)
381
 
        my_config = config.GlobalConfig()
382
 
        my_config._parser = my_config._get_parser(file=config_file)
 
797
        my_config = config.GlobalConfig.from_string(sample_always_signatures)
383
798
        self.assertEqual(config.CHECK_NEVER,
384
799
                         my_config.signature_checking())
385
800
        self.assertEqual(config.SIGN_ALWAYS,
387
802
        self.assertEqual(True, my_config.signature_needed())
388
803
 
389
804
    def test_signatures_if_possible(self):
390
 
        config_file = StringIO(sample_maybe_signatures)
391
 
        my_config = config.GlobalConfig()
392
 
        my_config._parser = my_config._get_parser(file=config_file)
 
805
        my_config = config.GlobalConfig.from_string(sample_maybe_signatures)
393
806
        self.assertEqual(config.CHECK_NEVER,
394
807
                         my_config.signature_checking())
395
808
        self.assertEqual(config.SIGN_WHEN_REQUIRED,
397
810
        self.assertEqual(False, my_config.signature_needed())
398
811
 
399
812
    def test_signatures_ignore(self):
400
 
        config_file = StringIO(sample_ignore_signatures)
401
 
        my_config = config.GlobalConfig()
402
 
        my_config._parser = my_config._get_parser(file=config_file)
 
813
        my_config = config.GlobalConfig.from_string(sample_ignore_signatures)
403
814
        self.assertEqual(config.CHECK_ALWAYS,
404
815
                         my_config.signature_checking())
405
816
        self.assertEqual(config.SIGN_NEVER,
407
818
        self.assertEqual(False, my_config.signature_needed())
408
819
 
409
820
    def _get_sample_config(self):
410
 
        config_file = StringIO(sample_config_text.encode('utf-8'))
411
 
        my_config = config.GlobalConfig()
412
 
        my_config._parser = my_config._get_parser(file=config_file)
 
821
        my_config = config.GlobalConfig.from_string(sample_config_text)
413
822
        return my_config
414
823
 
415
824
    def test_gpg_signing_command(self):
418
827
        self.assertEqual(False, my_config.signature_needed())
419
828
 
420
829
    def _get_empty_config(self):
421
 
        config_file = StringIO("")
422
830
        my_config = config.GlobalConfig()
423
 
        my_config._parser = my_config._get_parser(file=config_file)
424
831
        return my_config
425
832
 
426
833
    def test_gpg_signing_command_unset(self):
435
842
        my_config = self._get_sample_config()
436
843
        self.assertEqual("something",
437
844
                         my_config.get_user_option('user_global_option'))
438
 
        
 
845
 
439
846
    def test_post_commit_default(self):
440
847
        my_config = self._get_sample_config()
441
848
        self.assertEqual(None, my_config.post_commit())
448
855
        my_config = self._get_sample_config()
449
856
        self.assertEqual('help', my_config.get_alias('h'))
450
857
 
 
858
    def test_get_aliases(self):
 
859
        my_config = self._get_sample_config()
 
860
        aliases = my_config.get_aliases()
 
861
        self.assertEqual(2, len(aliases))
 
862
        sorted_keys = sorted(aliases)
 
863
        self.assertEqual('help', aliases[sorted_keys[0]])
 
864
        self.assertEqual(sample_long_alias, aliases[sorted_keys[1]])
 
865
 
451
866
    def test_get_no_alias(self):
452
867
        my_config = self._get_sample_config()
453
868
        self.assertEqual(None, my_config.get_alias('foo'))
456
871
        my_config = self._get_sample_config()
457
872
        self.assertEqual(sample_long_alias, my_config.get_alias('ll'))
458
873
 
459
 
 
460
 
class TestLocationConfig(TestCaseInTempDir):
 
874
    def test_get_change_editor(self):
 
875
        my_config = self._get_sample_config()
 
876
        change_editor = my_config.get_change_editor('old', 'new')
 
877
        self.assertIs(diff.DiffFromTool, change_editor.__class__)
 
878
        self.assertEqual('vimdiff -of @new_path @old_path',
 
879
                         ' '.join(change_editor.command_template))
 
880
 
 
881
    def test_get_no_change_editor(self):
 
882
        my_config = self._get_empty_config()
 
883
        change_editor = my_config.get_change_editor('old', 'new')
 
884
        self.assertIs(None, change_editor)
 
885
 
 
886
 
 
887
class TestGlobalConfigSavingOptions(tests.TestCaseInTempDir):
 
888
 
 
889
    def test_empty(self):
 
890
        my_config = config.GlobalConfig()
 
891
        self.assertEqual(0, len(my_config.get_aliases()))
 
892
 
 
893
    def test_set_alias(self):
 
894
        my_config = config.GlobalConfig()
 
895
        alias_value = 'commit --strict'
 
896
        my_config.set_alias('commit', alias_value)
 
897
        new_config = config.GlobalConfig()
 
898
        self.assertEqual(alias_value, new_config.get_alias('commit'))
 
899
 
 
900
    def test_remove_alias(self):
 
901
        my_config = config.GlobalConfig()
 
902
        my_config.set_alias('commit', 'commit --strict')
 
903
        # Now remove the alias again.
 
904
        my_config.unset_alias('commit')
 
905
        new_config = config.GlobalConfig()
 
906
        self.assertIs(None, new_config.get_alias('commit'))
 
907
 
 
908
 
 
909
class TestLocationConfig(tests.TestCaseInTempDir):
461
910
 
462
911
    def test_constructs(self):
463
912
        my_config = config.LocationConfig('http://example.com')
467
916
        # This is testing the correct file names are provided.
468
917
        # TODO: consolidate with the test for GlobalConfigs filename checks.
469
918
        #
470
 
        # replace the class that is constructured, to check its parameters
 
919
        # replace the class that is constructed, to check its parameters
471
920
        oldparserclass = config.ConfigObj
472
921
        config.ConfigObj = InstrumentedConfigObj
473
922
        try:
479
928
        self.assertEqual(parser._calls,
480
929
                         [('__init__', config.locations_config_filename(),
481
930
                           'utf-8')])
482
 
        config.ensure_config_dir_exists()
483
 
        #os.mkdir(config.config_dir())
484
 
        f = file(config.branches_config_filename(), 'wb')
485
 
        f.write('')
486
 
        f.close()
487
 
        oldparserclass = config.ConfigObj
488
 
        config.ConfigObj = InstrumentedConfigObj
489
 
        try:
490
 
            my_config = config.LocationConfig('http://www.example.com')
491
 
            parser = my_config._get_parser()
492
 
        finally:
493
 
            config.ConfigObj = oldparserclass
494
931
 
495
932
    def test_get_global_config(self):
496
933
        my_config = config.BranchConfig(FakeBranch('http://example.com'))
498
935
        self.failUnless(isinstance(global_config, config.GlobalConfig))
499
936
        self.failUnless(global_config is my_config._get_global_config())
500
937
 
501
 
    def test__get_section_no_match(self):
 
938
    def test__get_matching_sections_no_match(self):
502
939
        self.get_branch_config('/')
503
 
        self.assertEqual(None, self.my_location_config._get_section())
504
 
        
505
 
    def test__get_section_exact(self):
 
940
        self.assertEqual([], self.my_location_config._get_matching_sections())
 
941
 
 
942
    def test__get_matching_sections_exact(self):
506
943
        self.get_branch_config('http://www.example.com')
507
 
        self.assertEqual('http://www.example.com',
508
 
                         self.my_location_config._get_section())
509
 
   
510
 
    def test__get_section_suffix_does_not(self):
 
944
        self.assertEqual([('http://www.example.com', '')],
 
945
                         self.my_location_config._get_matching_sections())
 
946
 
 
947
    def test__get_matching_sections_suffix_does_not(self):
511
948
        self.get_branch_config('http://www.example.com-com')
512
 
        self.assertEqual(None, self.my_location_config._get_section())
 
949
        self.assertEqual([], self.my_location_config._get_matching_sections())
513
950
 
514
 
    def test__get_section_subdir_recursive(self):
 
951
    def test__get_matching_sections_subdir_recursive(self):
515
952
        self.get_branch_config('http://www.example.com/com')
516
 
        self.assertEqual('http://www.example.com',
517
 
                         self.my_location_config._get_section())
518
 
 
519
 
    def test__get_section_subdir_matches(self):
520
 
        self.get_branch_config('http://www.example.com/useglobal')
521
 
        self.assertEqual('http://www.example.com/useglobal',
522
 
                         self.my_location_config._get_section())
523
 
 
524
 
    def test__get_section_subdir_nonrecursive(self):
 
953
        self.assertEqual([('http://www.example.com', 'com')],
 
954
                         self.my_location_config._get_matching_sections())
 
955
 
 
956
    def test__get_matching_sections_ignoreparent(self):
 
957
        self.get_branch_config('http://www.example.com/ignoreparent')
 
958
        self.assertEqual([('http://www.example.com/ignoreparent', '')],
 
959
                         self.my_location_config._get_matching_sections())
 
960
 
 
961
    def test__get_matching_sections_ignoreparent_subdir(self):
525
962
        self.get_branch_config(
526
 
            'http://www.example.com/useglobal/childbranch')
527
 
        self.assertEqual('http://www.example.com',
528
 
                         self.my_location_config._get_section())
 
963
            'http://www.example.com/ignoreparent/childbranch')
 
964
        self.assertEqual([('http://www.example.com/ignoreparent',
 
965
                           'childbranch')],
 
966
                         self.my_location_config._get_matching_sections())
529
967
 
530
 
    def test__get_section_subdir_trailing_slash(self):
 
968
    def test__get_matching_sections_subdir_trailing_slash(self):
531
969
        self.get_branch_config('/b')
532
 
        self.assertEqual('/b/', self.my_location_config._get_section())
 
970
        self.assertEqual([('/b/', '')],
 
971
                         self.my_location_config._get_matching_sections())
533
972
 
534
 
    def test__get_section_subdir_child(self):
 
973
    def test__get_matching_sections_subdir_child(self):
535
974
        self.get_branch_config('/a/foo')
536
 
        self.assertEqual('/a/*', self.my_location_config._get_section())
 
975
        self.assertEqual([('/a/*', ''), ('/a/', 'foo')],
 
976
                         self.my_location_config._get_matching_sections())
537
977
 
538
 
    def test__get_section_subdir_child_child(self):
 
978
    def test__get_matching_sections_subdir_child_child(self):
539
979
        self.get_branch_config('/a/foo/bar')
540
 
        self.assertEqual('/a/', self.my_location_config._get_section())
 
980
        self.assertEqual([('/a/*', 'bar'), ('/a/', 'foo/bar')],
 
981
                         self.my_location_config._get_matching_sections())
541
982
 
542
 
    def test__get_section_trailing_slash_with_children(self):
 
983
    def test__get_matching_sections_trailing_slash_with_children(self):
543
984
        self.get_branch_config('/a/')
544
 
        self.assertEqual('/a/', self.my_location_config._get_section())
 
985
        self.assertEqual([('/a/', '')],
 
986
                         self.my_location_config._get_matching_sections())
545
987
 
546
 
    def test__get_section_explicit_over_glob(self):
 
988
    def test__get_matching_sections_explicit_over_glob(self):
 
989
        # XXX: 2006-09-08 jamesh
 
990
        # This test only passes because ord('c') > ord('*').  If there
 
991
        # was a config section for '/a/?', it would get precedence
 
992
        # over '/a/c'.
547
993
        self.get_branch_config('/a/c')
548
 
        self.assertEqual('/a/c', self.my_location_config._get_section())
549
 
 
 
994
        self.assertEqual([('/a/c', ''), ('/a/*', ''), ('/a/', 'c')],
 
995
                         self.my_location_config._get_matching_sections())
 
996
 
 
997
    def test__get_option_policy_normal(self):
 
998
        self.get_branch_config('http://www.example.com')
 
999
        self.assertEqual(
 
1000
            self.my_location_config._get_config_policy(
 
1001
            'http://www.example.com', 'normal_option'),
 
1002
            config.POLICY_NONE)
 
1003
 
 
1004
    def test__get_option_policy_norecurse(self):
 
1005
        self.get_branch_config('http://www.example.com')
 
1006
        self.assertEqual(
 
1007
            self.my_location_config._get_option_policy(
 
1008
            'http://www.example.com', 'norecurse_option'),
 
1009
            config.POLICY_NORECURSE)
 
1010
        # Test old recurse=False setting:
 
1011
        self.assertEqual(
 
1012
            self.my_location_config._get_option_policy(
 
1013
            'http://www.example.com/norecurse', 'normal_option'),
 
1014
            config.POLICY_NORECURSE)
 
1015
 
 
1016
    def test__get_option_policy_normal(self):
 
1017
        self.get_branch_config('http://www.example.com')
 
1018
        self.assertEqual(
 
1019
            self.my_location_config._get_option_policy(
 
1020
            'http://www.example.com', 'appendpath_option'),
 
1021
            config.POLICY_APPENDPATH)
550
1022
 
551
1023
    def test_location_without_username(self):
552
 
        self.get_branch_config('http://www.example.com/useglobal')
 
1024
        self.get_branch_config('http://www.example.com/ignoreparent')
553
1025
        self.assertEqual(u'Erik B\u00e5gfors <erik@bagfors.nu>',
554
1026
                         self.my_config.username())
555
1027
 
576
1048
        self.get_branch_config('/a/c')
577
1049
        self.assertEqual(config.CHECK_NEVER,
578
1050
                         self.my_config.signature_checking())
579
 
        
 
1051
 
580
1052
    def test_signatures_when_available(self):
581
1053
        self.get_branch_config('/a/', global_config=sample_ignore_signatures)
582
1054
        self.assertEqual(config.CHECK_IF_POSSIBLE,
583
1055
                         self.my_config.signature_checking())
584
 
        
 
1056
 
585
1057
    def test_signatures_always(self):
586
1058
        self.get_branch_config('/b')
587
1059
        self.assertEqual(config.CHECK_ALWAYS,
588
1060
                         self.my_config.signature_checking())
589
 
        
 
1061
 
590
1062
    def test_gpg_signing_command(self):
591
1063
        self.get_branch_config('/b')
592
1064
        self.assertEqual("gnome-gpg", self.my_config.gpg_signing_command())
604
1076
        self.get_branch_config('/a')
605
1077
        self.assertEqual('local',
606
1078
                         self.my_config.get_user_option('user_local_option'))
607
 
        
 
1079
 
 
1080
    def test_get_user_option_appendpath(self):
 
1081
        # returned as is for the base path:
 
1082
        self.get_branch_config('http://www.example.com')
 
1083
        self.assertEqual('append',
 
1084
                         self.my_config.get_user_option('appendpath_option'))
 
1085
        # Extra path components get appended:
 
1086
        self.get_branch_config('http://www.example.com/a/b/c')
 
1087
        self.assertEqual('append/a/b/c',
 
1088
                         self.my_config.get_user_option('appendpath_option'))
 
1089
        # Overriden for http://www.example.com/dir, where it is a
 
1090
        # normal option:
 
1091
        self.get_branch_config('http://www.example.com/dir/a/b/c')
 
1092
        self.assertEqual('normal',
 
1093
                         self.my_config.get_user_option('appendpath_option'))
 
1094
 
 
1095
    def test_get_user_option_norecurse(self):
 
1096
        self.get_branch_config('http://www.example.com')
 
1097
        self.assertEqual('norecurse',
 
1098
                         self.my_config.get_user_option('norecurse_option'))
 
1099
        self.get_branch_config('http://www.example.com/dir')
 
1100
        self.assertEqual(None,
 
1101
                         self.my_config.get_user_option('norecurse_option'))
 
1102
        # http://www.example.com/norecurse is a recurse=False section
 
1103
        # that redefines normal_option.  Subdirectories do not pick up
 
1104
        # this redefinition.
 
1105
        self.get_branch_config('http://www.example.com/norecurse')
 
1106
        self.assertEqual('norecurse',
 
1107
                         self.my_config.get_user_option('normal_option'))
 
1108
        self.get_branch_config('http://www.example.com/norecurse/subdir')
 
1109
        self.assertEqual('normal',
 
1110
                         self.my_config.get_user_option('normal_option'))
 
1111
 
 
1112
    def test_set_user_option_norecurse(self):
 
1113
        self.get_branch_config('http://www.example.com')
 
1114
        self.my_config.set_user_option('foo', 'bar',
 
1115
                                       store=config.STORE_LOCATION_NORECURSE)
 
1116
        self.assertEqual(
 
1117
            self.my_location_config._get_option_policy(
 
1118
            'http://www.example.com', 'foo'),
 
1119
            config.POLICY_NORECURSE)
 
1120
 
 
1121
    def test_set_user_option_appendpath(self):
 
1122
        self.get_branch_config('http://www.example.com')
 
1123
        self.my_config.set_user_option('foo', 'bar',
 
1124
                                       store=config.STORE_LOCATION_APPENDPATH)
 
1125
        self.assertEqual(
 
1126
            self.my_location_config._get_option_policy(
 
1127
            'http://www.example.com', 'foo'),
 
1128
            config.POLICY_APPENDPATH)
 
1129
 
 
1130
    def test_set_user_option_change_policy(self):
 
1131
        self.get_branch_config('http://www.example.com')
 
1132
        self.my_config.set_user_option('norecurse_option', 'normal',
 
1133
                                       store=config.STORE_LOCATION)
 
1134
        self.assertEqual(
 
1135
            self.my_location_config._get_option_policy(
 
1136
            'http://www.example.com', 'norecurse_option'),
 
1137
            config.POLICY_NONE)
 
1138
 
 
1139
    def test_set_user_option_recurse_false_section(self):
 
1140
        # The following section has recurse=False set.  The test is to
 
1141
        # make sure that a normal option can be added to the section,
 
1142
        # converting recurse=False to the norecurse policy.
 
1143
        self.get_branch_config('http://www.example.com/norecurse')
 
1144
        self.callDeprecated(['The recurse option is deprecated as of 0.14.  '
 
1145
                             'The section "http://www.example.com/norecurse" '
 
1146
                             'has been converted to use policies.'],
 
1147
                            self.my_config.set_user_option,
 
1148
                            'foo', 'bar', store=config.STORE_LOCATION)
 
1149
        self.assertEqual(
 
1150
            self.my_location_config._get_option_policy(
 
1151
            'http://www.example.com/norecurse', 'foo'),
 
1152
            config.POLICY_NONE)
 
1153
        # The previously existing option is still norecurse:
 
1154
        self.assertEqual(
 
1155
            self.my_location_config._get_option_policy(
 
1156
            'http://www.example.com/norecurse', 'normal_option'),
 
1157
            config.POLICY_NORECURSE)
 
1158
 
608
1159
    def test_post_commit_default(self):
609
1160
        self.get_branch_config('/a/c')
610
1161
        self.assertEqual('bzrlib.tests.test_config.post_commit',
611
1162
                         self.my_config.post_commit())
612
1163
 
613
1164
    def get_branch_config(self, location, global_config=None):
 
1165
        my_branch = FakeBranch(location)
614
1166
        if global_config is None:
615
 
            global_file = StringIO(sample_config_text.encode('utf-8'))
616
 
        else:
617
 
            global_file = StringIO(global_config.encode('utf-8'))
618
 
        branches_file = StringIO(sample_branches_text.encode('utf-8'))
619
 
        self.my_config = config.BranchConfig(FakeBranch(location))
620
 
        # Force location config to use specified file
621
 
        self.my_location_config = self.my_config._get_location_config()
622
 
        self.my_location_config._get_parser(branches_file)
623
 
        # Force global config to use specified file
624
 
        self.my_config._get_global_config()._get_parser(global_file)
 
1167
            global_config = sample_config_text
 
1168
 
 
1169
        my_global_config = config.GlobalConfig.from_string(global_config,
 
1170
                                                           save=True)
 
1171
        my_location_config = config.LocationConfig.from_string(
 
1172
            sample_branches_text, my_branch.base, save=True)
 
1173
        my_config = config.BranchConfig(my_branch)
 
1174
        self.my_config = my_config
 
1175
        self.my_location_config = my_config._get_location_config()
625
1176
 
626
1177
    def test_set_user_setting_sets_and_saves(self):
627
1178
        self.get_branch_config('/a/c')
628
1179
        record = InstrumentedConfigObj("foo")
629
1180
        self.my_location_config._parser = record
630
1181
 
631
 
        real_mkdir = os.mkdir
632
 
        self.created = False
633
 
        def checked_mkdir(path, mode=0777):
634
 
            self.log('making directory: %s', path)
635
 
            real_mkdir(path, mode)
636
 
            self.created = True
637
 
 
638
 
        os.mkdir = checked_mkdir
639
 
        try:
640
 
            self.my_config.set_user_option('foo', 'bar', local=True)
641
 
        finally:
642
 
            os.mkdir = real_mkdir
643
 
 
644
 
        self.failUnless(self.created, 'Failed to create ~/.bazaar')
645
 
        self.assertEqual([('__contains__', '/a/c'),
 
1182
        self.callDeprecated(['The recurse option is deprecated as of '
 
1183
                             '0.14.  The section "/a/c" has been '
 
1184
                             'converted to use policies.'],
 
1185
                            self.my_config.set_user_option,
 
1186
                            'foo', 'bar', store=config.STORE_LOCATION)
 
1187
        self.assertEqual([('reload',),
 
1188
                          ('__contains__', '/a/c'),
646
1189
                          ('__contains__', '/a/c/'),
647
1190
                          ('__setitem__', '/a/c', {}),
648
1191
                          ('__getitem__', '/a/c'),
649
1192
                          ('__setitem__', 'foo', 'bar'),
 
1193
                          ('__getitem__', '/a/c'),
 
1194
                          ('as_bool', 'recurse'),
 
1195
                          ('__getitem__', '/a/c'),
 
1196
                          ('__delitem__', 'recurse'),
 
1197
                          ('__getitem__', '/a/c'),
 
1198
                          ('keys',),
 
1199
                          ('__getitem__', '/a/c'),
 
1200
                          ('__contains__', 'foo:policy'),
650
1201
                          ('write',)],
651
1202
                         record._calls[1:])
652
1203
 
655
1206
        self.assertIs(self.my_config.get_user_option('foo'), None)
656
1207
        self.my_config.set_user_option('foo', 'bar')
657
1208
        self.assertEqual(
658
 
            self.my_config.branch.control_files.files['branch.conf'], 
 
1209
            self.my_config.branch.control_files.files['branch.conf'].strip(),
659
1210
            'foo = bar')
660
1211
        self.assertEqual(self.my_config.get_user_option('foo'), 'bar')
661
 
        self.my_config.set_user_option('foo', 'baz', local=True)
 
1212
        self.my_config.set_user_option('foo', 'baz',
 
1213
                                       store=config.STORE_LOCATION)
662
1214
        self.assertEqual(self.my_config.get_user_option('foo'), 'baz')
663
1215
        self.my_config.set_user_option('foo', 'qux')
664
1216
        self.assertEqual(self.my_config.get_user_option('foo'), 'baz')
665
 
        
 
1217
 
 
1218
    def test_get_bzr_remote_path(self):
 
1219
        my_config = config.LocationConfig('/a/c')
 
1220
        self.assertEqual('bzr', my_config.get_bzr_remote_path())
 
1221
        my_config.set_user_option('bzr_remote_path', '/path-bzr')
 
1222
        self.assertEqual('/path-bzr', my_config.get_bzr_remote_path())
 
1223
        os.environ['BZR_REMOTE_PATH'] = '/environ-bzr'
 
1224
        self.assertEqual('/environ-bzr', my_config.get_bzr_remote_path())
 
1225
 
666
1226
 
667
1227
precedence_global = 'option = global'
668
1228
precedence_branch = 'option = branch'
674
1234
option = exact
675
1235
"""
676
1236
 
677
 
 
678
 
class TestBranchConfigItems(TestCaseInTempDir):
679
 
 
680
 
    def get_branch_config(self, global_config=None, location=None, 
 
1237
class TestBranchConfigItems(tests.TestCaseInTempDir):
 
1238
 
 
1239
    def get_branch_config(self, global_config=None, location=None,
681
1240
                          location_config=None, branch_data_config=None):
682
 
        my_config = config.BranchConfig(FakeBranch(location))
 
1241
        my_branch = FakeBranch(location)
683
1242
        if global_config is not None:
684
 
            global_file = StringIO(global_config.encode('utf-8'))
685
 
            my_config._get_global_config()._get_parser(global_file)
686
 
        self.my_location_config = my_config._get_location_config()
 
1243
            my_global_config = config.GlobalConfig.from_string(global_config,
 
1244
                                                               save=True)
687
1245
        if location_config is not None:
688
 
            location_file = StringIO(location_config.encode('utf-8'))
689
 
            self.my_location_config._get_parser(location_file)
 
1246
            my_location_config = config.LocationConfig.from_string(
 
1247
                location_config, my_branch.base, save=True)
 
1248
        my_config = config.BranchConfig(my_branch)
690
1249
        if branch_data_config is not None:
691
1250
            my_config.branch.control_files.files['branch.conf'] = \
692
1251
                branch_data_config
697
1256
        my_config = config.BranchConfig(branch)
698
1257
        self.assertEqual("Robert Collins <robertc@example.net>",
699
1258
                         my_config.username())
700
 
        branch.control_files.email = "John"
701
 
        my_config.set_user_option('email', 
 
1259
        my_config.branch.control_files.files['email'] = "John"
 
1260
        my_config.set_user_option('email',
702
1261
                                  "Robert Collins <robertc@example.org>")
703
1262
        self.assertEqual("John", my_config.username())
704
 
        branch.control_files.email = None
 
1263
        del my_config.branch.control_files.files['email']
705
1264
        self.assertEqual("Robert Collins <robertc@example.org>",
706
1265
                         my_config.username())
707
1266
 
708
1267
    def test_not_set_in_branch(self):
709
 
        my_config = self.get_branch_config(sample_config_text)
710
 
        my_config.branch.control_files.email = None
 
1268
        my_config = self.get_branch_config(global_config=sample_config_text)
711
1269
        self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
712
1270
                         my_config._get_user_id())
713
 
        my_config.branch.control_files.email = "John"
 
1271
        my_config.branch.control_files.files['email'] = "John"
714
1272
        self.assertEqual("John", my_config._get_user_id())
715
1273
 
716
 
    def test_BZREMAIL_OVERRIDES(self):
717
 
        os.environ['BZREMAIL'] = "Robert Collins <robertc@example.org>"
 
1274
    def test_BZR_EMAIL_OVERRIDES(self):
 
1275
        os.environ['BZR_EMAIL'] = "Robert Collins <robertc@example.org>"
718
1276
        branch = FakeBranch()
719
1277
        my_config = config.BranchConfig(branch)
720
1278
        self.assertEqual("Robert Collins <robertc@example.org>",
721
1279
                         my_config.username())
722
 
    
 
1280
 
723
1281
    def test_signatures_forced(self):
724
1282
        my_config = self.get_branch_config(
725
1283
            global_config=sample_always_signatures)
737
1295
 
738
1296
    def test_gpg_signing_command(self):
739
1297
        my_config = self.get_branch_config(
 
1298
            global_config=sample_config_text,
740
1299
            # branch data cannot set gpg_signing_command
741
1300
            branch_data_config="gpg_signing_command=pgp")
742
 
        config_file = StringIO(sample_config_text.encode('utf-8'))
743
 
        my_config._get_global_config()._get_parser(config_file)
744
1301
        self.assertEqual('gnome-gpg', my_config.gpg_signing_command())
745
1302
 
746
1303
    def test_get_user_option_global(self):
747
 
        branch = FakeBranch()
748
 
        my_config = config.BranchConfig(branch)
749
 
        config_file = StringIO(sample_config_text.encode('utf-8'))
750
 
        (my_config._get_global_config()._get_parser(config_file))
 
1304
        my_config = self.get_branch_config(global_config=sample_config_text)
751
1305
        self.assertEqual('something',
752
1306
                         my_config.get_user_option('user_global_option'))
753
1307
 
754
1308
    def test_post_commit_default(self):
755
 
        branch = FakeBranch()
756
 
        my_config = self.get_branch_config(sample_config_text, '/a/c',
757
 
                                           sample_branches_text)
 
1309
        my_config = self.get_branch_config(global_config=sample_config_text,
 
1310
                                      location='/a/c',
 
1311
                                      location_config=sample_branches_text)
758
1312
        self.assertEqual(my_config.branch.base, '/a/c')
759
1313
        self.assertEqual('bzrlib.tests.test_config.post_commit',
760
1314
                         my_config.post_commit())
761
1315
        my_config.set_user_option('post_commit', 'rmtree_root')
762
 
        # post-commit is ignored when bresent in branch data
 
1316
        # post-commit is ignored when present in branch data
763
1317
        self.assertEqual('bzrlib.tests.test_config.post_commit',
764
1318
                         my_config.post_commit())
765
 
        my_config.set_user_option('post_commit', 'rmtree_root', local=True)
 
1319
        my_config.set_user_option('post_commit', 'rmtree_root',
 
1320
                                  store=config.STORE_LOCATION)
766
1321
        self.assertEqual('rmtree_root', my_config.post_commit())
767
1322
 
768
1323
    def test_config_precedence(self):
 
1324
        # FIXME: eager test, luckily no persitent config file makes it fail
 
1325
        # -- vila 20100716
769
1326
        my_config = self.get_branch_config(global_config=precedence_global)
770
1327
        self.assertEqual(my_config.get_user_option('option'), 'global')
771
 
        my_config = self.get_branch_config(global_config=precedence_global, 
772
 
                                      branch_data_config=precedence_branch)
 
1328
        my_config = self.get_branch_config(global_config=precedence_global,
 
1329
                                           branch_data_config=precedence_branch)
773
1330
        self.assertEqual(my_config.get_user_option('option'), 'branch')
774
 
        my_config = self.get_branch_config(global_config=precedence_global, 
775
 
                                      branch_data_config=precedence_branch,
776
 
                                      location_config=precedence_location)
 
1331
        my_config = self.get_branch_config(
 
1332
            global_config=precedence_global,
 
1333
            branch_data_config=precedence_branch,
 
1334
            location_config=precedence_location)
777
1335
        self.assertEqual(my_config.get_user_option('option'), 'recurse')
778
 
        my_config = self.get_branch_config(global_config=precedence_global, 
779
 
                                      branch_data_config=precedence_branch,
780
 
                                      location_config=precedence_location,
781
 
                                      location='http://example.com/specific')
 
1336
        my_config = self.get_branch_config(
 
1337
            global_config=precedence_global,
 
1338
            branch_data_config=precedence_branch,
 
1339
            location_config=precedence_location,
 
1340
            location='http://example.com/specific')
782
1341
        self.assertEqual(my_config.get_user_option('option'), 'exact')
783
1342
 
784
 
 
785
 
class TestMailAddressExtraction(TestCase):
 
1343
    def test_get_mail_client(self):
 
1344
        config = self.get_branch_config()
 
1345
        client = config.get_mail_client()
 
1346
        self.assertIsInstance(client, mail_client.DefaultMail)
 
1347
 
 
1348
        # Specific clients
 
1349
        config.set_user_option('mail_client', 'evolution')
 
1350
        client = config.get_mail_client()
 
1351
        self.assertIsInstance(client, mail_client.Evolution)
 
1352
 
 
1353
        config.set_user_option('mail_client', 'kmail')
 
1354
        client = config.get_mail_client()
 
1355
        self.assertIsInstance(client, mail_client.KMail)
 
1356
 
 
1357
        config.set_user_option('mail_client', 'mutt')
 
1358
        client = config.get_mail_client()
 
1359
        self.assertIsInstance(client, mail_client.Mutt)
 
1360
 
 
1361
        config.set_user_option('mail_client', 'thunderbird')
 
1362
        client = config.get_mail_client()
 
1363
        self.assertIsInstance(client, mail_client.Thunderbird)
 
1364
 
 
1365
        # Generic options
 
1366
        config.set_user_option('mail_client', 'default')
 
1367
        client = config.get_mail_client()
 
1368
        self.assertIsInstance(client, mail_client.DefaultMail)
 
1369
 
 
1370
        config.set_user_option('mail_client', 'editor')
 
1371
        client = config.get_mail_client()
 
1372
        self.assertIsInstance(client, mail_client.Editor)
 
1373
 
 
1374
        config.set_user_option('mail_client', 'mapi')
 
1375
        client = config.get_mail_client()
 
1376
        self.assertIsInstance(client, mail_client.MAPIClient)
 
1377
 
 
1378
        config.set_user_option('mail_client', 'xdg-email')
 
1379
        client = config.get_mail_client()
 
1380
        self.assertIsInstance(client, mail_client.XDGEmail)
 
1381
 
 
1382
        config.set_user_option('mail_client', 'firebird')
 
1383
        self.assertRaises(errors.UnknownMailClient, config.get_mail_client)
 
1384
 
 
1385
 
 
1386
class TestMailAddressExtraction(tests.TestCase):
786
1387
 
787
1388
    def test_extract_email_address(self):
788
1389
        self.assertEqual('jane@test.com',
789
1390
                         config.extract_email_address('Jane <jane@test.com>'))
790
 
        self.assertRaises(errors.BzrError,
 
1391
        self.assertRaises(errors.NoEmailInUsername,
791
1392
                          config.extract_email_address, 'Jane Tester')
 
1393
 
 
1394
    def test_parse_username(self):
 
1395
        self.assertEqual(('', 'jdoe@example.com'),
 
1396
                         config.parse_username('jdoe@example.com'))
 
1397
        self.assertEqual(('', 'jdoe@example.com'),
 
1398
                         config.parse_username('<jdoe@example.com>'))
 
1399
        self.assertEqual(('John Doe', 'jdoe@example.com'),
 
1400
                         config.parse_username('John Doe <jdoe@example.com>'))
 
1401
        self.assertEqual(('John Doe', ''),
 
1402
                         config.parse_username('John Doe'))
 
1403
        self.assertEqual(('John Doe', 'jdoe@example.com'),
 
1404
                         config.parse_username('John Doe jdoe@example.com'))
 
1405
 
 
1406
class TestTreeConfig(tests.TestCaseWithTransport):
 
1407
 
 
1408
    def test_get_value(self):
 
1409
        """Test that retreiving a value from a section is possible"""
 
1410
        branch = self.make_branch('.')
 
1411
        tree_config = config.TreeConfig(branch)
 
1412
        tree_config.set_option('value', 'key', 'SECTION')
 
1413
        tree_config.set_option('value2', 'key2')
 
1414
        tree_config.set_option('value3-top', 'key3')
 
1415
        tree_config.set_option('value3-section', 'key3', 'SECTION')
 
1416
        value = tree_config.get_option('key', 'SECTION')
 
1417
        self.assertEqual(value, 'value')
 
1418
        value = tree_config.get_option('key2')
 
1419
        self.assertEqual(value, 'value2')
 
1420
        self.assertEqual(tree_config.get_option('non-existant'), None)
 
1421
        value = tree_config.get_option('non-existant', 'SECTION')
 
1422
        self.assertEqual(value, None)
 
1423
        value = tree_config.get_option('non-existant', default='default')
 
1424
        self.assertEqual(value, 'default')
 
1425
        self.assertEqual(tree_config.get_option('key2', 'NOSECTION'), None)
 
1426
        value = tree_config.get_option('key2', 'NOSECTION', default='default')
 
1427
        self.assertEqual(value, 'default')
 
1428
        value = tree_config.get_option('key3')
 
1429
        self.assertEqual(value, 'value3-top')
 
1430
        value = tree_config.get_option('key3', 'SECTION')
 
1431
        self.assertEqual(value, 'value3-section')
 
1432
 
 
1433
 
 
1434
class TestTransportConfig(tests.TestCaseWithTransport):
 
1435
 
 
1436
    def test_get_value(self):
 
1437
        """Test that retreiving a value from a section is possible"""
 
1438
        bzrdir_config = config.TransportConfig(transport.get_transport('.'),
 
1439
                                               'control.conf')
 
1440
        bzrdir_config.set_option('value', 'key', 'SECTION')
 
1441
        bzrdir_config.set_option('value2', 'key2')
 
1442
        bzrdir_config.set_option('value3-top', 'key3')
 
1443
        bzrdir_config.set_option('value3-section', 'key3', 'SECTION')
 
1444
        value = bzrdir_config.get_option('key', 'SECTION')
 
1445
        self.assertEqual(value, 'value')
 
1446
        value = bzrdir_config.get_option('key2')
 
1447
        self.assertEqual(value, 'value2')
 
1448
        self.assertEqual(bzrdir_config.get_option('non-existant'), None)
 
1449
        value = bzrdir_config.get_option('non-existant', 'SECTION')
 
1450
        self.assertEqual(value, None)
 
1451
        value = bzrdir_config.get_option('non-existant', default='default')
 
1452
        self.assertEqual(value, 'default')
 
1453
        self.assertEqual(bzrdir_config.get_option('key2', 'NOSECTION'), None)
 
1454
        value = bzrdir_config.get_option('key2', 'NOSECTION',
 
1455
                                         default='default')
 
1456
        self.assertEqual(value, 'default')
 
1457
        value = bzrdir_config.get_option('key3')
 
1458
        self.assertEqual(value, 'value3-top')
 
1459
        value = bzrdir_config.get_option('key3', 'SECTION')
 
1460
        self.assertEqual(value, 'value3-section')
 
1461
 
 
1462
    def test_set_unset_default_stack_on(self):
 
1463
        my_dir = self.make_bzrdir('.')
 
1464
        bzrdir_config = config.BzrDirConfig(my_dir)
 
1465
        self.assertIs(None, bzrdir_config.get_default_stack_on())
 
1466
        bzrdir_config.set_default_stack_on('Foo')
 
1467
        self.assertEqual('Foo', bzrdir_config._config.get_option(
 
1468
                         'default_stack_on'))
 
1469
        self.assertEqual('Foo', bzrdir_config.get_default_stack_on())
 
1470
        bzrdir_config.set_default_stack_on(None)
 
1471
        self.assertIs(None, bzrdir_config.get_default_stack_on())
 
1472
 
 
1473
 
 
1474
def create_configs(test):
 
1475
    """Create configuration files for a given test.
 
1476
 
 
1477
    This requires creating a tree (and populate the ``test.tree`` attribute and
 
1478
    its associated branch and will populate the following attributes:
 
1479
 
 
1480
    - branch_config: A BranchConfig for the associated branch.
 
1481
 
 
1482
    - locations_config : A LocationConfig for the associated branch
 
1483
 
 
1484
    - bazaar_config: A GlobalConfig.
 
1485
 
 
1486
    The tree and branch are created in a 'tree' subdirectory so the tests can
 
1487
    still use the test directory to stay outside of the branch.
 
1488
    """
 
1489
    tree = test.make_branch_and_tree('tree')
 
1490
    test.tree = tree
 
1491
    test.branch_config = config.BranchConfig(tree.branch)
 
1492
    test.locations_config = config.LocationConfig(tree.basedir)
 
1493
    test.bazaar_config = config.GlobalConfig()
 
1494
 
 
1495
 
 
1496
def create_configs_with_file_option(test):
 
1497
    """Create configuration files with a ``file`` option set in each.
 
1498
 
 
1499
    This builds on ``create_configs`` and add one ``file`` option in each
 
1500
    configuration with a value which allows identifying the configuration file.
 
1501
    """
 
1502
    create_configs(test)
 
1503
    test.bazaar_config.set_user_option('file', 'bazaar')
 
1504
    test.locations_config.set_user_option('file', 'locations')
 
1505
    test.branch_config.set_user_option('file', 'branch')
 
1506
 
 
1507
 
 
1508
class TestConfigGetOptions(tests.TestCaseWithTransport):
 
1509
 
 
1510
    def setUp(self):
 
1511
        super(TestConfigGetOptions, self).setUp()
 
1512
        create_configs(self)
 
1513
 
 
1514
    def assertOptions(self, expected, conf):
 
1515
        actual = list(conf._get_options())
 
1516
        self.assertEqual(expected, actual)
 
1517
 
 
1518
    # One variable in none of the above
 
1519
    def test_no_variable(self):
 
1520
        # Using branch should query branch, locations and bazaar
 
1521
        self.assertOptions([], self.branch_config)
 
1522
 
 
1523
    def test_option_in_bazaar(self):
 
1524
        self.bazaar_config.set_user_option('file', 'bazaar')
 
1525
        self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
 
1526
                           self.bazaar_config)
 
1527
 
 
1528
    def test_option_in_locations(self):
 
1529
        self.locations_config.set_user_option('file', 'locations')
 
1530
        self.assertOptions(
 
1531
            [('file', 'locations', self.tree.basedir, 'locations')],
 
1532
            self.locations_config)
 
1533
 
 
1534
    def test_option_in_branch(self):
 
1535
        self.branch_config.set_user_option('file', 'branch')
 
1536
        self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
 
1537
                           self.branch_config)
 
1538
 
 
1539
    def test_option_in_bazaar_and_branch(self):
 
1540
        self.bazaar_config.set_user_option('file', 'bazaar')
 
1541
        self.branch_config.set_user_option('file', 'branch')
 
1542
        self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
 
1543
                            ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
 
1544
                           self.branch_config)
 
1545
 
 
1546
    def test_option_in_branch_and_locations(self):
 
1547
        # Hmm, locations override branch :-/
 
1548
        self.locations_config.set_user_option('file', 'locations')
 
1549
        self.branch_config.set_user_option('file', 'branch')
 
1550
        self.assertOptions(
 
1551
            [('file', 'locations', self.tree.basedir, 'locations'),
 
1552
             ('file', 'branch', 'DEFAULT', 'branch'),],
 
1553
            self.branch_config)
 
1554
 
 
1555
    def test_option_in_bazaar_locations_and_branch(self):
 
1556
        self.bazaar_config.set_user_option('file', 'bazaar')
 
1557
        self.locations_config.set_user_option('file', 'locations')
 
1558
        self.branch_config.set_user_option('file', 'branch')
 
1559
        self.assertOptions(
 
1560
            [('file', 'locations', self.tree.basedir, 'locations'),
 
1561
             ('file', 'branch', 'DEFAULT', 'branch'),
 
1562
             ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
 
1563
            self.branch_config)
 
1564
 
 
1565
 
 
1566
class TestConfigRemoveOption(tests.TestCaseWithTransport):
 
1567
 
 
1568
    def setUp(self):
 
1569
        super(TestConfigRemoveOption, self).setUp()
 
1570
        create_configs_with_file_option(self)
 
1571
 
 
1572
    def assertOptions(self, expected, conf):
 
1573
        actual = list(conf._get_options())
 
1574
        self.assertEqual(expected, actual)
 
1575
 
 
1576
    def test_remove_in_locations(self):
 
1577
        self.locations_config.remove_user_option('file', self.tree.basedir)
 
1578
        self.assertOptions(
 
1579
            [('file', 'branch', 'DEFAULT', 'branch'),
 
1580
             ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
 
1581
            self.branch_config)
 
1582
 
 
1583
    def test_remove_in_branch(self):
 
1584
        self.branch_config.remove_user_option('file')
 
1585
        self.assertOptions(
 
1586
            [('file', 'locations', self.tree.basedir, 'locations'),
 
1587
             ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
 
1588
            self.branch_config)
 
1589
 
 
1590
    def test_remove_in_bazaar(self):
 
1591
        self.bazaar_config.remove_user_option('file')
 
1592
        self.assertOptions(
 
1593
            [('file', 'locations', self.tree.basedir, 'locations'),
 
1594
             ('file', 'branch', 'DEFAULT', 'branch'),],
 
1595
            self.branch_config)
 
1596
 
 
1597
 
 
1598
class TestConfigGetSections(tests.TestCaseWithTransport):
 
1599
 
 
1600
    def setUp(self):
 
1601
        super(TestConfigGetSections, self).setUp()
 
1602
        create_configs(self)
 
1603
 
 
1604
    def assertSectionNames(self, expected, conf, name=None):
 
1605
        """Check which sections are returned for a given config.
 
1606
 
 
1607
        If fallback configurations exist their sections can be included.
 
1608
 
 
1609
        :param expected: A list of section names.
 
1610
 
 
1611
        :param conf: The configuration that will be queried.
 
1612
 
 
1613
        :param name: An optional section name that will be passed to
 
1614
            get_sections().
 
1615
        """
 
1616
        sections = list(conf._get_sections(name))
 
1617
        self.assertLength(len(expected), sections)
 
1618
        self.assertEqual(expected, [name for name, _, _ in sections])
 
1619
 
 
1620
    def test_bazaar_default_section(self):
 
1621
        self.assertSectionNames(['DEFAULT'], self.bazaar_config)
 
1622
 
 
1623
    def test_locations_default_section(self):
 
1624
        # No sections are defined in an empty file
 
1625
        self.assertSectionNames([], self.locations_config)
 
1626
 
 
1627
    def test_locations_named_section(self):
 
1628
        self.locations_config.set_user_option('file', 'locations')
 
1629
        self.assertSectionNames([self.tree.basedir], self.locations_config)
 
1630
 
 
1631
    def test_locations_matching_sections(self):
 
1632
        loc_config = self.locations_config
 
1633
        loc_config.set_user_option('file', 'locations')
 
1634
        # We need to cheat a bit here to create an option in sections above and
 
1635
        # below the 'location' one.
 
1636
        parser = loc_config._get_parser()
 
1637
        # locations.cong deals with '/' ignoring native os.sep
 
1638
        location_names = self.tree.basedir.split('/')
 
1639
        parent = '/'.join(location_names[:-1])
 
1640
        child = '/'.join(location_names + ['child'])
 
1641
        parser[parent] = {}
 
1642
        parser[parent]['file'] = 'parent'
 
1643
        parser[child] = {}
 
1644
        parser[child]['file'] = 'child'
 
1645
        self.assertSectionNames([self.tree.basedir, parent], loc_config)
 
1646
 
 
1647
    def test_branch_data_default_section(self):
 
1648
        self.assertSectionNames([None],
 
1649
                                self.branch_config._get_branch_data_config())
 
1650
 
 
1651
    def test_branch_default_sections(self):
 
1652
        # No sections are defined in an empty locations file
 
1653
        self.assertSectionNames([None, 'DEFAULT'],
 
1654
                                self.branch_config)
 
1655
        # Unless we define an option
 
1656
        self.branch_config._get_location_config().set_user_option(
 
1657
            'file', 'locations')
 
1658
        self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
 
1659
                                self.branch_config)
 
1660
 
 
1661
    def test_bazaar_named_section(self):
 
1662
        # We need to cheat as the API doesn't give direct access to sections
 
1663
        # other than DEFAULT.
 
1664
        self.bazaar_config.set_alias('bazaar', 'bzr')
 
1665
        self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
 
1666
 
 
1667
 
 
1668
class TestAuthenticationConfigFile(tests.TestCase):
 
1669
    """Test the authentication.conf file matching"""
 
1670
 
 
1671
    def _got_user_passwd(self, expected_user, expected_password,
 
1672
                         config, *args, **kwargs):
 
1673
        credentials = config.get_credentials(*args, **kwargs)
 
1674
        if credentials is None:
 
1675
            user = None
 
1676
            password = None
 
1677
        else:
 
1678
            user = credentials['user']
 
1679
            password = credentials['password']
 
1680
        self.assertEquals(expected_user, user)
 
1681
        self.assertEquals(expected_password, password)
 
1682
 
 
1683
    def test_empty_config(self):
 
1684
        conf = config.AuthenticationConfig(_file=StringIO())
 
1685
        self.assertEquals({}, conf._get_config())
 
1686
        self._got_user_passwd(None, None, conf, 'http', 'foo.net')
 
1687
 
 
1688
    def test_missing_auth_section_header(self):
 
1689
        conf = config.AuthenticationConfig(_file=StringIO('foo = bar'))
 
1690
        self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
 
1691
 
 
1692
    def test_auth_section_header_not_closed(self):
 
1693
        conf = config.AuthenticationConfig(_file=StringIO('[DEF'))
 
1694
        self.assertRaises(errors.ParseConfigError, conf._get_config)
 
1695
 
 
1696
    def test_auth_value_not_boolean(self):
 
1697
        conf = config.AuthenticationConfig(_file=StringIO(
 
1698
                """[broken]
 
1699
scheme=ftp
 
1700
user=joe
 
1701
verify_certificates=askme # Error: Not a boolean
 
1702
"""))
 
1703
        self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
 
1704
 
 
1705
    def test_auth_value_not_int(self):
 
1706
        conf = config.AuthenticationConfig(_file=StringIO(
 
1707
                """[broken]
 
1708
scheme=ftp
 
1709
user=joe
 
1710
port=port # Error: Not an int
 
1711
"""))
 
1712
        self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
 
1713
 
 
1714
    def test_unknown_password_encoding(self):
 
1715
        conf = config.AuthenticationConfig(_file=StringIO(
 
1716
                """[broken]
 
1717
scheme=ftp
 
1718
user=joe
 
1719
password_encoding=unknown
 
1720
"""))
 
1721
        self.assertRaises(ValueError, conf.get_password,
 
1722
                          'ftp', 'foo.net', 'joe')
 
1723
 
 
1724
    def test_credentials_for_scheme_host(self):
 
1725
        conf = config.AuthenticationConfig(_file=StringIO(
 
1726
                """# Identity on foo.net
 
1727
[ftp definition]
 
1728
scheme=ftp
 
1729
host=foo.net
 
1730
user=joe
 
1731
password=secret-pass
 
1732
"""))
 
1733
        # Basic matching
 
1734
        self._got_user_passwd('joe', 'secret-pass', conf, 'ftp', 'foo.net')
 
1735
        # different scheme
 
1736
        self._got_user_passwd(None, None, conf, 'http', 'foo.net')
 
1737
        # different host
 
1738
        self._got_user_passwd(None, None, conf, 'ftp', 'bar.net')
 
1739
 
 
1740
    def test_credentials_for_host_port(self):
 
1741
        conf = config.AuthenticationConfig(_file=StringIO(
 
1742
                """# Identity on foo.net
 
1743
[ftp definition]
 
1744
scheme=ftp
 
1745
port=10021
 
1746
host=foo.net
 
1747
user=joe
 
1748
password=secret-pass
 
1749
"""))
 
1750
        # No port
 
1751
        self._got_user_passwd('joe', 'secret-pass',
 
1752
                              conf, 'ftp', 'foo.net', port=10021)
 
1753
        # different port
 
1754
        self._got_user_passwd(None, None, conf, 'ftp', 'foo.net')
 
1755
 
 
1756
    def test_for_matching_host(self):
 
1757
        conf = config.AuthenticationConfig(_file=StringIO(
 
1758
                """# Identity on foo.net
 
1759
[sourceforge]
 
1760
scheme=bzr
 
1761
host=bzr.sf.net
 
1762
user=joe
 
1763
password=joepass
 
1764
[sourceforge domain]
 
1765
scheme=bzr
 
1766
host=.bzr.sf.net
 
1767
user=georges
 
1768
password=bendover
 
1769
"""))
 
1770
        # matching domain
 
1771
        self._got_user_passwd('georges', 'bendover',
 
1772
                              conf, 'bzr', 'foo.bzr.sf.net')
 
1773
        # phishing attempt
 
1774
        self._got_user_passwd(None, None,
 
1775
                              conf, 'bzr', 'bbzr.sf.net')
 
1776
 
 
1777
    def test_for_matching_host_None(self):
 
1778
        conf = config.AuthenticationConfig(_file=StringIO(
 
1779
                """# Identity on foo.net
 
1780
[catchup bzr]
 
1781
scheme=bzr
 
1782
user=joe
 
1783
password=joepass
 
1784
[DEFAULT]
 
1785
user=georges
 
1786
password=bendover
 
1787
"""))
 
1788
        # match no host
 
1789
        self._got_user_passwd('joe', 'joepass',
 
1790
                              conf, 'bzr', 'quux.net')
 
1791
        # no host but different scheme
 
1792
        self._got_user_passwd('georges', 'bendover',
 
1793
                              conf, 'ftp', 'quux.net')
 
1794
 
 
1795
    def test_credentials_for_path(self):
 
1796
        conf = config.AuthenticationConfig(_file=StringIO(
 
1797
                """
 
1798
[http dir1]
 
1799
scheme=http
 
1800
host=bar.org
 
1801
path=/dir1
 
1802
user=jim
 
1803
password=jimpass
 
1804
[http dir2]
 
1805
scheme=http
 
1806
host=bar.org
 
1807
path=/dir2
 
1808
user=georges
 
1809
password=bendover
 
1810
"""))
 
1811
        # no path no dice
 
1812
        self._got_user_passwd(None, None,
 
1813
                              conf, 'http', host='bar.org', path='/dir3')
 
1814
        # matching path
 
1815
        self._got_user_passwd('georges', 'bendover',
 
1816
                              conf, 'http', host='bar.org', path='/dir2')
 
1817
        # matching subdir
 
1818
        self._got_user_passwd('jim', 'jimpass',
 
1819
                              conf, 'http', host='bar.org',path='/dir1/subdir')
 
1820
 
 
1821
    def test_credentials_for_user(self):
 
1822
        conf = config.AuthenticationConfig(_file=StringIO(
 
1823
                """
 
1824
[with user]
 
1825
scheme=http
 
1826
host=bar.org
 
1827
user=jim
 
1828
password=jimpass
 
1829
"""))
 
1830
        # Get user
 
1831
        self._got_user_passwd('jim', 'jimpass',
 
1832
                              conf, 'http', 'bar.org')
 
1833
        # Get same user
 
1834
        self._got_user_passwd('jim', 'jimpass',
 
1835
                              conf, 'http', 'bar.org', user='jim')
 
1836
        # Don't get a different user if one is specified
 
1837
        self._got_user_passwd(None, None,
 
1838
                              conf, 'http', 'bar.org', user='georges')
 
1839
 
 
1840
    def test_credentials_for_user_without_password(self):
 
1841
        conf = config.AuthenticationConfig(_file=StringIO(
 
1842
                """
 
1843
[without password]
 
1844
scheme=http
 
1845
host=bar.org
 
1846
user=jim
 
1847
"""))
 
1848
        # Get user but no password
 
1849
        self._got_user_passwd('jim', None,
 
1850
                              conf, 'http', 'bar.org')
 
1851
 
 
1852
    def test_verify_certificates(self):
 
1853
        conf = config.AuthenticationConfig(_file=StringIO(
 
1854
                """
 
1855
[self-signed]
 
1856
scheme=https
 
1857
host=bar.org
 
1858
user=jim
 
1859
password=jimpass
 
1860
verify_certificates=False
 
1861
[normal]
 
1862
scheme=https
 
1863
host=foo.net
 
1864
user=georges
 
1865
password=bendover
 
1866
"""))
 
1867
        credentials = conf.get_credentials('https', 'bar.org')
 
1868
        self.assertEquals(False, credentials.get('verify_certificates'))
 
1869
        credentials = conf.get_credentials('https', 'foo.net')
 
1870
        self.assertEquals(True, credentials.get('verify_certificates'))
 
1871
 
 
1872
 
 
1873
class TestAuthenticationStorage(tests.TestCaseInTempDir):
 
1874
 
 
1875
    def test_set_credentials(self):
 
1876
        conf = config.AuthenticationConfig()
 
1877
        conf.set_credentials('name', 'host', 'user', 'scheme', 'password',
 
1878
        99, path='/foo', verify_certificates=False, realm='realm')
 
1879
        credentials = conf.get_credentials(host='host', scheme='scheme',
 
1880
                                           port=99, path='/foo',
 
1881
                                           realm='realm')
 
1882
        CREDENTIALS = {'name': 'name', 'user': 'user', 'password': 'password',
 
1883
                       'verify_certificates': False, 'scheme': 'scheme', 
 
1884
                       'host': 'host', 'port': 99, 'path': '/foo', 
 
1885
                       'realm': 'realm'}
 
1886
        self.assertEqual(CREDENTIALS, credentials)
 
1887
        credentials_from_disk = config.AuthenticationConfig().get_credentials(
 
1888
            host='host', scheme='scheme', port=99, path='/foo', realm='realm')
 
1889
        self.assertEqual(CREDENTIALS, credentials_from_disk)
 
1890
 
 
1891
    def test_reset_credentials_different_name(self):
 
1892
        conf = config.AuthenticationConfig()
 
1893
        conf.set_credentials('name', 'host', 'user', 'scheme', 'password'),
 
1894
        conf.set_credentials('name2', 'host', 'user2', 'scheme', 'password'),
 
1895
        self.assertIs(None, conf._get_config().get('name'))
 
1896
        credentials = conf.get_credentials(host='host', scheme='scheme')
 
1897
        CREDENTIALS = {'name': 'name2', 'user': 'user2', 'password':
 
1898
                       'password', 'verify_certificates': True, 
 
1899
                       'scheme': 'scheme', 'host': 'host', 'port': None, 
 
1900
                       'path': None, 'realm': None}
 
1901
        self.assertEqual(CREDENTIALS, credentials)
 
1902
 
 
1903
 
 
1904
class TestAuthenticationConfig(tests.TestCase):
 
1905
    """Test AuthenticationConfig behaviour"""
 
1906
 
 
1907
    def _check_default_password_prompt(self, expected_prompt_format, scheme,
 
1908
                                       host=None, port=None, realm=None,
 
1909
                                       path=None):
 
1910
        if host is None:
 
1911
            host = 'bar.org'
 
1912
        user, password = 'jim', 'precious'
 
1913
        expected_prompt = expected_prompt_format % {
 
1914
            'scheme': scheme, 'host': host, 'port': port,
 
1915
            'user': user, 'realm': realm}
 
1916
 
 
1917
        stdout = tests.StringIOWrapper()
 
1918
        stderr = tests.StringIOWrapper()
 
1919
        ui.ui_factory = tests.TestUIFactory(stdin=password + '\n',
 
1920
                                            stdout=stdout, stderr=stderr)
 
1921
        # We use an empty conf so that the user is always prompted
 
1922
        conf = config.AuthenticationConfig()
 
1923
        self.assertEquals(password,
 
1924
                          conf.get_password(scheme, host, user, port=port,
 
1925
                                            realm=realm, path=path))
 
1926
        self.assertEquals(expected_prompt, stderr.getvalue())
 
1927
        self.assertEquals('', stdout.getvalue())
 
1928
 
 
1929
    def _check_default_username_prompt(self, expected_prompt_format, scheme,
 
1930
                                       host=None, port=None, realm=None,
 
1931
                                       path=None):
 
1932
        if host is None:
 
1933
            host = 'bar.org'
 
1934
        username = 'jim'
 
1935
        expected_prompt = expected_prompt_format % {
 
1936
            'scheme': scheme, 'host': host, 'port': port,
 
1937
            'realm': realm}
 
1938
        stdout = tests.StringIOWrapper()
 
1939
        stderr = tests.StringIOWrapper()
 
1940
        ui.ui_factory = tests.TestUIFactory(stdin=username+ '\n',
 
1941
                                            stdout=stdout, stderr=stderr)
 
1942
        # We use an empty conf so that the user is always prompted
 
1943
        conf = config.AuthenticationConfig()
 
1944
        self.assertEquals(username, conf.get_user(scheme, host, port=port,
 
1945
                          realm=realm, path=path, ask=True))
 
1946
        self.assertEquals(expected_prompt, stderr.getvalue())
 
1947
        self.assertEquals('', stdout.getvalue())
 
1948
 
 
1949
    def test_username_defaults_prompts(self):
 
1950
        # HTTP prompts can't be tested here, see test_http.py
 
1951
        self._check_default_username_prompt('FTP %(host)s username: ', 'ftp')
 
1952
        self._check_default_username_prompt(
 
1953
            'FTP %(host)s:%(port)d username: ', 'ftp', port=10020)
 
1954
        self._check_default_username_prompt(
 
1955
            'SSH %(host)s:%(port)d username: ', 'ssh', port=12345)
 
1956
 
 
1957
    def test_username_default_no_prompt(self):
 
1958
        conf = config.AuthenticationConfig()
 
1959
        self.assertEquals(None,
 
1960
            conf.get_user('ftp', 'example.com'))
 
1961
        self.assertEquals("explicitdefault",
 
1962
            conf.get_user('ftp', 'example.com', default="explicitdefault"))
 
1963
 
 
1964
    def test_password_default_prompts(self):
 
1965
        # HTTP prompts can't be tested here, see test_http.py
 
1966
        self._check_default_password_prompt(
 
1967
            'FTP %(user)s@%(host)s password: ', 'ftp')
 
1968
        self._check_default_password_prompt(
 
1969
            'FTP %(user)s@%(host)s:%(port)d password: ', 'ftp', port=10020)
 
1970
        self._check_default_password_prompt(
 
1971
            'SSH %(user)s@%(host)s:%(port)d password: ', 'ssh', port=12345)
 
1972
        # SMTP port handling is a bit special (it's handled if embedded in the
 
1973
        # host too)
 
1974
        # FIXME: should we: forbid that, extend it to other schemes, leave
 
1975
        # things as they are that's fine thank you ?
 
1976
        self._check_default_password_prompt('SMTP %(user)s@%(host)s password: ',
 
1977
                                            'smtp')
 
1978
        self._check_default_password_prompt('SMTP %(user)s@%(host)s password: ',
 
1979
                                            'smtp', host='bar.org:10025')
 
1980
        self._check_default_password_prompt(
 
1981
            'SMTP %(user)s@%(host)s:%(port)d password: ',
 
1982
            'smtp', port=10025)
 
1983
 
 
1984
    def test_ssh_password_emits_warning(self):
 
1985
        conf = config.AuthenticationConfig(_file=StringIO(
 
1986
                """
 
1987
[ssh with password]
 
1988
scheme=ssh
 
1989
host=bar.org
 
1990
user=jim
 
1991
password=jimpass
 
1992
"""))
 
1993
        entered_password = 'typed-by-hand'
 
1994
        stdout = tests.StringIOWrapper()
 
1995
        stderr = tests.StringIOWrapper()
 
1996
        ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
 
1997
                                            stdout=stdout, stderr=stderr)
 
1998
 
 
1999
        # Since the password defined in the authentication config is ignored,
 
2000
        # the user is prompted
 
2001
        self.assertEquals(entered_password,
 
2002
                          conf.get_password('ssh', 'bar.org', user='jim'))
 
2003
        self.assertContainsRe(
 
2004
            self.get_log(),
 
2005
            'password ignored in section \[ssh with password\]')
 
2006
 
 
2007
    def test_ssh_without_password_doesnt_emit_warning(self):
 
2008
        conf = config.AuthenticationConfig(_file=StringIO(
 
2009
                """
 
2010
[ssh with password]
 
2011
scheme=ssh
 
2012
host=bar.org
 
2013
user=jim
 
2014
"""))
 
2015
        entered_password = 'typed-by-hand'
 
2016
        stdout = tests.StringIOWrapper()
 
2017
        stderr = tests.StringIOWrapper()
 
2018
        ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
 
2019
                                            stdout=stdout,
 
2020
                                            stderr=stderr)
 
2021
 
 
2022
        # Since the password defined in the authentication config is ignored,
 
2023
        # the user is prompted
 
2024
        self.assertEquals(entered_password,
 
2025
                          conf.get_password('ssh', 'bar.org', user='jim'))
 
2026
        # No warning shoud be emitted since there is no password. We are only
 
2027
        # providing "user".
 
2028
        self.assertNotContainsRe(
 
2029
            self.get_log(),
 
2030
            'password ignored in section \[ssh with password\]')
 
2031
 
 
2032
    def test_uses_fallback_stores(self):
 
2033
        self.overrideAttr(config, 'credential_store_registry',
 
2034
                          config.CredentialStoreRegistry())
 
2035
        store = StubCredentialStore()
 
2036
        store.add_credentials("http", "example.com", "joe", "secret")
 
2037
        config.credential_store_registry.register("stub", store, fallback=True)
 
2038
        conf = config.AuthenticationConfig(_file=StringIO())
 
2039
        creds = conf.get_credentials("http", "example.com")
 
2040
        self.assertEquals("joe", creds["user"])
 
2041
        self.assertEquals("secret", creds["password"])
 
2042
 
 
2043
 
 
2044
class StubCredentialStore(config.CredentialStore):
 
2045
 
 
2046
    def __init__(self):
 
2047
        self._username = {}
 
2048
        self._password = {}
 
2049
 
 
2050
    def add_credentials(self, scheme, host, user, password=None):
 
2051
        self._username[(scheme, host)] = user
 
2052
        self._password[(scheme, host)] = password
 
2053
 
 
2054
    def get_credentials(self, scheme, host, port=None, user=None,
 
2055
        path=None, realm=None):
 
2056
        key = (scheme, host)
 
2057
        if not key in self._username:
 
2058
            return None
 
2059
        return { "scheme": scheme, "host": host, "port": port,
 
2060
                "user": self._username[key], "password": self._password[key]}
 
2061
 
 
2062
 
 
2063
class CountingCredentialStore(config.CredentialStore):
 
2064
 
 
2065
    def __init__(self):
 
2066
        self._calls = 0
 
2067
 
 
2068
    def get_credentials(self, scheme, host, port=None, user=None,
 
2069
        path=None, realm=None):
 
2070
        self._calls += 1
 
2071
        return None
 
2072
 
 
2073
 
 
2074
class TestCredentialStoreRegistry(tests.TestCase):
 
2075
 
 
2076
    def _get_cs_registry(self):
 
2077
        return config.credential_store_registry
 
2078
 
 
2079
    def test_default_credential_store(self):
 
2080
        r = self._get_cs_registry()
 
2081
        default = r.get_credential_store(None)
 
2082
        self.assertIsInstance(default, config.PlainTextCredentialStore)
 
2083
 
 
2084
    def test_unknown_credential_store(self):
 
2085
        r = self._get_cs_registry()
 
2086
        # It's hard to imagine someone creating a credential store named
 
2087
        # 'unknown' so we use that as an never registered key.
 
2088
        self.assertRaises(KeyError, r.get_credential_store, 'unknown')
 
2089
 
 
2090
    def test_fallback_none_registered(self):
 
2091
        r = config.CredentialStoreRegistry()
 
2092
        self.assertEquals(None,
 
2093
                          r.get_fallback_credentials("http", "example.com"))
 
2094
 
 
2095
    def test_register(self):
 
2096
        r = config.CredentialStoreRegistry()
 
2097
        r.register("stub", StubCredentialStore(), fallback=False)
 
2098
        r.register("another", StubCredentialStore(), fallback=True)
 
2099
        self.assertEquals(["another", "stub"], r.keys())
 
2100
 
 
2101
    def test_register_lazy(self):
 
2102
        r = config.CredentialStoreRegistry()
 
2103
        r.register_lazy("stub", "bzrlib.tests.test_config",
 
2104
                        "StubCredentialStore", fallback=False)
 
2105
        self.assertEquals(["stub"], r.keys())
 
2106
        self.assertIsInstance(r.get_credential_store("stub"),
 
2107
                              StubCredentialStore)
 
2108
 
 
2109
    def test_is_fallback(self):
 
2110
        r = config.CredentialStoreRegistry()
 
2111
        r.register("stub1", None, fallback=False)
 
2112
        r.register("stub2", None, fallback=True)
 
2113
        self.assertEquals(False, r.is_fallback("stub1"))
 
2114
        self.assertEquals(True, r.is_fallback("stub2"))
 
2115
 
 
2116
    def test_no_fallback(self):
 
2117
        r = config.CredentialStoreRegistry()
 
2118
        store = CountingCredentialStore()
 
2119
        r.register("count", store, fallback=False)
 
2120
        self.assertEquals(None,
 
2121
                          r.get_fallback_credentials("http", "example.com"))
 
2122
        self.assertEquals(0, store._calls)
 
2123
 
 
2124
    def test_fallback_credentials(self):
 
2125
        r = config.CredentialStoreRegistry()
 
2126
        store = StubCredentialStore()
 
2127
        store.add_credentials("http", "example.com",
 
2128
                              "somebody", "geheim")
 
2129
        r.register("stub", store, fallback=True)
 
2130
        creds = r.get_fallback_credentials("http", "example.com")
 
2131
        self.assertEquals("somebody", creds["user"])
 
2132
        self.assertEquals("geheim", creds["password"])
 
2133
 
 
2134
    def test_fallback_first_wins(self):
 
2135
        r = config.CredentialStoreRegistry()
 
2136
        stub1 = StubCredentialStore()
 
2137
        stub1.add_credentials("http", "example.com",
 
2138
                              "somebody", "stub1")
 
2139
        r.register("stub1", stub1, fallback=True)
 
2140
        stub2 = StubCredentialStore()
 
2141
        stub2.add_credentials("http", "example.com",
 
2142
                              "somebody", "stub2")
 
2143
        r.register("stub2", stub1, fallback=True)
 
2144
        creds = r.get_fallback_credentials("http", "example.com")
 
2145
        self.assertEquals("somebody", creds["user"])
 
2146
        self.assertEquals("stub1", creds["password"])
 
2147
 
 
2148
 
 
2149
class TestPlainTextCredentialStore(tests.TestCase):
 
2150
 
 
2151
    def test_decode_password(self):
 
2152
        r = config.credential_store_registry
 
2153
        plain_text = r.get_credential_store()
 
2154
        decoded = plain_text.decode_password(dict(password='secret'))
 
2155
        self.assertEquals('secret', decoded)
 
2156
 
 
2157
 
 
2158
# FIXME: Once we have a way to declare authentication to all test servers, we
 
2159
# can implement generic tests.
 
2160
# test_user_password_in_url
 
2161
# test_user_in_url_password_from_config
 
2162
# test_user_in_url_password_prompted
 
2163
# test_user_in_config
 
2164
# test_user_getpass.getuser
 
2165
# test_user_prompted ?
 
2166
class TestAuthenticationRing(tests.TestCaseWithTransport):
 
2167
    pass