~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_config.py

  • Committer: John Arbash Meinel
  • Date: 2006-08-25 22:19:48 UTC
  • mto: This revision was merged to the branch mainline in revision 1963.
  • Revision ID: john@arbash-meinel.com-20060825221948-b14fa1054682bab2
David Allouche: Make transports return escaped paths

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005, 2006 by Canonical Ltd
 
2
#   Authors: Robert Collins <robert.collins@canonical.com>
2
3
#
3
4
# This program is free software; you can redistribute it and/or modify
4
5
# it under the terms of the GNU General Public License as published by
12
13
#
13
14
# You should have received a copy of the GNU General Public License
14
15
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
 
17
18
"""Tests for finding and reading the bzr config file[s]."""
18
19
# import system imports here
 
20
from bzrlib.util.configobj.configobj import ConfigObj, ConfigObjError
19
21
from cStringIO import StringIO
20
22
import os
21
23
import sys
22
 
import threading
23
24
 
24
25
#import bzrlib specific imports here
25
26
from bzrlib import (
26
 
    branch,
27
 
    bzrdir,
28
27
    config,
29
 
    diff,
30
28
    errors,
31
29
    osutils,
32
 
    mail_client,
33
 
    ui,
34
30
    urlutils,
35
 
    tests,
36
 
    trace,
37
 
    transport,
38
31
    )
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
 
32
from bzrlib.branch import Branch
 
33
from bzrlib.bzrdir import BzrDir
 
34
from bzrlib.tests import TestCase, TestCaseInTempDir, TestCaseWithTransport
65
35
 
66
36
 
67
37
sample_long_alias="log -r-15..-1 --line"
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
 
