~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_config.py

  • Committer: Jelmer Vernooij
  • Date: 2011-02-26 15:39:49 UTC
  • mto: (5691.1.1 bzr.dev)
  • mto: This revision was merged to the branch mainline in revision 5692.
  • Revision ID: jelmer@samba.org-20110226153949-o0fk909b30g7z570
Fix the use of "bzr tags" in branches with ghosts in their mainline /and/ tags on revisions not in the branch ancestry.

Show diffs side-by-side

added added

removed removed

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