"""
 
38
sample_config_text = ("[DEFAULT]\n"
 
39
                      u"email=Erik B\u00e5gfors <erik@bagfors.nu>\n"
 
40
                      "editor=vim\n"
 
41
                      "gpg_signing_command=gnome-gpg\n"
 
42
                      "log_format=short\n"
 
43
                      "user_global_option=something\n"
 
44
                      "[ALIASES]\n"
 
45
                      "h=help\n"
 
46
                      "ll=" + sample_long_alias + "\n")
 
47
 
 
48
 
 
49
sample_always_signatures = ("[DEFAULT]\n"
 
50
                            "check_signatures=ignore\n"
 
51
                            "create_signatures=always")
 
52
 
 
53
 
 
54
sample_ignore_signatures = ("[DEFAULT]\n"
 
55
                            "check_signatures=require\n"
 
56
                            "create_signatures=never")
 
57
 
 
58
 
 
59
sample_maybe_signatures = ("[DEFAULT]\n"
 
60
                            "check_signatures=ignore\n"
 
61
                            "create_signatures=when-required")
 
62
 
 
63
 
 
64
sample_branches_text = ("[http://www.example.com]\n"
 
65
                        "# Top level policy\n"
 
66
                        "email=Robert Collins <robertc@example.org>\n"
 
67
                        "[http://www.example.com/useglobal]\n"
 
68
                        "# different project, forces global lookup\n"
 
69
                        "recurse=false\n"
 
70
                        "[/b/]\n"
 
71
                        "check_signatures=require\n"
 
72
                        "# test trailing / matching with no children\n"
 
73
                        "[/a/]\n"
 
74
                        "check_signatures=check-available\n"
 
75
                        "gpg_signing_command=false\n"
 
76
                        "user_local_option=local\n"
 
77
                        "# test trailing / matching\n"
 
78
                        "[/a/*]\n"
 
79
                        "#subdirs will match but not the parent\n"
 
80
                        "recurse=False\n"
 
81
                        "[/a/c]\n"
 
82
                        "check_signatures=ignore\n"
 
83
                        "post_commit=bzrlib.tests.test_config.post_commit\n"
 
84
                        "#testing explicit beats globs\n")
 
85
 
132
86
 
133
87
 
134
88
class InstrumentedConfigObj(object):
148
102
    def __setitem__(self, key, value):
149
103
        self._calls.append(('__setitem__', key, value))
150
104
 
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
 
 
161
105
    def write(self, arg):
162
106
        self._calls.append(('write',))
163
107
 
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
 
 
172
108
 
173
109
class FakeBranch(object):
174
110
 
177
113
            self.base = "http://example.com/branches/demo"
178
114
        else:
179
115
            self.base = base
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')
 
116
        self.control_files = FakeControlFiles(user_id=user_id)
185
117
 
186
118
    def lock_write(self):
187
119
        pass
190
122
        pass
191
123
 
192
124
 
193
 
class FakeControlFilesAndTransport(object):
 
125
class FakeControlFiles(object):
194
126
 
195
127
    def __init__(self, user_id=None):
 
128
        self.email = user_id
196
129
        self.files = {}
197
 
        if user_id:
198
 
            self.files['email'] = user_id
199
 
        self._transport = self
200
130
 
201
131
    def get_utf8(self, filename):
202
 
        # from LockableFiles
203
 
        raise AssertionError("get_utf8 should no longer be used")
 
132
        if filename != 'email':
 
133
            raise NotImplementedError
 
134
        if self.email is not None:
 
135
            return StringIO(self.email)
 
136
        raise errors.NoSuchFile(filename)
204
137
 
205
138
    def get(self, filename):
206
 
        # from Transport
207
139
        try:
208
140
            return StringIO(self.files[filename])
209
141
        except KeyError:
210
142
            raise errors.NoSuchFile(filename)
211
143
 
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
 
 
219
144
    def put(self, filename, fileobj):
220
145
        self.files[filename] = fileobj.read()
221
146
 
222
 
    def put_file(self, filename, fileobj):
223
 
        return self.put(filename, fileobj)
224
 
 
225
147
 
226
148
class InstrumentedConfig(config.Config):
227
149
    """An instrumented config that supplies stubs for template methods."""
228
 
 
 
150
    
229
151
    def __init__(self):
230
152
        super(InstrumentedConfig, self).__init__()
231
153
        self._calls = []
239
161
        self._calls.append('_get_signature_checking')
240
162
        return self._signatures
241
163
 
242
 
    def _get_change_editor(self):
243
 
        self._calls.append('_get_change_editor')
244
 
        return 'vimdiff -fo @new_path @old_path'
245
 
 
246
164
 
247
165
bool_config = """[DEFAULT]
248
166
active = true
251
169
active = True
252
170
nonactive = False
253
171
"""
254
 
 
255
 
 
256
 
class TestConfigObj(tests.TestCase):
257
 
 
 
172
class TestConfigObj(TestCase):
258
173
    def test_get_bool(self):
259
 
        co = config.ConfigObj(StringIO(bool_config))
 
174
        from bzrlib.config import ConfigObj
 
175
        co = ConfigObj(StringIO(bool_config))
260
176
        self.assertIs(co.get_bool('DEFAULT', 'active'), True)
261
177
        self.assertIs(co.get_bool('DEFAULT', 'inactive'), False)
262
178
        self.assertIs(co.get_bool('UPPERCASE', 'active'), True)
263
179
        self.assertIs(co.get_bool('UPPERCASE', 'nonactive'), False)
264
180
 
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):
 
181
 
 
182
class TestConfig(TestCase):
298
183
 
299
184
    def test_constructs(self):
300
185
        config.Config()
301
 
 
 
186
 
302
187
    def test_no_default_editor(self):
303
188
        self.assertRaises(NotImplementedError, config.Config().get_editor)
304
189
 
349
234
        my_config = config.Config()
350
235
        self.assertEqual('long', my_config.log_format())
351
236
 
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):
 
237
 
 
238
class TestConfigPath(TestCase):
362
239
 
363
240
    def setUp(self):
364
241
        super(TestConfigPath, self).setUp()
 
242
        self.old_home = os.environ.get('HOME', None)
 
243
        self.old_appdata = os.environ.get('APPDATA', None)
365
244
        os.environ['HOME'] = '/home/bogus'
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'
 
245
        os.environ['APPDATA'] = \
 
246
            r'C:\Documents and Settings\bogus\Application Data'
374
247
 
 
248
    def tearDown(self):
 
249
        if self.old_home is None:
 
250
            del os.environ['HOME']
 
251
        else:
 
252
            os.environ['HOME'] = self.old_home
 
253
        if self.old_appdata is None:
 
254
            del os.environ['APPDATA']
 
255
        else:
 
256
            os.environ['APPDATA'] = self.old_appdata
 
257
        super(TestConfigPath, self).tearDown()
 
258
    
375
259
    def test_config_dir(self):
376
 
        self.assertEqual(config.config_dir(), self.bzr_home)
 
260
        if sys.platform == 'win32':
 
261
            self.assertEqual(config.config_dir(), 
 
262
                'C:/Documents and Settings/bogus/Application Data/bazaar/2.0')
 
263
        else:
 
264
            self.assertEqual(config.config_dir(), '/home/bogus/.bazaar')
377
265
 
378
266
    def test_config_filename(self):
379
 
        self.assertEqual(config.config_filename(),
380
 
                         self.bzr_home + '/bazaar.conf')
 
267
        if sys.platform == 'win32':
 
268
            self.assertEqual(config.config_filename(), 
 
269
                'C:/Documents and Settings/bogus/Application Data/bazaar/2.0/bazaar.conf')
 
270
        else:
 
271
            self.assertEqual(config.config_filename(),
 
272
                             '/home/bogus/.bazaar/bazaar.conf')
 
273
 
 
274
    def test_branches_config_filename(self):
 
275
        if sys.platform == 'win32':
 
276
            self.assertEqual(config.branches_config_filename(), 
 
277
                'C:/Documents and Settings/bogus/Application Data/bazaar/2.0/branches.conf')
 
278
        else:
 
279
            self.assertEqual(config.branches_config_filename(),
 
280
                             '/home/bogus/.bazaar/branches.conf')
381
281
 
382
282
    def test_locations_config_filename(self):
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):
 
283
        if sys.platform == 'win32':
 
284
            self.assertEqual(config.locations_config_filename(), 
 
285
                'C:/Documents and Settings/bogus/Application Data/bazaar/2.0/locations.conf')
 
286
        else:
 
287
            self.assertEqual(config.locations_config_filename(),
 
288
                             '/home/bogus/.bazaar/locations.conf')
 
289
 
 
290
class TestIniConfig(TestCase):
403
291
 
404
292
    def test_contructs(self):
405
 
        my_config = config.IniBasedConfig()
 
293
        my_config = config.IniBasedConfig("nothing")
406
294
 
407
295
    def test_from_fp(self):
408
 
        my_config = config.IniBasedConfig.from_string(sample_config_text)
409
 
        self.assertIsInstance(my_config._get_parser(), configobj.ConfigObj)
 
296
        config_file = StringIO(sample_config_text.encode('utf-8'))
 
297
        my_config = config.IniBasedConfig(None)
 
298
        self.failUnless(
 
299
            isinstance(my_config._get_parser(file=config_file),
 
300
                        ConfigObj))
410
301
 
411
302
    def test_cached(self):
412
 
        my_config = config.IniBasedConfig.from_string(sample_config_text)
413
 
        parser = my_config._get_parser()
 
303
        config_file = StringIO(sample_config_text.encode('utf-8'))
 
304
        my_config = config.IniBasedConfig(None)
 
305
        parser = my_config._get_parser(file=config_file)
414
306
        self.failUnless(my_config._get_parser() is parser)
415
307
 
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):
 
308
 
 
309
class TestGetConfig(TestCase):
658
310
 
659
311
    def test_constructs(self):
660
312
        my_config = config.GlobalConfig()
661
313
 
662
314
    def test_calls_read_filenames(self):
663
 
        # replace the class that is constructed, to check its parameters
 
315
        # replace the class that is constructured, to check its parameters
664
316
        oldparserclass = config.ConfigObj
665
317
        config.ConfigObj = InstrumentedConfigObj
666
318
        my_config = config.GlobalConfig()
673
325
                                          'utf-8')])
674
326
 
675
327
 
676
 
class TestBranchConfig(tests.TestCaseWithTransport):
 
328
class TestBranchConfig(TestCaseWithTransport):
677
329
 
678
330
    def test_constructs(self):
679
331
        branch = FakeBranch()
689
341
 
690
342
    def test_get_config(self):
691
343
        """The Branch.get_config method works properly"""
692
 
        b = bzrdir.BzrDir.create_standalone_workingtree('.').branch
 
344
        b = BzrDir.create_standalone_workingtree('.').branch
693
345
        my_config = b.get_config()
694
346
        self.assertIs(my_config.get_user_option('wacky'), None)
695
347
        my_config.set_user_option('wacky', 'unlikely')
696
348
        self.assertEqual(my_config.get_user_option('wacky'), 'unlikely')
697
349
 
698
350
        # Ensure we get the same thing if we start again
699
 
        b2 = branch.Branch.open('.')
 
351
        b2 = Branch.open('.')
700
352
        my_config2 = b2.get_config()
701
353
        self.assertEqual(my_config2.get_user_option('wacky'), 'unlikely')
702
354
 
711
363
        branch = self.make_branch('branch')
712
364
        self.assertEqual('branch', branch.nick)
713
365
 
 
366
        locations = config.locations_config_filename()
 
367
        config.ensure_config_dir_exists()
714
368
        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)
 
369
        open(locations, 'wb').write('[%s]\nnickname = foobar' 
 
370
                                    % (local_url,))
718
371
        self.assertEqual('foobar', branch.nick)
719
372
 
720
373
    def test_config_local_path(self):
722
375
        branch = self.make_branch('branch')
723
376
        self.assertEqual('branch', branch.nick)
724
377
 
725
 
        local_path = osutils.getcwd().encode('utf8')
726
 
        conf = config.LocationConfig.from_string(
727
 
            '[%s/branch]\nnickname = barry' % (local_path,),
728
 
            'branch',  save=True)
 
378
        locations = config.locations_config_filename()
 
379
        config.ensure_config_dir_exists()
 
380
        open(locations, 'wb').write('[%s/branch]\nnickname = barry' 
 
381
                                    % (osutils.getcwd().encode('utf8'),))
729
382
        self.assertEqual('barry', branch.nick)
730
383
 
731
384
    def test_config_creates_local(self):
732
385
        """Creating a new entry in config uses a local path."""
733
 
        branch = self.make_branch('branch', format='knit')
 
386
        branch = self.make_branch('branch')
734
387
        branch.set_push_location('http://foobar')
 
388
        locations = config.locations_config_filename()
735
389
        local_path = osutils.getcwd().encode('utf8')
736
390
        # 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.TestCase):
 
391
        self.check_file_contents(locations,
 
392
            '[%s/branch]\npush_location = http://foobar' % (local_path,))
 
393
 
 
394
 
 
395
class TestGlobalConfigItems(TestCase):
782
396
 
783
397
    def test_user_id(self):
784
 
        my_config = config.GlobalConfig.from_string(sample_config_text)
 
398
        config_file = StringIO(sample_config_text.encode('utf-8'))
 
399
        my_config = config.GlobalConfig()
 
400
        my_config._parser = my_config._get_parser(file=config_file)
785
401
        self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
786
402
                         my_config._get_user_id())
787
403
 
788
404
    def test_absent_user_id(self):
 
405
        config_file = StringIO("")
789
406
        my_config = config.GlobalConfig()
 
407
        my_config._parser = my_config._get_parser(file=config_file)
790
408
        self.assertEqual(None, my_config._get_user_id())
791
409
 
792
410
    def test_configured_editor(self):
793
 
        my_config = config.GlobalConfig.from_string(sample_config_text)
 
411
        config_file = StringIO(sample_config_text.encode('utf-8'))
 
412
        my_config = config.GlobalConfig()
 
413
        my_config._parser = my_config._get_parser(file=config_file)
794
414
        self.assertEqual("vim", my_config.get_editor())
795
415
 
796
416
    def test_signatures_always(self):
797
 
        my_config = config.GlobalConfig.from_string(sample_always_signatures)
 
417
        config_file = StringIO(sample_always_signatures)
 
418
        my_config = config.GlobalConfig()
 
419
        my_config._parser = my_config._get_parser(file=config_file)
798
420
        self.assertEqual(config.CHECK_NEVER,
799
421
                         my_config.signature_checking())
800
422
        self.assertEqual(config.SIGN_ALWAYS,
802
424
        self.assertEqual(True, my_config.signature_needed())
803
425
 
804
426
    def test_signatures_if_possible(self):
805
 
        my_config = config.GlobalConfig.from_string(sample_maybe_signatures)
 
427
        config_file = StringIO(sample_maybe_signatures)
 
428
        my_config = config.GlobalConfig()
 
429
        my_config._parser = my_config._get_parser(file=config_file)
806
430
        self.assertEqual(config.CHECK_NEVER,
807
431
                         my_config.signature_checking())
808
432
        self.assertEqual(config.SIGN_WHEN_REQUIRED,
810
434
        self.assertEqual(False, my_config.signature_needed())
811
435
 
812
436
    def test_signatures_ignore(self):
813
 
        my_config = config.GlobalConfig.from_string(sample_ignore_signatures)
 
437
        config_file = StringIO(sample_ignore_signatures)
 
438
        my_config = config.GlobalConfig()
 
439
        my_config._parser = my_config._get_parser(file=config_file)
814
440
        self.assertEqual(config.CHECK_ALWAYS,
815
441
                         my_config.signature_checking())
816
442
        self.assertEqual(config.SIGN_NEVER,
818
444
        self.assertEqual(False, my_config.signature_needed())
819
445
 
820
446
    def _get_sample_config(self):
821
 
        my_config = config.GlobalConfig.from_string(sample_config_text)
 
447
        config_file = StringIO(sample_config_text.encode('utf-8'))
 
448
        my_config = config.GlobalConfig()
 
449
        my_config._parser = my_config._get_parser(file=config_file)
822
450
        return my_config
823
451
 
824
452
    def test_gpg_signing_command(self):
827
455
        self.assertEqual(False, my_config.signature_needed())
828
456
 
829
457
    def _get_empty_config(self):
 
458
        config_file = StringIO("")
830
459
        my_config = config.GlobalConfig()
 
460
        my_config._parser = my_config._get_parser(file=config_file)
831
461
        return my_config
832
462
 
833
463
    def test_gpg_signing_command_unset(self):
842
472
        my_config = self._get_sample_config()
843
473
        self.assertEqual("something",
844
474
                         my_config.get_user_option('user_global_option'))
845
 
 
 
475
        
846
476
    def test_post_commit_default(self):
847
477
        my_config = self._get_sample_config()
848
478
        self.assertEqual(None, my_config.post_commit())
855
485
        my_config = self._get_sample_config()
856
486
        self.assertEqual('help', my_config.get_alias('h'))
857
487
 
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
 
 
866
488
    def test_get_no_alias(self):
867
489
        my_config = self._get_sample_config()
868
490
        self.assertEqual(None, my_config.get_alias('foo'))
871
493
        my_config = self._get_sample_config()
872
494
        self.assertEqual(sample_long_alias, my_config.get_alias('ll'))
873
495
 
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):
 
496
 
 
497
class TestLocationConfig(TestCaseInTempDir):
910
498
 
911
499
    def test_constructs(self):
912
500
        my_config = config.LocationConfig('http://example.com')
916
504
        # This is testing the correct file names are provided.
917
505
        # TODO: consolidate with the test for GlobalConfigs filename checks.
918
506
        #
919
 
        # replace the class that is constructed, to check its parameters
 
507
        # replace the class that is constructured, to check its parameters
920
508
        oldparserclass = config.ConfigObj
921
509
        config.ConfigObj = InstrumentedConfigObj
922
510
        try:
928
516
        self.assertEqual(parser._calls,
929
517
                         [('__init__', config.locations_config_filename(),
930
518
                           'utf-8')])
 
519
        config.ensure_config_dir_exists()
 
520
        #os.mkdir(config.config_dir())
 
521
        f = file(config.branches_config_filename(), 'wb')
 
522
        f.write('')
 
523
        f.close()
 
524
        oldparserclass = config.ConfigObj
 
525
        config.ConfigObj = InstrumentedConfigObj
 
526
        try:
 
527
            my_config = config.LocationConfig('http://www.example.com')
 
528
            parser = my_config._get_parser()
 
529
        finally:
 
530
            config.ConfigObj = oldparserclass
931
531
 
932
532
    def test_get_global_config(self):
933
533
        my_config = config.BranchConfig(FakeBranch('http://example.com'))
935
535
        self.failUnless(isinstance(global_config, config.GlobalConfig))
936
536
        self.failUnless(global_config is my_config._get_global_config())
937
537
 
938
 
    def test__get_matching_sections_no_match(self):
 
538
    def test__get_section_no_match(self):
939
539
        self.get_branch_config('/')
940
 
        self.assertEqual([], self.my_location_config._get_matching_sections())
941
 
 
942
 
    def test__get_matching_sections_exact(self):
 
540
        self.assertEqual(None, self.my_location_config._get_section())
 
541
        
 
542
    def test__get_section_exact(self):
943
543
        self.get_branch_config('http://www.example.com')
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):
 
544
        self.assertEqual('http://www.example.com',
 
545
                         self.my_location_config._get_section())
 
546
   
 
547
    def test__get_section_suffix_does_not(self):
948
548
        self.get_branch_config('http://www.example.com-com')
949
 
        self.assertEqual([], self.my_location_config._get_matching_sections())
 
549
        self.assertEqual(None, self.my_location_config._get_section())
950
550
 
951
 
    def test__get_matching_sections_subdir_recursive(self):
 
551
    def test__get_section_subdir_recursive(self):
952
552
        self.get_branch_config('http://www.example.com/com')
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):
 
553
        self.assertEqual('http://www.example.com',
 
554
                         self.my_location_config._get_section())
 
555
 
 
556
    def test__get_section_subdir_matches(self):
 
557
        self.get_branch_config('http://www.example.com/useglobal')
 
558
        self.assertEqual('http://www.example.com/useglobal',
 
559
                         self.my_location_config._get_section())
 
560
 
 
561
    def test__get_section_subdir_nonrecursive(self):
962
562
        self.get_branch_config(
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())
 
563
            'http://www.example.com/useglobal/childbranch')
 
564
        self.assertEqual('http://www.example.com',
 
565
                         self.my_location_config._get_section())
967
566
 
968
 
    def test__get_matching_sections_subdir_trailing_slash(self):
 
567
    def test__get_section_subdir_trailing_slash(self):
969
568
        self.get_branch_config('/b')
970
 
        self.assertEqual([('/b/', '')],
971
 
                         self.my_location_config._get_matching_sections())
 
569
        self.assertEqual('/b/', self.my_location_config._get_section())
972
570
 
973
 
    def test__get_matching_sections_subdir_child(self):
 
571
    def test__get_section_subdir_child(self):
974
572
        self.get_branch_config('/a/foo')
975
 
        self.assertEqual([('/a/*', ''), ('/a/', 'foo')],
976
 
                         self.my_location_config._get_matching_sections())
 
573
        self.assertEqual('/a/*', self.my_location_config._get_section())
977
574
 
978
 
    def test__get_matching_sections_subdir_child_child(self):
 
575
    def test__get_section_subdir_child_child(self):
979
576
        self.get_branch_config('/a/foo/bar')
980
 
        self.assertEqual([('/a/*', 'bar'), ('/a/', 'foo/bar')],
981
 
                         self.my_location_config._get_matching_sections())
 
577
        self.assertEqual('/a/', self.my_location_config._get_section())
982
578
 
983
 
    def test__get_matching_sections_trailing_slash_with_children(self):
 
579
    def test__get_section_trailing_slash_with_children(self):
984
580
        self.get_branch_config('/a/')
985
 
        self.assertEqual([('/a/', '')],
986
 
                         self.my_location_config._get_matching_sections())
 
581
        self.assertEqual('/a/', self.my_location_config._get_section())
987
582
 
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'.
 
583
    def test__get_section_explicit_over_glob(self):
993
584
        self.get_branch_config('/a/c')
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)
 
585
        self.assertEqual('/a/c', self.my_location_config._get_section())
 
586
 
1022
587
 
1023
588
    def test_location_without_username(self):
1024
 
        self.get_branch_config('http://www.example.com/ignoreparent')
 
589
        self.get_branch_config('http://www.example.com/useglobal')
1025
590
        self.assertEqual(u'Erik B\u00e5gfors <erik@bagfors.nu>',
1026
591
                         self.my_config.username())
1027
592
 
1048
613
        self.get_branch_config('/a/c')
1049
614
        self.assertEqual(config.CHECK_NEVER,
1050
615
                         self.my_config.signature_checking())
1051
 
 
 
616
        
1052
617
    def test_signatures_when_available(self):
1053
618
        self.get_branch_config('/a/', global_config=sample_ignore_signatures)
1054
619
        self.assertEqual(config.CHECK_IF_POSSIBLE,
1055
620
                         self.my_config.signature_checking())
1056
 
 
 
621
        
1057
622
    def test_signatures_always(self):
1058
623
        self.get_branch_config('/b')
1059
624
        self.assertEqual(config.CHECK_ALWAYS,
1060
625
                         self.my_config.signature_checking())
1061
 
 
 
626
        
1062
627
    def test_gpg_signing_command(self):
1063
628
        self.get_branch_config('/b')
1064
629
        self.assertEqual("gnome-gpg", self.my_config.gpg_signing_command())
1076
641
        self.get_branch_config('/a')
1077
642
        self.assertEqual('local',
1078
643
                         self.my_config.get_user_option('user_local_option'))
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
 
 
 
644
        
1159
645
    def test_post_commit_default(self):
1160
646
        self.get_branch_config('/a/c')
1161
647
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1162
648
                         self.my_config.post_commit())
1163
649
 
1164
650
    def get_branch_config(self, location, global_config=None):
1165
 
        my_branch = FakeBranch(location)
1166
651
        if global_config is None:
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()
 
652
            global_file = StringIO(sample_config_text.encode('utf-8'))
 
653
        else:
 
654
            global_file = StringIO(global_config.encode('utf-8'))
 
655
        branches_file = StringIO(sample_branches_text.encode('utf-8'))
 
656
        self.my_config = config.BranchConfig(FakeBranch(location))
 
657
        # Force location config to use specified file
 
658
        self.my_location_config = self.my_config._get_location_config()
 
659
        self.my_location_config._get_parser(branches_file)
 
660
        # Force global config to use specified file
 
661
        self.my_config._get_global_config()._get_parser(global_file)
1176
662
 
1177
663
    def test_set_user_setting_sets_and_saves(self):
1178
664
        self.get_branch_config('/a/c')
1179
665
        record = InstrumentedConfigObj("foo")
1180
666
        self.my_location_config._parser = record
1181
667
 
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'),
 
668
        real_mkdir = os.mkdir
 
669
        self.created = False
 
670
        def checked_mkdir(path, mode=0777):
 
671
            self.log('making directory: %s', path)
 
672
            real_mkdir(path, mode)
 
673
            self.created = True
 
674
 
 
675
        os.mkdir = checked_mkdir
 
676
        try:
 
677
            self.my_config.set_user_option('foo', 'bar', local=True)
 
678
        finally:
 
679
            os.mkdir = real_mkdir
 
680
 
 
681
        self.failUnless(self.created, 'Failed to create ~/.bazaar')
 
682
        self.assertEqual([('__contains__', '/a/c'),
1189
683
                          ('__contains__', '/a/c/'),
1190
684
                          ('__setitem__', '/a/c', {}),
1191
685
                          ('__getitem__', '/a/c'),
1192
686
                          ('__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'),
1201
687
                          ('write',)],
1202
688
                         record._calls[1:])
1203
689
 
1206
692
        self.assertIs(self.my_config.get_user_option('foo'), None)
1207
693
        self.my_config.set_user_option('foo', 'bar')
1208
694
        self.assertEqual(
1209
 
            self.my_config.branch.control_files.files['branch.conf'].strip(),
 
695
            self.my_config.branch.control_files.files['branch.conf'], 
1210
696
            'foo = bar')
1211
697
        self.assertEqual(self.my_config.get_user_option('foo'), 'bar')
1212
 
        self.my_config.set_user_option('foo', 'baz',
1213
 
                                       store=config.STORE_LOCATION)
 
698
        self.my_config.set_user_option('foo', 'baz', local=True)
1214
699
        self.assertEqual(self.my_config.get_user_option('foo'), 'baz')
1215
700
        self.my_config.set_user_option('foo', 'qux')
1216
701
        self.assertEqual(self.my_config.get_user_option('foo'), 'baz')
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
 
 
 
702
        
1226
703
 
1227
704
precedence_global = 'option = global'
1228
705
precedence_branch = 'option = branch'
1234
711
option = exact
1235
712
"""
1236
713
 
1237
 
class TestBranchConfigItems(tests.TestCaseInTempDir):
1238
 
 
1239
 
    def get_branch_config(self, global_config=None, location=None,
 
714
 
 
715
class TestBranchConfigItems(TestCaseInTempDir):
 
716
 
 
717
    def get_branch_config(self, global_config=None, location=None, 
1240
718
                          location_config=None, branch_data_config=None):
1241
 
        my_branch = FakeBranch(location)
 
719
        my_config = config.BranchConfig(FakeBranch(location))
1242
720
        if global_config is not None:
1243
 
            my_global_config = config.GlobalConfig.from_string(global_config,
1244
 
                                                               save=True)
 
721
            global_file = StringIO(global_config.encode('utf-8'))
 
722
            my_config._get_global_config()._get_parser(global_file)
 
723
        self.my_location_config = my_config._get_location_config()
1245
724
        if location_config is not None:
1246
 
            my_location_config = config.LocationConfig.from_string(
1247
 
                location_config, my_branch.base, save=True)
1248
 
        my_config = config.BranchConfig(my_branch)
 
725
            location_file = StringIO(location_config.encode('utf-8'))
 
726
            self.my_location_config._get_parser(location_file)
1249
727
        if branch_data_config is not None:
1250
728
            my_config.branch.control_files.files['branch.conf'] = \
1251
729
                branch_data_config
1256
734
        my_config = config.BranchConfig(branch)
1257
735
        self.assertEqual("Robert Collins <robertc@example.net>",
1258
736
                         my_config.username())
1259
 
        my_config.branch.control_files.files['email'] = "John"
1260
 
        my_config.set_user_option('email',
 
737
        branch.control_files.email = "John"
 
738
        my_config.set_user_option('email', 
1261
739
                                  "Robert Collins <robertc@example.org>")
1262
740
        self.assertEqual("John", my_config.username())
1263
 
        del my_config.branch.control_files.files['email']
 
741
        branch.control_files.email = None
1264
742
        self.assertEqual("Robert Collins <robertc@example.org>",
1265
743
                         my_config.username())
1266
744
 
1267
745
    def test_not_set_in_branch(self):
1268
 
        my_config = self.get_branch_config(global_config=sample_config_text)
 
746
        my_config = self.get_branch_config(sample_config_text)
 
747
        my_config.branch.control_files.email = None
1269
748
        self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
1270
749
                         my_config._get_user_id())
1271
 
        my_config.branch.control_files.files['email'] = "John"
 
750
        my_config.branch.control_files.email = "John"
1272
751
        self.assertEqual("John", my_config._get_user_id())
1273
752
 
1274
753
    def test_BZR_EMAIL_OVERRIDES(self):
1277
756
        my_config = config.BranchConfig(branch)
1278
757
        self.assertEqual("Robert Collins <robertc@example.org>",
1279
758
                         my_config.username())
1280
 
 
 
759
    
1281
760
    def test_signatures_forced(self):
1282
761
        my_config = self.get_branch_config(
1283
762
            global_config=sample_always_signatures)
1295
774
 
1296
775
    def test_gpg_signing_command(self):
1297
776
        my_config = self.get_branch_config(
1298
 
            global_config=sample_config_text,
1299
777
            # branch data cannot set gpg_signing_command
1300
778
            branch_data_config="gpg_signing_command=pgp")
 
779
        config_file = StringIO(sample_config_text.encode('utf-8'))
 
780
        my_config._get_global_config()._get_parser(config_file)
1301
781
        self.assertEqual('gnome-gpg', my_config.gpg_signing_command())
1302
782
 
1303
783
    def test_get_user_option_global(self):
1304
 
        my_config = self.get_branch_config(global_config=sample_config_text)
 
784
        branch = FakeBranch()
 
785
        my_config = config.BranchConfig(branch)
 
786
        config_file = StringIO(sample_config_text.encode('utf-8'))
 
787
        (my_config._get_global_config()._get_parser(config_file))
1305
788
        self.assertEqual('something',
1306
789
                         my_config.get_user_option('user_global_option'))
1307
790
 
1308
791
    def test_post_commit_default(self):
1309
 
        my_config = self.get_branch_config(global_config=sample_config_text,
1310
 
                                      location='/a/c',
1311
 
                                      location_config=sample_branches_text)
 
792
        branch = FakeBranch()
 
793
        my_config = self.get_branch_config(sample_config_text, '/a/c',
 
794
                                           sample_branches_text)
1312
795
        self.assertEqual(my_config.branch.base, '/a/c')
1313
796
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1314
797
                         my_config.post_commit())
1315
798
        my_config.set_user_option('post_commit', 'rmtree_root')
1316
 
        # post-commit is ignored when present in branch data
 
799
        # post-commit is ignored when bresent in branch data
1317
800
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1318
801
                         my_config.post_commit())
1319
 
        my_config.set_user_option('post_commit', 'rmtree_root',
1320
 
                                  store=config.STORE_LOCATION)
 
802
        my_config.set_user_option('post_commit', 'rmtree_root', local=True)
1321
803
        self.assertEqual('rmtree_root', my_config.post_commit())
1322
804
 
1323
805
    def test_config_precedence(self):
1324
 
        # FIXME: eager test, luckily no persitent config file makes it fail
1325
 
        # -- vila 20100716
1326
806
        my_config = self.get_branch_config(global_config=precedence_global)
1327
807
        self.assertEqual(my_config.get_user_option('option'), 'global')
1328
 
        my_config = self.get_branch_config(global_config=precedence_global,
1329
 
                                           branch_data_config=precedence_branch)
 
808
        my_config = self.get_branch_config(global_config=precedence_global, 
 
809
                                      branch_data_config=precedence_branch)
1330
810
        self.assertEqual(my_config.get_user_option('option'), 'branch')
1331
 
        my_config = self.get_branch_config(
1332
 
            global_config=precedence_global,
1333
 
            branch_data_config=precedence_branch,
1334
 
            location_config=precedence_location)
 
811
        my_config = self.get_branch_config(global_config=precedence_global, 
 
812
                                      branch_data_config=precedence_branch,
 
813
                                      location_config=precedence_location)
1335
814
        self.assertEqual(my_config.get_user_option('option'), 'recurse')
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')
 
815
        my_config = self.get_branch_config(global_config=precedence_global, 
 
816
                                      branch_data_config=precedence_branch,
 
817
                                      location_config=precedence_location,
 
818
                                      location='http://example.com/specific')
1341
819
        self.assertEqual(my_config.get_user_option('option'), 'exact')
1342
820
 
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):
 
821
 
 
822
class TestMailAddressExtraction(TestCase):
1387
823
 
1388
824
    def test_extract_email_address(self):
1389
825
        self.assertEqual('jane@test.com',
1390
826
                         config.extract_email_address('Jane <jane@test.com>'))
1391
 
        self.assertRaises(errors.NoEmailInUsername,
 
827
        self.assertRaises(errors.BzrError,
1392
828
                          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
 
class TestAuthenticationConfigFile(tests.TestCase):
1475
 
    """Test the authentication.conf file matching"""
1476
 
 
1477
 
    def _got_user_passwd(self, expected_user, expected_password,
1478
 
                         config, *args, **kwargs):
1479
 
        credentials = config.get_credentials(*args, **kwargs)
1480
 
        if credentials is None:
1481
 
            user = None
1482
 
            password = None
1483
 
        else:
1484
 
            user = credentials['user']
1485
 
            password = credentials['password']
1486
 
        self.assertEquals(expected_user, user)
1487
 
        self.assertEquals(expected_password, password)
1488
 
 
1489
 
    def test_empty_config(self):
1490
 
        conf = config.AuthenticationConfig(_file=StringIO())
1491
 
        self.assertEquals({}, conf._get_config())
1492
 
        self._got_user_passwd(None, None, conf, 'http', 'foo.net')
1493
 
 
1494
 
    def test_missing_auth_section_header(self):
1495
 
        conf = config.AuthenticationConfig(_file=StringIO('foo = bar'))
1496
 
        self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
1497
 
 
1498
 
    def test_auth_section_header_not_closed(self):
1499
 
        conf = config.AuthenticationConfig(_file=StringIO('[DEF'))
1500
 
        self.assertRaises(errors.ParseConfigError, conf._get_config)
1501
 
 
1502
 
    def test_auth_value_not_boolean(self):
1503
 
        conf = config.AuthenticationConfig(_file=StringIO(
1504
 
                """[broken]
1505
 
scheme=ftp
1506
 
user=joe
1507
 
verify_certificates=askme # Error: Not a boolean
1508
 
"""))
1509
 
        self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
1510
 
 
1511
 
    def test_auth_value_not_int(self):
1512
 
        conf = config.AuthenticationConfig(_file=StringIO(
1513
 
                """[broken]
1514
 
scheme=ftp
1515
 
user=joe
1516
 
port=port # Error: Not an int
1517
 
"""))
1518
 
        self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
1519
 
 
1520
 
    def test_unknown_password_encoding(self):
1521
 
        conf = config.AuthenticationConfig(_file=StringIO(
1522
 
                """[broken]
1523
 
scheme=ftp
1524
 
user=joe
1525
 
password_encoding=unknown
1526
 
"""))
1527
 
        self.assertRaises(ValueError, conf.get_password,
1528
 
                          'ftp', 'foo.net', 'joe')
1529
 
 
1530
 
    def test_credentials_for_scheme_host(self):
1531
 
        conf = config.AuthenticationConfig(_file=StringIO(
1532
 
                """# Identity on foo.net
1533
 
[ftp definition]
1534
 
scheme=ftp
1535
 
host=foo.net
1536
 
user=joe
1537
 
password=secret-pass
1538
 
"""))
1539
 
        # Basic matching
1540
 
        self._got_user_passwd('joe', 'secret-pass', conf, 'ftp', 'foo.net')
1541
 
        # different scheme
1542
 
        self._got_user_passwd(None, None, conf, 'http', 'foo.net')
1543
 
        # different host
1544
 
        self._got_user_passwd(None, None, conf, 'ftp', 'bar.net')
1545
 
 
1546
 
    def test_credentials_for_host_port(self):
1547
 
        conf = config.AuthenticationConfig(_file=StringIO(
1548
 
                """# Identity on foo.net
1549
 
[ftp definition]
1550
 
scheme=ftp
1551
 
port=10021
1552
 
host=foo.net
1553
 
user=joe
1554
 
password=secret-pass
1555
 
"""))
1556
 
        # No port
1557
 
        self._got_user_passwd('joe', 'secret-pass',
1558
 
                              conf, 'ftp', 'foo.net', port=10021)
1559
 
        # different port
1560
 
        self._got_user_passwd(None, None, conf, 'ftp', 'foo.net')
1561
 
 
1562
 
    def test_for_matching_host(self):
1563
 
        conf = config.AuthenticationConfig(_file=StringIO(
1564
 
                """# Identity on foo.net
1565
 
[sourceforge]
1566
 
scheme=bzr
1567
 
host=bzr.sf.net
1568
 
user=joe
1569
 
password=joepass
1570
 
[sourceforge domain]
1571
 
scheme=bzr
1572
 
host=.bzr.sf.net
1573
 
user=georges
1574
 
password=bendover
1575
 
"""))
1576
 
        # matching domain
1577
 
        self._got_user_passwd('georges', 'bendover',
1578
 
                              conf, 'bzr', 'foo.bzr.sf.net')
1579
 
        # phishing attempt
1580
 
        self._got_user_passwd(None, None,
1581
 
                              conf, 'bzr', 'bbzr.sf.net')
1582
 
 
1583
 
    def test_for_matching_host_None(self):
1584
 
        conf = config.AuthenticationConfig(_file=StringIO(
1585
 
                """# Identity on foo.net
1586
 
[catchup bzr]
1587
 
scheme=bzr
1588
 
user=joe
1589
 
password=joepass
1590
 
[DEFAULT]
1591
 
user=georges
1592
 
password=bendover
1593
 
"""))
1594
 
        # match no host
1595
 
        self._got_user_passwd('joe', 'joepass',
1596
 
                              conf, 'bzr', 'quux.net')
1597
 
        # no host but different scheme
1598
 
        self._got_user_passwd('georges', 'bendover',
1599
 
                              conf, 'ftp', 'quux.net')
1600
 
 
1601
 
    def test_credentials_for_path(self):
1602
 
        conf = config.AuthenticationConfig(_file=StringIO(
1603
 
                """
1604
 
[http dir1]
1605
 
scheme=http
1606
 
host=bar.org
1607
 
path=/dir1
1608
 
user=jim
1609
 
password=jimpass
1610
 
[http dir2]
1611
 
scheme=http
1612
 
host=bar.org
1613
 
path=/dir2
1614
 
user=georges
1615
 
password=bendover
1616
 
"""))
1617
 
        # no path no dice
1618
 
        self._got_user_passwd(None, None,
1619
 
                              conf, 'http', host='bar.org', path='/dir3')
1620
 
        # matching path
1621
 
        self._got_user_passwd('georges', 'bendover',
1622
 
                              conf, 'http', host='bar.org', path='/dir2')
1623
 
        # matching subdir
1624
 
        self._got_user_passwd('jim', 'jimpass',
1625
 
                              conf, 'http', host='bar.org',path='/dir1/subdir')
1626
 
 
1627
 
    def test_credentials_for_user(self):
1628
 
        conf = config.AuthenticationConfig(_file=StringIO(
1629
 
                """
1630
 
[with user]
1631
 
scheme=http
1632
 
host=bar.org
1633
 
user=jim
1634
 
password=jimpass
1635
 
"""))
1636
 
        # Get user
1637
 
        self._got_user_passwd('jim', 'jimpass',
1638
 
                              conf, 'http', 'bar.org')
1639
 
        # Get same user
1640
 
        self._got_user_passwd('jim', 'jimpass',
1641
 
                              conf, 'http', 'bar.org', user='jim')
1642
 
        # Don't get a different user if one is specified
1643
 
        self._got_user_passwd(None, None,
1644
 
                              conf, 'http', 'bar.org', user='georges')
1645
 
 
1646
 
    def test_credentials_for_user_without_password(self):
1647
 
        conf = config.AuthenticationConfig(_file=StringIO(
1648
 
                """
1649
 
[without password]
1650
 
scheme=http
1651
 
host=bar.org
1652
 
user=jim
1653
 
"""))
1654
 
        # Get user but no password
1655
 
        self._got_user_passwd('jim', None,
1656
 
                              conf, 'http', 'bar.org')
1657
 
 
1658
 
    def test_verify_certificates(self):
1659
 
        conf = config.AuthenticationConfig(_file=StringIO(
1660
 
                """
1661
 
[self-signed]
1662
 
scheme=https
1663
 
host=bar.org
1664
 
user=jim
1665
 
password=jimpass
1666
 
verify_certificates=False
1667
 
[normal]
1668
 
scheme=https
1669
 
host=foo.net
1670
 
user=georges
1671
 
password=bendover
1672
 
"""))
1673
 
        credentials = conf.get_credentials('https', 'bar.org')
1674
 
        self.assertEquals(False, credentials.get('verify_certificates'))
1675
 
        credentials = conf.get_credentials('https', 'foo.net')
1676
 
        self.assertEquals(True, credentials.get('verify_certificates'))
1677
 
 
1678
 
 
1679
 
class TestAuthenticationStorage(tests.TestCaseInTempDir):
1680
 
 
1681
 
    def test_set_credentials(self):
1682
 
        conf = config.AuthenticationConfig()
1683
 
        conf.set_credentials('name', 'host', 'user', 'scheme', 'password',
1684
 
        99, path='/foo', verify_certificates=False, realm='realm')
1685
 
        credentials = conf.get_credentials(host='host', scheme='scheme',
1686
 
                                           port=99, path='/foo',
1687
 
                                           realm='realm')
1688
 
        CREDENTIALS = {'name': 'name', 'user': 'user', 'password': 'password',
1689
 
                       'verify_certificates': False, 'scheme': 'scheme', 
1690
 
                       'host': 'host', 'port': 99, 'path': '/foo', 
1691
 
                       'realm': 'realm'}
1692
 
        self.assertEqual(CREDENTIALS, credentials)
1693
 
        credentials_from_disk = config.AuthenticationConfig().get_credentials(
1694
 
            host='host', scheme='scheme', port=99, path='/foo', realm='realm')
1695
 
        self.assertEqual(CREDENTIALS, credentials_from_disk)
1696
 
 
1697
 
    def test_reset_credentials_different_name(self):
1698
 
        conf = config.AuthenticationConfig()
1699
 
        conf.set_credentials('name', 'host', 'user', 'scheme', 'password'),
1700
 
        conf.set_credentials('name2', 'host', 'user2', 'scheme', 'password'),
1701
 
        self.assertIs(None, conf._get_config().get('name'))
1702
 
        credentials = conf.get_credentials(host='host', scheme='scheme')
1703
 
        CREDENTIALS = {'name': 'name2', 'user': 'user2', 'password':
1704
 
                       'password', 'verify_certificates': True, 
1705
 
                       'scheme': 'scheme', 'host': 'host', 'port': None, 
1706
 
                       'path': None, 'realm': None}
1707
 
        self.assertEqual(CREDENTIALS, credentials)
1708
 
 
1709
 
 
1710
 
class TestAuthenticationConfig(tests.TestCase):
1711
 
    """Test AuthenticationConfig behaviour"""
1712
 
 
1713
 
    def _check_default_password_prompt(self, expected_prompt_format, scheme,
1714
 
                                       host=None, port=None, realm=None,
1715
 
                                       path=None):
1716
 
        if host is None:
1717
 
            host = 'bar.org'
1718
 
        user, password = 'jim', 'precious'
1719
 
        expected_prompt = expected_prompt_format % {
1720
 
            'scheme': scheme, 'host': host, 'port': port,
1721
 
            'user': user, 'realm': realm}
1722
 
 
1723
 
        stdout = tests.StringIOWrapper()
1724
 
        stderr = tests.StringIOWrapper()
1725
 
        ui.ui_factory = tests.TestUIFactory(stdin=password + '\n',
1726
 
                                            stdout=stdout, stderr=stderr)
1727
 
        # We use an empty conf so that the user is always prompted
1728
 
        conf = config.AuthenticationConfig()
1729
 
        self.assertEquals(password,
1730
 
                          conf.get_password(scheme, host, user, port=port,
1731
 
                                            realm=realm, path=path))
1732
 
        self.assertEquals(expected_prompt, stderr.getvalue())
1733
 
        self.assertEquals('', stdout.getvalue())
1734
 
 
1735
 
    def _check_default_username_prompt(self, expected_prompt_format, scheme,
1736
 
                                       host=None, port=None, realm=None,
1737
 
                                       path=None):
1738
 
        if host is None:
1739
 
            host = 'bar.org'
1740
 
        username = 'jim'
1741
 
        expected_prompt = expected_prompt_format % {
1742
 
            'scheme': scheme, 'host': host, 'port': port,
1743
 
            'realm': realm}
1744
 
        stdout = tests.StringIOWrapper()
1745
 
        stderr = tests.StringIOWrapper()
1746
 
        ui.ui_factory = tests.TestUIFactory(stdin=username+ '\n',
1747
 
                                            stdout=stdout, stderr=stderr)
1748
 
        # We use an empty conf so that the user is always prompted
1749
 
        conf = config.AuthenticationConfig()
1750
 
        self.assertEquals(username, conf.get_user(scheme, host, port=port,
1751
 
                          realm=realm, path=path, ask=True))
1752
 
        self.assertEquals(expected_prompt, stderr.getvalue())
1753
 
        self.assertEquals('', stdout.getvalue())
1754
 
 
1755
 
    def test_username_defaults_prompts(self):
1756
 
        # HTTP prompts can't be tested here, see test_http.py
1757
 
        self._check_default_username_prompt('FTP %(host)s username: ', 'ftp')
1758
 
        self._check_default_username_prompt(
1759
 
            'FTP %(host)s:%(port)d username: ', 'ftp', port=10020)
1760
 
        self._check_default_username_prompt(
1761
 
            'SSH %(host)s:%(port)d username: ', 'ssh', port=12345)
1762
 
 
1763
 
    def test_username_default_no_prompt(self):
1764
 
        conf = config.AuthenticationConfig()
1765
 
        self.assertEquals(None,
1766
 
            conf.get_user('ftp', 'example.com'))
1767
 
        self.assertEquals("explicitdefault",
1768
 
            conf.get_user('ftp', 'example.com', default="explicitdefault"))
1769
 
 
1770
 
    def test_password_default_prompts(self):
1771
 
        # HTTP prompts can't be tested here, see test_http.py
1772
 
        self._check_default_password_prompt(
1773
 
            'FTP %(user)s@%(host)s password: ', 'ftp')
1774
 
        self._check_default_password_prompt(
1775
 
            'FTP %(user)s@%(host)s:%(port)d password: ', 'ftp', port=10020)
1776
 
        self._check_default_password_prompt(
1777
 
            'SSH %(user)s@%(host)s:%(port)d password: ', 'ssh', port=12345)
1778
 
        # SMTP port handling is a bit special (it's handled if embedded in the
1779
 
        # host too)
1780
 
        # FIXME: should we: forbid that, extend it to other schemes, leave
1781
 
        # things as they are that's fine thank you ?
1782
 
        self._check_default_password_prompt('SMTP %(user)s@%(host)s password: ',
1783
 
                                            'smtp')
1784
 
        self._check_default_password_prompt('SMTP %(user)s@%(host)s password: ',
1785
 
                                            'smtp', host='bar.org:10025')
1786
 
        self._check_default_password_prompt(
1787
 
            'SMTP %(user)s@%(host)s:%(port)d password: ',
1788
 
            'smtp', port=10025)
1789
 
 
1790
 
    def test_ssh_password_emits_warning(self):
1791
 
        conf = config.AuthenticationConfig(_file=StringIO(
1792
 
                """
1793
 
[ssh with password]
1794
 
scheme=ssh
1795
 
host=bar.org
1796
 
user=jim
1797
 
password=jimpass
1798
 
"""))
1799
 
        entered_password = 'typed-by-hand'
1800
 
        stdout = tests.StringIOWrapper()
1801
 
        stderr = tests.StringIOWrapper()
1802
 
        ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
1803
 
                                            stdout=stdout, stderr=stderr)
1804
 
 
1805
 
        # Since the password defined in the authentication config is ignored,
1806
 
        # the user is prompted
1807
 
        self.assertEquals(entered_password,
1808
 
                          conf.get_password('ssh', 'bar.org', user='jim'))
1809
 
        self.assertContainsRe(
1810
 
            self.get_log(),
1811
 
            'password ignored in section \[ssh with password\]')
1812
 
 
1813
 
    def test_ssh_without_password_doesnt_emit_warning(self):
1814
 
        conf = config.AuthenticationConfig(_file=StringIO(
1815
 
                """
1816
 
[ssh with password]
1817
 
scheme=ssh
1818
 
host=bar.org
1819
 
user=jim
1820
 
"""))
1821
 
        entered_password = 'typed-by-hand'
1822
 
        stdout = tests.StringIOWrapper()
1823
 
        stderr = tests.StringIOWrapper()
1824
 
        ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
1825
 
                                            stdout=stdout,
1826
 
                                            stderr=stderr)
1827
 
 
1828
 
        # Since the password defined in the authentication config is ignored,
1829
 
        # the user is prompted
1830
 
        self.assertEquals(entered_password,
1831
 
                          conf.get_password('ssh', 'bar.org', user='jim'))
1832
 
        # No warning shoud be emitted since there is no password. We are only
1833
 
        # providing "user".
1834
 
        self.assertNotContainsRe(
1835
 
            self.get_log(),
1836
 
            'password ignored in section \[ssh with password\]')
1837
 
 
1838
 
    def test_uses_fallback_stores(self):
1839
 
        self.overrideAttr(config, 'credential_store_registry',
1840
 
                          config.CredentialStoreRegistry())
1841
 
        store = StubCredentialStore()
1842
 
        store.add_credentials("http", "example.com", "joe", "secret")
1843
 
        config.credential_store_registry.register("stub", store, fallback=True)
1844
 
        conf = config.AuthenticationConfig(_file=StringIO())
1845
 
        creds = conf.get_credentials("http", "example.com")
1846
 
        self.assertEquals("joe", creds["user"])
1847
 
        self.assertEquals("secret", creds["password"])
1848
 
 
1849
 
 
1850
 
class StubCredentialStore(config.CredentialStore):
1851
 
 
1852
 
    def __init__(self):
1853
 
        self._username = {}
1854
 
        self._password = {}
1855
 
 
1856
 
    def add_credentials(self, scheme, host, user, password=None):
1857
 
        self._username[(scheme, host)] = user
1858
 
        self._password[(scheme, host)] = password
1859
 
 
1860
 
    def get_credentials(self, scheme, host, port=None, user=None,
1861
 
        path=None, realm=None):
1862
 
        key = (scheme, host)
1863
 
        if not key in self._username:
1864
 
            return None
1865
 
        return { "scheme": scheme, "host": host, "port": port,
1866
 
                "user": self._username[key], "password": self._password[key]}
1867
 
 
1868
 
 
1869
 
class CountingCredentialStore(config.CredentialStore):
1870
 
 
1871
 
    def __init__(self):
1872
 
        self._calls = 0
1873
 
 
1874
 
    def get_credentials(self, scheme, host, port=None, user=None,
1875
 
        path=None, realm=None):
1876
 
        self._calls += 1
1877
 
        return None
1878
 
 
1879
 
 
1880
 
class TestCredentialStoreRegistry(tests.TestCase):
1881
 
 
1882
 
    def _get_cs_registry(self):
1883
 
        return config.credential_store_registry
1884
 
 
1885
 
    def test_default_credential_store(self):
1886
 
        r = self._get_cs_registry()
1887
 
        default = r.get_credential_store(None)
1888
 
        self.assertIsInstance(default, config.PlainTextCredentialStore)
1889
 
 
1890
 
    def test_unknown_credential_store(self):
1891
 
        r = self._get_cs_registry()
1892
 
        # It's hard to imagine someone creating a credential store named
1893
 
        # 'unknown' so we use that as an never registered key.
1894
 
        self.assertRaises(KeyError, r.get_credential_store, 'unknown')
1895
 
 
1896
 
    def test_fallback_none_registered(self):
1897
 
        r = config.CredentialStoreRegistry()
1898
 
        self.assertEquals(None,
1899
 
                          r.get_fallback_credentials("http", "example.com"))
1900
 
 
1901
 
    def test_register(self):
1902
 
        r = config.CredentialStoreRegistry()
1903
 
        r.register("stub", StubCredentialStore(), fallback=False)
1904
 
        r.register("another", StubCredentialStore(), fallback=True)
1905
 
        self.assertEquals(["another", "stub"], r.keys())
1906
 
 
1907
 
    def test_register_lazy(self):
1908
 
        r = config.CredentialStoreRegistry()
1909
 
        r.register_lazy("stub", "bzrlib.tests.test_config",
1910
 
                        "StubCredentialStore", fallback=False)
1911
 
        self.assertEquals(["stub"], r.keys())
1912
 
        self.assertIsInstance(r.get_credential_store("stub"),
1913
 
                              StubCredentialStore)
1914
 
 
1915
 
    def test_is_fallback(self):
1916
 
        r = config.CredentialStoreRegistry()
1917
 
        r.register("stub1", None, fallback=False)
1918
 
        r.register("stub2", None, fallback=True)
1919
 
        self.assertEquals(False, r.is_fallback("stub1"))
1920
 
        self.assertEquals(True, r.is_fallback("stub2"))
1921
 
 
1922
 
    def test_no_fallback(self):
1923
 
        r = config.CredentialStoreRegistry()
1924
 
        store = CountingCredentialStore()
1925
 
        r.register("count", store, fallback=False)
1926
 
        self.assertEquals(None,
1927
 
                          r.get_fallback_credentials("http", "example.com"))
1928
 
        self.assertEquals(0, store._calls)
1929
 
 
1930
 
    def test_fallback_credentials(self):
1931
 
        r = config.CredentialStoreRegistry()
1932
 
        store = StubCredentialStore()
1933
 
        store.add_credentials("http", "example.com",
1934
 
                              "somebody", "geheim")
1935
 
        r.register("stub", store, fallback=True)
1936
 
        creds = r.get_fallback_credentials("http", "example.com")
1937
 
        self.assertEquals("somebody", creds["user"])
1938
 
        self.assertEquals("geheim", creds["password"])
1939
 
 
1940
 
    def test_fallback_first_wins(self):
1941
 
        r = config.CredentialStoreRegistry()
1942
 
        stub1 = StubCredentialStore()
1943
 
        stub1.add_credentials("http", "example.com",
1944
 
                              "somebody", "stub1")
1945
 
        r.register("stub1", stub1, fallback=True)
1946
 
        stub2 = StubCredentialStore()
1947
 
        stub2.add_credentials("http", "example.com",
1948
 
                              "somebody", "stub2")
1949
 
        r.register("stub2", stub1, fallback=True)
1950
 
        creds = r.get_fallback_credentials("http", "example.com")
1951
 
        self.assertEquals("somebody", creds["user"])
1952
 
        self.assertEquals("stub1", creds["password"])
1953
 
 
1954
 
 
1955
 
class TestPlainTextCredentialStore(tests.TestCase):
1956
 
 
1957
 
    def test_decode_password(self):
1958
 
        r = config.credential_store_registry
1959
 
        plain_text = r.get_credential_store()
1960
 
        decoded = plain_text.decode_password(dict(password='secret'))
1961
 
        self.assertEquals('secret', decoded)
1962
 
 
1963
 
 
1964
 
# FIXME: Once we have a way to declare authentication to all test servers, we
1965
 
# can implement generic tests.
1966
 
# test_user_password_in_url
1967
 
# test_user_in_url_password_from_config
1968
 
# test_user_in_url_password_prompted
1969
 
# test_user_in_config
1970
 
# test_user_getpass.getuser
1971
 
# test_user_prompted ?
1972
 
class TestAuthenticationRing(tests.TestCaseWithTransport):
1973
 
    pass