~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_config.py

  • Committer: Martin Pool
  • Date: 2005-09-13 06:24:27 UTC
  • Revision ID: mbp@sourcefrog.net-20050913062426-330489777cdc9099
- more progress on fetch on top of weaves

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
2
 
#
3
 
# This program is free software; you can redistribute it and/or modify
4
 
# it under the terms of the GNU General Public License as published by
5
 
# the Free Software Foundation; either version 2 of the License, or
6
 
# (at your option) any later version.
7
 
#
8
 
# This program is distributed in the hope that it will be useful,
9
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
 
# GNU General Public License for more details.
12
 
#
13
 
# You should have received a copy of the GNU General Public License
14
 
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
 
 
17
 
"""Tests for finding and reading the bzr config file[s]."""
18
 
# import system imports here
19
 
from cStringIO import StringIO
20
 
import os
21
 
import sys
22
 
import threading
23
 
 
24
 
 
25
 
from testtools import matchers
26
 
 
27
 
#import bzrlib specific imports here
28
 
from bzrlib import (
29
 
    branch,
30
 
    bzrdir,
31
 
    config,
32
 
    diff,
33
 
    errors,
34
 
    osutils,
35
 
    mail_client,
36
 
    mergetools,
37
 
    ui,
38
 
    urlutils,
39
 
    registry,
40
 
    tests,
41
 
    trace,
42
 
    transport,
43
 
    )
44
 
from bzrlib.tests import (
45
 
    features,
46
 
    TestSkipped,
47
 
    scenarios,
48
 
    )
49
 
from bzrlib.util.configobj import configobj
50
 
 
51
 
 
52
 
def lockable_config_scenarios():
53
 
    return [
54
 
        ('global',
55
 
         {'config_class': config.GlobalConfig,
56
 
          'config_args': [],
57
 
          'config_section': 'DEFAULT'}),
58
 
        ('locations',
59
 
         {'config_class': config.LocationConfig,
60
 
          'config_args': ['.'],
61
 
          'config_section': '.'}),]
62
 
 
63
 
 
64
 
load_tests = scenarios.load_tests_apply_scenarios
65
 
 
66
 
# We need adapters that can build a config store in a test context. Test
67
 
# classes, based on TestCaseWithTransport, can use the registry to parametrize
68
 
# themselves. The builder will receive a test instance and should return a
69
 
# ready-to-use store.  Plugins that defines new stores can also register
70
 
# themselves here to be tested against the tests defined below.
71
 
 
72
 
# FIXME: plugins should *not* need to import test_config to register their
73
 
# helpers (or selftest -s xxx will be broken), the following registry should be
74
 
# moved to bzrlib.config instead so that selftest -s bt.test_config also runs
75
 
# the plugin specific tests (selftest -s bp.xxx won't, that would be against
76
 
# the spirit of '-s') -- vila 20110503
77
 
test_store_builder_registry = registry.Registry()
78
 
test_store_builder_registry.register(
79
 
    'configobj', lambda test: config.IniFileStore(test.get_transport(),
80
 
                                                  'configobj.conf'))
81
 
test_store_builder_registry.register(
82
 
    'bazaar', lambda test: config.GlobalStore())
83
 
test_store_builder_registry.register(
84
 
    'location', lambda test: config.LocationStore())
85
 
test_store_builder_registry.register(
86
 
    'branch', lambda test: config.BranchStore(test.branch))
87
 
 
88
 
# FIXME: Same remark as above for the following registry -- vila 20110503
89
 
test_stack_builder_registry = registry.Registry()
90
 
test_stack_builder_registry.register(
91
 
    'bazaar', lambda test: config.GlobalStack())
92
 
test_stack_builder_registry.register(
93
 
    'location', lambda test: config.LocationStack('.'))
94
 
test_stack_builder_registry.register(
95
 
    'branch', lambda test: config.BranchStack(test.branch))
96
 
 
97
 
 
98
 
sample_long_alias="log -r-15..-1 --line"
99
 
sample_config_text = u"""
100
 
[DEFAULT]
101
 
email=Erik B\u00e5gfors <erik@bagfors.nu>
102
 
editor=vim
103
 
change_editor=vimdiff -of @new_path @old_path
104
 
gpg_signing_command=gnome-gpg
105
 
log_format=short
106
 
user_global_option=something
107
 
bzr.mergetool.sometool=sometool {base} {this} {other} -o {result}
108
 
bzr.mergetool.funkytool=funkytool "arg with spaces" {this_temp}
109
 
bzr.default_mergetool=sometool
110
 
[ALIASES]
111
 
h=help
112
 
ll=""" + sample_long_alias + "\n"
113
 
 
114
 
 
115
 
sample_always_signatures = """
116
 
[DEFAULT]
117
 
check_signatures=ignore
118
 
create_signatures=always
119
 
"""
120
 
 
121
 
sample_ignore_signatures = """
122
 
[DEFAULT]
123
 
check_signatures=require
124
 
create_signatures=never
125
 
"""
126
 
 
127
 
sample_maybe_signatures = """
128
 
[DEFAULT]
129
 
check_signatures=ignore
130
 
create_signatures=when-required
131
 
"""
132
 
 
133
 
sample_branches_text = """
134
 
[http://www.example.com]
135
 
# Top level policy
136
 
email=Robert Collins <robertc@example.org>
137
 
normal_option = normal
138
 
appendpath_option = append
139
 
appendpath_option:policy = appendpath
140
 
norecurse_option = norecurse
141
 
norecurse_option:policy = norecurse
142
 
[http://www.example.com/ignoreparent]
143
 
# different project: ignore parent dir config
144
 
ignore_parents=true
145
 
[http://www.example.com/norecurse]
146
 
# configuration items that only apply to this dir
147
 
recurse=false
148
 
normal_option = norecurse
149
 
[http://www.example.com/dir]
150
 
appendpath_option = normal
151
 
[/b/]
152
 
check_signatures=require
153
 
# test trailing / matching with no children
154
 
[/a/]
155
 
check_signatures=check-available
156
 
gpg_signing_command=false
157
 
user_local_option=local
158
 
# test trailing / matching
159
 
[/a/*]
160
 
#subdirs will match but not the parent
161
 
[/a/c]
162
 
check_signatures=ignore
163
 
post_commit=bzrlib.tests.test_config.post_commit
164
 
#testing explicit beats globs
165
 
"""
166
 
 
167
 
 
168
 
def create_configs(test):
169
 
    """Create configuration files for a given test.
170
 
 
171
 
    This requires creating a tree (and populate the ``test.tree`` attribute)
172
 
    and its associated branch and will populate the following attributes:
173
 
 
174
 
    - branch_config: A BranchConfig for the associated branch.
175
 
 
176
 
    - locations_config : A LocationConfig for the associated branch
177
 
 
178
 
    - bazaar_config: A GlobalConfig.
179
 
 
180
 
    The tree and branch are created in a 'tree' subdirectory so the tests can
181
 
    still use the test directory to stay outside of the branch.
182
 
    """
183
 
    tree = test.make_branch_and_tree('tree')
184
 
    test.tree = tree
185
 
    test.branch_config = config.BranchConfig(tree.branch)
186
 
    test.locations_config = config.LocationConfig(tree.basedir)
187
 
    test.bazaar_config = config.GlobalConfig()
188
 
 
189
 
 
190
 
def create_configs_with_file_option(test):
191
 
    """Create configuration files with a ``file`` option set in each.
192
 
 
193
 
    This builds on ``create_configs`` and add one ``file`` option in each
194
 
    configuration with a value which allows identifying the configuration file.
195
 
    """
196
 
    create_configs(test)
197
 
    test.bazaar_config.set_user_option('file', 'bazaar')
198
 
    test.locations_config.set_user_option('file', 'locations')
199
 
    test.branch_config.set_user_option('file', 'branch')
200
 
 
201
 
 
202
 
class TestOptionsMixin:
203
 
 
204
 
    def assertOptions(self, expected, conf):
205
 
        # We don't care about the parser (as it will make tests hard to write
206
 
        # and error-prone anyway)
207
 
        self.assertThat([opt[:4] for opt in conf._get_options()],
208
 
                        matchers.Equals(expected))
209
 
 
210
 
 
211
 
class InstrumentedConfigObj(object):
212
 
    """A config obj look-enough-alike to record calls made to it."""
213
 
 
214
 
    def __contains__(self, thing):
215
 
        self._calls.append(('__contains__', thing))
216
 
        return False
217
 
 
218
 
    def __getitem__(self, key):
219
 
        self._calls.append(('__getitem__', key))
220
 
        return self
221
 
 
222
 
    def __init__(self, input, encoding=None):
223
 
        self._calls = [('__init__', input, encoding)]
224
 
 
225
 
    def __setitem__(self, key, value):
226
 
        self._calls.append(('__setitem__', key, value))
227
 
 
228
 
    def __delitem__(self, key):
229
 
        self._calls.append(('__delitem__', key))
230
 
 
231
 
    def keys(self):
232
 
        self._calls.append(('keys',))
233
 
        return []
234
 
 
235
 
    def reload(self):
236
 
        self._calls.append(('reload',))
237
 
 
238
 
    def write(self, arg):
239
 
        self._calls.append(('write',))
240
 
 
241
 
    def as_bool(self, value):
242
 
        self._calls.append(('as_bool', value))
243
 
        return False
244
 
 
245
 
    def get_value(self, section, name):
246
 
        self._calls.append(('get_value', section, name))
247
 
        return None
248
 
 
249
 
 
250
 
class FakeBranch(object):
251
 
 
252
 
    def __init__(self, base=None, user_id=None):
253
 
        if base is None:
254
 
            self.base = "http://example.com/branches/demo"
255
 
        else:
256
 
            self.base = base
257
 
        self._transport = self.control_files = \
258
 
            FakeControlFilesAndTransport(user_id=user_id)
259
 
 
260
 
    def _get_config(self):
261
 
        return config.TransportConfig(self._transport, 'branch.conf')
262
 
 
263
 
    def lock_write(self):
264
 
        pass
265
 
 
266
 
    def unlock(self):
267
 
        pass
268
 
 
269
 
 
270
 
class FakeControlFilesAndTransport(object):
271
 
 
272
 
    def __init__(self, user_id=None):
273
 
        self.files = {}
274
 
        if user_id:
275
 
            self.files['email'] = user_id
276
 
        self._transport = self
277
 
 
278
 
    def get_utf8(self, filename):
279
 
        # from LockableFiles
280
 
        raise AssertionError("get_utf8 should no longer be used")
281
 
 
282
 
    def get(self, filename):
283
 
        # from Transport
284
 
        try:
285
 
            return StringIO(self.files[filename])
286
 
        except KeyError:
287
 
            raise errors.NoSuchFile(filename)
288
 
 
289
 
    def get_bytes(self, filename):
290
 
        # from Transport
291
 
        try:
292
 
            return self.files[filename]
293
 
        except KeyError:
294
 
            raise errors.NoSuchFile(filename)
295
 
 
296
 
    def put(self, filename, fileobj):
297
 
        self.files[filename] = fileobj.read()
298
 
 
299
 
    def put_file(self, filename, fileobj):
300
 
        return self.put(filename, fileobj)
301
 
 
302
 
 
303
 
class InstrumentedConfig(config.Config):
304
 
    """An instrumented config that supplies stubs for template methods."""
305
 
 
306
 
    def __init__(self):
307
 
        super(InstrumentedConfig, self).__init__()
308
 
        self._calls = []
309
 
        self._signatures = config.CHECK_NEVER
310
 
 
311
 
    def _get_user_id(self):
312
 
        self._calls.append('_get_user_id')
313
 
        return "Robert Collins <robert.collins@example.org>"
314
 
 
315
 
    def _get_signature_checking(self):
316
 
        self._calls.append('_get_signature_checking')
317
 
        return self._signatures
318
 
 
319
 
    def _get_change_editor(self):
320
 
        self._calls.append('_get_change_editor')
321
 
        return 'vimdiff -fo @new_path @old_path'
322
 
 
323
 
 
324
 
bool_config = """[DEFAULT]
325
 
active = true
326
 
inactive = false
327
 
[UPPERCASE]
328
 
active = True
329
 
nonactive = False
330
 
"""
331
 
 
332
 
 
333
 
class TestConfigObj(tests.TestCase):
334
 
 
335
 
    def test_get_bool(self):
336
 
        co = config.ConfigObj(StringIO(bool_config))
337
 
        self.assertIs(co.get_bool('DEFAULT', 'active'), True)
338
 
        self.assertIs(co.get_bool('DEFAULT', 'inactive'), False)
339
 
        self.assertIs(co.get_bool('UPPERCASE', 'active'), True)
340
 
        self.assertIs(co.get_bool('UPPERCASE', 'nonactive'), False)
341
 
 
342
 
    def test_hash_sign_in_value(self):
343
 
        """
344
 
        Before 4.5.0, ConfigObj did not quote # signs in values, so they'd be
345
 
        treated as comments when read in again. (#86838)
346
 
        """
347
 
        co = config.ConfigObj()
348
 
        co['test'] = 'foo#bar'
349
 
        outfile = StringIO()
350
 
        co.write(outfile=outfile)
351
 
        lines = outfile.getvalue().splitlines()
352
 
        self.assertEqual(lines, ['test = "foo#bar"'])
353
 
        co2 = config.ConfigObj(lines)
354
 
        self.assertEqual(co2['test'], 'foo#bar')
355
 
 
356
 
    def test_triple_quotes(self):
357
 
        # Bug #710410: if the value string has triple quotes
358
 
        # then ConfigObj versions up to 4.7.2 will quote them wrong
359
 
        # and won't able to read them back
360
 
        triple_quotes_value = '''spam
361
 
""" that's my spam """
362
 
eggs'''
363
 
        co = config.ConfigObj()
364
 
        co['test'] = triple_quotes_value
365
 
        # While writing this test another bug in ConfigObj has been found:
366
 
        # method co.write() without arguments produces list of lines
367
 
        # one option per line, and multiline values are not split
368
 
        # across multiple lines,
369
 
        # and that breaks the parsing these lines back by ConfigObj.
370
 
        # This issue only affects test, but it's better to avoid
371
 
        # `co.write()` construct at all.
372
 
        # [bialix 20110222] bug report sent to ConfigObj's author
373
 
        outfile = StringIO()
374
 
        co.write(outfile=outfile)
375
 
        output = outfile.getvalue()
376
 
        # now we're trying to read it back
377
 
        co2 = config.ConfigObj(StringIO(output))
378
 
        self.assertEquals(triple_quotes_value, co2['test'])
379
 
 
380
 
 
381
 
erroneous_config = """[section] # line 1
382
 
good=good # line 2
383
 
[section] # line 3
384
 
whocares=notme # line 4
385
 
"""
386
 
 
387
 
 
388
 
class TestConfigObjErrors(tests.TestCase):
389
 
 
390
 
    def test_duplicate_section_name_error_line(self):
391
 
        try:
392
 
            co = configobj.ConfigObj(StringIO(erroneous_config),
393
 
                                     raise_errors=True)
394
 
        except config.configobj.DuplicateError, e:
395
 
            self.assertEqual(3, e.line_number)
396
 
        else:
397
 
            self.fail('Error in config file not detected')
398
 
 
399
 
 
400
 
class TestConfig(tests.TestCase):
401
 
 
402
 
    def test_constructs(self):
403
 
        config.Config()
404
 
 
405
 
    def test_no_default_editor(self):
406
 
        self.assertRaises(NotImplementedError, config.Config().get_editor)
407
 
 
408
 
    def test_user_email(self):
409
 
        my_config = InstrumentedConfig()
410
 
        self.assertEqual('robert.collins@example.org', my_config.user_email())
411
 
        self.assertEqual(['_get_user_id'], my_config._calls)
412
 
 
413
 
    def test_username(self):
414
 
        my_config = InstrumentedConfig()
415
 
        self.assertEqual('Robert Collins <robert.collins@example.org>',
416
 
                         my_config.username())
417
 
        self.assertEqual(['_get_user_id'], my_config._calls)
418
 
 
419
 
    def test_signatures_default(self):
420
 
        my_config = config.Config()
421
 
        self.assertFalse(my_config.signature_needed())
422
 
        self.assertEqual(config.CHECK_IF_POSSIBLE,
423
 
                         my_config.signature_checking())
424
 
        self.assertEqual(config.SIGN_WHEN_REQUIRED,
425
 
                         my_config.signing_policy())
426
 
 
427
 
    def test_signatures_template_method(self):
428
 
        my_config = InstrumentedConfig()
429
 
        self.assertEqual(config.CHECK_NEVER, my_config.signature_checking())
430
 
        self.assertEqual(['_get_signature_checking'], my_config._calls)
431
 
 
432
 
    def test_signatures_template_method_none(self):
433
 
        my_config = InstrumentedConfig()
434
 
        my_config._signatures = None
435
 
        self.assertEqual(config.CHECK_IF_POSSIBLE,
436
 
                         my_config.signature_checking())
437
 
        self.assertEqual(['_get_signature_checking'], my_config._calls)
438
 
 
439
 
    def test_gpg_signing_command_default(self):
440
 
        my_config = config.Config()
441
 
        self.assertEqual('gpg', my_config.gpg_signing_command())
442
 
 
443
 
    def test_get_user_option_default(self):
444
 
        my_config = config.Config()
445
 
        self.assertEqual(None, my_config.get_user_option('no_option'))
446
 
 
447
 
    def test_post_commit_default(self):
448
 
        my_config = config.Config()
449
 
        self.assertEqual(None, my_config.post_commit())
450
 
 
451
 
    def test_log_format_default(self):
452
 
        my_config = config.Config()
453
 
        self.assertEqual('long', my_config.log_format())
454
 
 
455
 
    def test_get_change_editor(self):
456
 
        my_config = InstrumentedConfig()
457
 
        change_editor = my_config.get_change_editor('old_tree', 'new_tree')
458
 
        self.assertEqual(['_get_change_editor'], my_config._calls)
459
 
        self.assertIs(diff.DiffFromTool, change_editor.__class__)
460
 
        self.assertEqual(['vimdiff', '-fo', '@new_path', '@old_path'],
461
 
                         change_editor.command_template)
462
 
 
463
 
 
464
 
class TestConfigPath(tests.TestCase):
465
 
 
466
 
    def setUp(self):
467
 
        super(TestConfigPath, self).setUp()
468
 
        self.overrideEnv('HOME', '/home/bogus')
469
 
        self.overrideEnv('XDG_CACHE_DIR', '')
470
 
        if sys.platform == 'win32':
471
 
            self.overrideEnv(
472
 
                'BZR_HOME', r'C:\Documents and Settings\bogus\Application Data')
473
 
            self.bzr_home = \
474
 
                'C:/Documents and Settings/bogus/Application Data/bazaar/2.0'
475
 
        else:
476
 
            self.bzr_home = '/home/bogus/.bazaar'
477
 
 
478
 
    def test_config_dir(self):
479
 
        self.assertEqual(config.config_dir(), self.bzr_home)
480
 
 
481
 
    def test_config_filename(self):
482
 
        self.assertEqual(config.config_filename(),
483
 
                         self.bzr_home + '/bazaar.conf')
484
 
 
485
 
    def test_locations_config_filename(self):
486
 
        self.assertEqual(config.locations_config_filename(),
487
 
                         self.bzr_home + '/locations.conf')
488
 
 
489
 
    def test_authentication_config_filename(self):
490
 
        self.assertEqual(config.authentication_config_filename(),
491
 
                         self.bzr_home + '/authentication.conf')
492
 
 
493
 
    def test_xdg_cache_dir(self):
494
 
        self.assertEqual(config.xdg_cache_dir(),
495
 
            '/home/bogus/.cache')
496
 
 
497
 
 
498
 
class TestXDGConfigDir(tests.TestCaseInTempDir):
499
 
    # must be in temp dir because config tests for the existence of the bazaar
500
 
    # subdirectory of $XDG_CONFIG_HOME
501
 
 
502
 
    def setUp(self):
503
 
        if sys.platform in ('darwin', 'win32'):
504
 
            raise tests.TestNotApplicable(
505
 
                'XDG config dir not used on this platform')
506
 
        super(TestXDGConfigDir, self).setUp()
507
 
        self.overrideEnv('HOME', self.test_home_dir)
508
 
        # BZR_HOME overrides everything we want to test so unset it.
509
 
        self.overrideEnv('BZR_HOME', None)
510
 
 
511
 
    def test_xdg_config_dir_exists(self):
512
 
        """When ~/.config/bazaar exists, use it as the config dir."""
513
 
        newdir = osutils.pathjoin(self.test_home_dir, '.config', 'bazaar')
514
 
        os.makedirs(newdir)
515
 
        self.assertEqual(config.config_dir(), newdir)
516
 
 
517
 
    def test_xdg_config_home(self):
518
 
        """When XDG_CONFIG_HOME is set, use it."""
519
 
        xdgconfigdir = osutils.pathjoin(self.test_home_dir, 'xdgconfig')
520
 
        self.overrideEnv('XDG_CONFIG_HOME', xdgconfigdir)
521
 
        newdir = osutils.pathjoin(xdgconfigdir, 'bazaar')
522
 
        os.makedirs(newdir)
523
 
        self.assertEqual(config.config_dir(), newdir)
524
 
 
525
 
 
526
 
class TestIniConfig(tests.TestCaseInTempDir):
527
 
 
528
 
    def make_config_parser(self, s):
529
 
        conf = config.IniBasedConfig.from_string(s)
530
 
        return conf, conf._get_parser()
531
 
 
532
 
 
533
 
class TestIniConfigBuilding(TestIniConfig):
534
 
 
535
 
    def test_contructs(self):
536
 
        my_config = config.IniBasedConfig()
537
 
 
538
 
    def test_from_fp(self):
539
 
        my_config = config.IniBasedConfig.from_string(sample_config_text)
540
 
        self.assertIsInstance(my_config._get_parser(), configobj.ConfigObj)
541
 
 
542
 
    def test_cached(self):
543
 
        my_config = config.IniBasedConfig.from_string(sample_config_text)
544
 
        parser = my_config._get_parser()
545
 
        self.assertTrue(my_config._get_parser() is parser)
546
 
 
547
 
    def _dummy_chown(self, path, uid, gid):
548
 
        self.path, self.uid, self.gid = path, uid, gid
549
 
 
550
 
    def test_ini_config_ownership(self):
551
 
        """Ensure that chown is happening during _write_config_file"""
552
 
        self.requireFeature(features.chown_feature)
553
 
        self.overrideAttr(os, 'chown', self._dummy_chown)
554
 
        self.path = self.uid = self.gid = None
555
 
        conf = config.IniBasedConfig(file_name='./foo.conf')
556
 
        conf._write_config_file()
557
 
        self.assertEquals(self.path, './foo.conf')
558
 
        self.assertTrue(isinstance(self.uid, int))
559
 
        self.assertTrue(isinstance(self.gid, int))
560
 
 
561
 
    def test_get_filename_parameter_is_deprecated_(self):
562
 
        conf = self.callDeprecated([
563
 
            'IniBasedConfig.__init__(get_filename) was deprecated in 2.3.'
564
 
            ' Use file_name instead.'],
565
 
            config.IniBasedConfig, lambda: 'ini.conf')
566
 
        self.assertEqual('ini.conf', conf.file_name)
567
 
 
568
 
    def test_get_parser_file_parameter_is_deprecated_(self):
569
 
        config_file = StringIO(sample_config_text.encode('utf-8'))
570
 
        conf = config.IniBasedConfig.from_string(sample_config_text)
571
 
        conf = self.callDeprecated([
572
 
            'IniBasedConfig._get_parser(file=xxx) was deprecated in 2.3.'
573
 
            ' Use IniBasedConfig(_content=xxx) instead.'],
574
 
            conf._get_parser, file=config_file)
575
 
 
576
 
 
577
 
class TestIniConfigSaving(tests.TestCaseInTempDir):
578
 
 
579
 
    def test_cant_save_without_a_file_name(self):
580
 
        conf = config.IniBasedConfig()
581
 
        self.assertRaises(AssertionError, conf._write_config_file)
582
 
 
583
 
    def test_saved_with_content(self):
584
 
        content = 'foo = bar\n'
585
 
        conf = config.IniBasedConfig.from_string(
586
 
            content, file_name='./test.conf', save=True)
587
 
        self.assertFileEqual(content, 'test.conf')
588
 
 
589
 
 
590
 
class TestIniConfigOptionExpansionDefaultValue(tests.TestCaseInTempDir):
591
 
    """What is the default value of expand for config options.
592
 
 
593
 
    This is an opt-in beta feature used to evaluate whether or not option
594
 
    references can appear in dangerous place raising exceptions, disapearing
595
 
    (and as such corrupting data) or if it's safe to activate the option by
596
 
    default.
597
 
 
598
 
    Note that these tests relies on config._expand_default_value being already
599
 
    overwritten in the parent class setUp.
600
 
    """
601
 
 
602
 
    def setUp(self):
603
 
        super(TestIniConfigOptionExpansionDefaultValue, self).setUp()
604
 
        self.config = None
605
 
        self.warnings = []
606
 
        def warning(*args):
607
 
            self.warnings.append(args[0] % args[1:])
608
 
        self.overrideAttr(trace, 'warning', warning)
609
 
 
610
 
    def get_config(self, expand):
611
 
        c = config.GlobalConfig.from_string('bzr.config.expand=%s' % (expand,),
612
 
                                            save=True)
613
 
        return c
614
 
 
615
 
    def assertExpandIs(self, expected):
616
 
        actual = config._get_expand_default_value()
617
 
        #self.config.get_user_option_as_bool('bzr.config.expand')
618
 
        self.assertEquals(expected, actual)
619
 
 
620
 
    def test_default_is_None(self):
621
 
        self.assertEquals(None, config._expand_default_value)
622
 
 
623
 
    def test_default_is_False_even_if_None(self):
624
 
        self.config = self.get_config(None)
625
 
        self.assertExpandIs(False)
626
 
 
627
 
    def test_default_is_False_even_if_invalid(self):
628
 
        self.config = self.get_config('<your choice>')
629
 
        self.assertExpandIs(False)
630
 
        # ...
631
 
        # Huh ? My choice is False ? Thanks, always happy to hear that :D
632
 
        # Wait, you've been warned !
633
 
        self.assertLength(1, self.warnings)
634
 
        self.assertEquals(
635
 
            'Value "<your choice>" is not a boolean for "bzr.config.expand"',
636
 
            self.warnings[0])
637
 
 
638
 
    def test_default_is_True(self):
639
 
        self.config = self.get_config(True)
640
 
        self.assertExpandIs(True)
641
 
        
642
 
    def test_default_is_False(self):
643
 
        self.config = self.get_config(False)
644
 
        self.assertExpandIs(False)
645
 
        
646
 
 
647
 
class TestIniConfigOptionExpansion(tests.TestCase):
648
 
    """Test option expansion from the IniConfig level.
649
 
 
650
 
    What we really want here is to test the Config level, but the class being
651
 
    abstract as far as storing values is concerned, this can't be done
652
 
    properly (yet).
653
 
    """
654
 
    # FIXME: This should be rewritten when all configs share a storage
655
 
    # implementation -- vila 2011-02-18
656
 
 
657
 
    def get_config(self, string=None):
658
 
        if string is None:
659
 
            string = ''
660
 
        c = config.IniBasedConfig.from_string(string)
661
 
        return c
662
 
 
663
 
    def assertExpansion(self, expected, conf, string, env=None):
664
 
        self.assertEquals(expected, conf.expand_options(string, env))
665
 
 
666
 
    def test_no_expansion(self):
667
 
        c = self.get_config('')
668
 
        self.assertExpansion('foo', c, 'foo')
669
 
 
670
 
    def test_env_adding_options(self):
671
 
        c = self.get_config('')
672
 
        self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
673
 
 
674
 
    def test_env_overriding_options(self):
675
 
        c = self.get_config('foo=baz')
676
 
        self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
677
 
 
678
 
    def test_simple_ref(self):
679
 
        c = self.get_config('foo=xxx')
680
 
        self.assertExpansion('xxx', c, '{foo}')
681
 
 
682
 
    def test_unknown_ref(self):
683
 
        c = self.get_config('')
684
 
        self.assertRaises(errors.ExpandingUnknownOption,
685
 
                          c.expand_options, '{foo}')
686
 
 
687
 
    def test_indirect_ref(self):
688
 
        c = self.get_config('''
689
 
foo=xxx
690
 
bar={foo}
691
 
''')
692
 
        self.assertExpansion('xxx', c, '{bar}')
693
 
 
694
 
    def test_embedded_ref(self):
695
 
        c = self.get_config('''
696
 
foo=xxx
697
 
bar=foo
698
 
''')
699
 
        self.assertExpansion('xxx', c, '{{bar}}')
700
 
 
701
 
    def test_simple_loop(self):
702
 
        c = self.get_config('foo={foo}')
703
 
        self.assertRaises(errors.OptionExpansionLoop, c.expand_options, '{foo}')
704
 
 
705
 
    def test_indirect_loop(self):
706
 
        c = self.get_config('''
707
 
foo={bar}
708
 
bar={baz}
709
 
baz={foo}''')
710
 
        e = self.assertRaises(errors.OptionExpansionLoop,
711
 
                              c.expand_options, '{foo}')
712
 
        self.assertEquals('foo->bar->baz', e.refs)
713
 
        self.assertEquals('{foo}', e.string)
714
 
 
715
 
    def test_list(self):
716
 
        conf = self.get_config('''
717
 
foo=start
718
 
bar=middle
719
 
baz=end
720
 
list={foo},{bar},{baz}
721
 
''')
722
 
        self.assertEquals(['start', 'middle', 'end'],
723
 
                           conf.get_user_option('list', expand=True))
724
 
 
725
 
    def test_cascading_list(self):
726
 
        conf = self.get_config('''
727
 
foo=start,{bar}
728
 
bar=middle,{baz}
729
 
baz=end
730
 
list={foo}
731
 
''')
732
 
        self.assertEquals(['start', 'middle', 'end'],
733
 
                           conf.get_user_option('list', expand=True))
734
 
 
735
 
    def test_pathological_hidden_list(self):
736
 
        conf = self.get_config('''
737
 
foo=bin
738
 
bar=go
739
 
start={foo
740
 
middle=},{
741
 
end=bar}
742
 
hidden={start}{middle}{end}
743
 
''')
744
 
        # Nope, it's either a string or a list, and the list wins as soon as a
745
 
        # ',' appears, so the string concatenation never occur.
746
 
        self.assertEquals(['{foo', '}', '{', 'bar}'],
747
 
                          conf.get_user_option('hidden', expand=True))
748
 
 
749
 
class TestLocationConfigOptionExpansion(tests.TestCaseInTempDir):
750
 
 
751
 
    def get_config(self, location, string=None):
752
 
        if string is None:
753
 
            string = ''
754
 
        # Since we don't save the config we won't strictly require to inherit
755
 
        # from TestCaseInTempDir, but an error occurs so quickly...
756
 
        c = config.LocationConfig.from_string(string, location)
757
 
        return c
758
 
 
759
 
    def test_dont_cross_unrelated_section(self):
760
 
        c = self.get_config('/another/branch/path','''
761
 
[/one/branch/path]
762
 
foo = hello
763
 
bar = {foo}/2
764
 
 
765
 
[/another/branch/path]
766
 
bar = {foo}/2
767
 
''')
768
 
        self.assertRaises(errors.ExpandingUnknownOption,
769
 
                          c.get_user_option, 'bar', expand=True)
770
 
 
771
 
    def test_cross_related_sections(self):
772
 
        c = self.get_config('/project/branch/path','''
773
 
[/project]
774
 
foo = qu
775
 
 
776
 
[/project/branch/path]
777
 
bar = {foo}ux
778
 
''')
779
 
        self.assertEquals('quux', c.get_user_option('bar', expand=True))
780
 
 
781
 
 
782
 
class TestIniBaseConfigOnDisk(tests.TestCaseInTempDir):
783
 
 
784
 
    def test_cannot_reload_without_name(self):
785
 
        conf = config.IniBasedConfig.from_string(sample_config_text)
786
 
        self.assertRaises(AssertionError, conf.reload)
787
 
 
788
 
    def test_reload_see_new_value(self):
789
 
        c1 = config.IniBasedConfig.from_string('editor=vim\n',
790
 
                                               file_name='./test/conf')
791
 
        c1._write_config_file()
792
 
        c2 = config.IniBasedConfig.from_string('editor=emacs\n',
793
 
                                               file_name='./test/conf')
794
 
        c2._write_config_file()
795
 
        self.assertEqual('vim', c1.get_user_option('editor'))
796
 
        self.assertEqual('emacs', c2.get_user_option('editor'))
797
 
        # Make sure we get the Right value
798
 
        c1.reload()
799
 
        self.assertEqual('emacs', c1.get_user_option('editor'))
800
 
 
801
 
 
802
 
class TestLockableConfig(tests.TestCaseInTempDir):
803
 
 
804
 
    scenarios = lockable_config_scenarios()
805
 
 
806
 
    # Set by load_tests
807
 
    config_class = None
808
 
    config_args = None
809
 
    config_section = None
810
 
 
811
 
    def setUp(self):
812
 
        super(TestLockableConfig, self).setUp()
813
 
        self._content = '[%s]\none=1\ntwo=2\n' % (self.config_section,)
814
 
        self.config = self.create_config(self._content)
815
 
 
816
 
    def get_existing_config(self):
817
 
        return self.config_class(*self.config_args)
818
 
 
819
 
    def create_config(self, content):
820
 
        kwargs = dict(save=True)
821
 
        c = self.config_class.from_string(content, *self.config_args, **kwargs)
822
 
        return c
823
 
 
824
 
    def test_simple_read_access(self):
825
 
        self.assertEquals('1', self.config.get_user_option('one'))
826
 
 
827
 
    def test_simple_write_access(self):
828
 
        self.config.set_user_option('one', 'one')
829
 
        self.assertEquals('one', self.config.get_user_option('one'))
830
 
 
831
 
    def test_listen_to_the_last_speaker(self):
832
 
        c1 = self.config
833
 
        c2 = self.get_existing_config()
834
 
        c1.set_user_option('one', 'ONE')
835
 
        c2.set_user_option('two', 'TWO')
836
 
        self.assertEquals('ONE', c1.get_user_option('one'))
837
 
        self.assertEquals('TWO', c2.get_user_option('two'))
838
 
        # The second update respect the first one
839
 
        self.assertEquals('ONE', c2.get_user_option('one'))
840
 
 
841
 
    def test_last_speaker_wins(self):
842
 
        # If the same config is not shared, the same variable modified twice
843
 
        # can only see a single result.
844
 
        c1 = self.config
845
 
        c2 = self.get_existing_config()
846
 
        c1.set_user_option('one', 'c1')
847
 
        c2.set_user_option('one', 'c2')
848
 
        self.assertEquals('c2', c2._get_user_option('one'))
849
 
        # The first modification is still available until another refresh
850
 
        # occur
851
 
        self.assertEquals('c1', c1._get_user_option('one'))
852
 
        c1.set_user_option('two', 'done')
853
 
        self.assertEquals('c2', c1._get_user_option('one'))
854
 
 
855
 
    def test_writes_are_serialized(self):
856
 
        c1 = self.config
857
 
        c2 = self.get_existing_config()
858
 
 
859
 
        # We spawn a thread that will pause *during* the write
860
 
        before_writing = threading.Event()
861
 
        after_writing = threading.Event()
862
 
        writing_done = threading.Event()
863
 
        c1_orig = c1._write_config_file
864
 
        def c1_write_config_file():
865
 
            before_writing.set()
866
 
            c1_orig()
867
 
            # The lock is held. We wait for the main thread to decide when to
868
 
            # continue
869
 
            after_writing.wait()
870
 
        c1._write_config_file = c1_write_config_file
871
 
        def c1_set_option():
872
 
            c1.set_user_option('one', 'c1')
873
 
            writing_done.set()
874
 
        t1 = threading.Thread(target=c1_set_option)
875
 
        # Collect the thread after the test
876
 
        self.addCleanup(t1.join)
877
 
        # Be ready to unblock the thread if the test goes wrong
878
 
        self.addCleanup(after_writing.set)
879
 
        t1.start()
880
 
        before_writing.wait()
881
 
        self.assertTrue(c1._lock.is_held)
882
 
        self.assertRaises(errors.LockContention,
883
 
                          c2.set_user_option, 'one', 'c2')
884
 
        self.assertEquals('c1', c1.get_user_option('one'))
885
 
        # Let the lock be released
886
 
        after_writing.set()
887
 
        writing_done.wait()
888
 
        c2.set_user_option('one', 'c2')
889
 
        self.assertEquals('c2', c2.get_user_option('one'))
890
 
 
891
 
    def test_read_while_writing(self):
892
 
       c1 = self.config
893
 
       # We spawn a thread that will pause *during* the write
894
 
       ready_to_write = threading.Event()
895
 
       do_writing = threading.Event()
896
 
       writing_done = threading.Event()
897
 
       c1_orig = c1._write_config_file
898
 
       def c1_write_config_file():
899
 
           ready_to_write.set()
900
 
           # The lock is held. We wait for the main thread to decide when to
901
 
           # continue
902
 
           do_writing.wait()
903
 
           c1_orig()
904
 
           writing_done.set()
905
 
       c1._write_config_file = c1_write_config_file
906
 
       def c1_set_option():
907
 
           c1.set_user_option('one', 'c1')
908
 
       t1 = threading.Thread(target=c1_set_option)
909
 
       # Collect the thread after the test
910
 
       self.addCleanup(t1.join)
911
 
       # Be ready to unblock the thread if the test goes wrong
912
 
       self.addCleanup(do_writing.set)
913
 
       t1.start()
914
 
       # Ensure the thread is ready to write
915
 
       ready_to_write.wait()
916
 
       self.assertTrue(c1._lock.is_held)
917
 
       self.assertEquals('c1', c1.get_user_option('one'))
918
 
       # If we read during the write, we get the old value
919
 
       c2 = self.get_existing_config()
920
 
       self.assertEquals('1', c2.get_user_option('one'))
921
 
       # Let the writing occur and ensure it occurred
922
 
       do_writing.set()
923
 
       writing_done.wait()
924
 
       # Now we get the updated value
925
 
       c3 = self.get_existing_config()
926
 
       self.assertEquals('c1', c3.get_user_option('one'))
927
 
 
928
 
 
929
 
class TestGetUserOptionAs(TestIniConfig):
930
 
 
931
 
    def test_get_user_option_as_bool(self):
932
 
        conf, parser = self.make_config_parser("""
933
 
a_true_bool = true
934
 
a_false_bool = 0
935
 
an_invalid_bool = maybe
936
 
a_list = hmm, who knows ? # This is interpreted as a list !
937
 
""")
938
 
        get_bool = conf.get_user_option_as_bool
939
 
        self.assertEqual(True, get_bool('a_true_bool'))
940
 
        self.assertEqual(False, get_bool('a_false_bool'))
941
 
        warnings = []
942
 
        def warning(*args):
943
 
            warnings.append(args[0] % args[1:])
944
 
        self.overrideAttr(trace, 'warning', warning)
945
 
        msg = 'Value "%s" is not a boolean for "%s"'
946
 
        self.assertIs(None, get_bool('an_invalid_bool'))
947
 
        self.assertEquals(msg % ('maybe', 'an_invalid_bool'), warnings[0])
948
 
        warnings = []
949
 
        self.assertIs(None, get_bool('not_defined_in_this_config'))
950
 
        self.assertEquals([], warnings)
951
 
 
952
 
    def test_get_user_option_as_list(self):
953
 
        conf, parser = self.make_config_parser("""
954
 
a_list = a,b,c
955
 
length_1 = 1,
956
 
one_item = x
957
 
""")
958
 
        get_list = conf.get_user_option_as_list
959
 
        self.assertEqual(['a', 'b', 'c'], get_list('a_list'))
960
 
        self.assertEqual(['1'], get_list('length_1'))
961
 
        self.assertEqual('x', conf.get_user_option('one_item'))
962
 
        # automatically cast to list
963
 
        self.assertEqual(['x'], get_list('one_item'))
964
 
 
965
 
 
966
 
class TestSupressWarning(TestIniConfig):
967
 
 
968
 
    def make_warnings_config(self, s):
969
 
        conf, parser = self.make_config_parser(s)
970
 
        return conf.suppress_warning
971
 
 
972
 
    def test_suppress_warning_unknown(self):
973
 
        suppress_warning = self.make_warnings_config('')
974
 
        self.assertEqual(False, suppress_warning('unknown_warning'))
975
 
 
976
 
    def test_suppress_warning_known(self):
977
 
        suppress_warning = self.make_warnings_config('suppress_warnings=a,b')
978
 
        self.assertEqual(False, suppress_warning('c'))
979
 
        self.assertEqual(True, suppress_warning('a'))
980
 
        self.assertEqual(True, suppress_warning('b'))
981
 
 
982
 
 
983
 
class TestGetConfig(tests.TestCase):
984
 
 
985
 
    def test_constructs(self):
986
 
        my_config = config.GlobalConfig()
987
 
 
988
 
    def test_calls_read_filenames(self):
989
 
        # replace the class that is constructed, to check its parameters
990
 
        oldparserclass = config.ConfigObj
991
 
        config.ConfigObj = InstrumentedConfigObj
992
 
        my_config = config.GlobalConfig()
993
 
        try:
994
 
            parser = my_config._get_parser()
995
 
        finally:
996
 
            config.ConfigObj = oldparserclass
997
 
        self.assertIsInstance(parser, InstrumentedConfigObj)
998
 
        self.assertEqual(parser._calls, [('__init__', config.config_filename(),
999
 
                                          'utf-8')])
1000
 
 
1001
 
 
1002
 
class TestBranchConfig(tests.TestCaseWithTransport):
1003
 
 
1004
 
    def test_constructs(self):
1005
 
        branch = FakeBranch()
1006
 
        my_config = config.BranchConfig(branch)
1007
 
        self.assertRaises(TypeError, config.BranchConfig)
1008
 
 
1009
 
    def test_get_location_config(self):
1010
 
        branch = FakeBranch()
1011
 
        my_config = config.BranchConfig(branch)
1012
 
        location_config = my_config._get_location_config()
1013
 
        self.assertEqual(branch.base, location_config.location)
1014
 
        self.assertIs(location_config, my_config._get_location_config())
1015
 
 
1016
 
    def test_get_config(self):
1017
 
        """The Branch.get_config method works properly"""
1018
 
        b = bzrdir.BzrDir.create_standalone_workingtree('.').branch
1019
 
        my_config = b.get_config()
1020
 
        self.assertIs(my_config.get_user_option('wacky'), None)
1021
 
        my_config.set_user_option('wacky', 'unlikely')
1022
 
        self.assertEqual(my_config.get_user_option('wacky'), 'unlikely')
1023
 
 
1024
 
        # Ensure we get the same thing if we start again
1025
 
        b2 = branch.Branch.open('.')
1026
 
        my_config2 = b2.get_config()
1027
 
        self.assertEqual(my_config2.get_user_option('wacky'), 'unlikely')
1028
 
 
1029
 
    def test_has_explicit_nickname(self):
1030
 
        b = self.make_branch('.')
1031
 
        self.assertFalse(b.get_config().has_explicit_nickname())
1032
 
        b.nick = 'foo'
1033
 
        self.assertTrue(b.get_config().has_explicit_nickname())
1034
 
 
1035
 
    def test_config_url(self):
1036
 
        """The Branch.get_config will use section that uses a local url"""
1037
 
        branch = self.make_branch('branch')
1038
 
        self.assertEqual('branch', branch.nick)
1039
 
 
1040
 
        local_url = urlutils.local_path_to_url('branch')
1041
 
        conf = config.LocationConfig.from_string(
1042
 
            '[%s]\nnickname = foobar' % (local_url,),
1043
 
            local_url, save=True)
1044
 
        self.assertEqual('foobar', branch.nick)
1045
 
 
1046
 
    def test_config_local_path(self):
1047
 
        """The Branch.get_config will use a local system path"""
1048
 
        branch = self.make_branch('branch')
1049
 
        self.assertEqual('branch', branch.nick)
1050
 
 
1051
 
        local_path = osutils.getcwd().encode('utf8')
1052
 
        conf = config.LocationConfig.from_string(
1053
 
            '[%s/branch]\nnickname = barry' % (local_path,),
1054
 
            'branch',  save=True)
1055
 
        self.assertEqual('barry', branch.nick)
1056
 
 
1057
 
    def test_config_creates_local(self):
1058
 
        """Creating a new entry in config uses a local path."""
1059
 
        branch = self.make_branch('branch', format='knit')
1060
 
        branch.set_push_location('http://foobar')
1061
 
        local_path = osutils.getcwd().encode('utf8')
1062
 
        # Surprisingly ConfigObj doesn't create a trailing newline
1063
 
        self.check_file_contents(config.locations_config_filename(),
1064
 
                                 '[%s/branch]\n'
1065
 
                                 'push_location = http://foobar\n'
1066
 
                                 'push_location:policy = norecurse\n'
1067
 
                                 % (local_path,))
1068
 
 
1069
 
    def test_autonick_urlencoded(self):
1070
 
        b = self.make_branch('!repo')
1071
 
        self.assertEqual('!repo', b.get_config().get_nickname())
1072
 
 
1073
 
    def test_warn_if_masked(self):
1074
 
        warnings = []
1075
 
        def warning(*args):
1076
 
            warnings.append(args[0] % args[1:])
1077
 
        self.overrideAttr(trace, 'warning', warning)
1078
 
 
1079
 
        def set_option(store, warn_masked=True):
1080
 
            warnings[:] = []
1081
 
            conf.set_user_option('example_option', repr(store), store=store,
1082
 
                                 warn_masked=warn_masked)
1083
 
        def assertWarning(warning):
1084
 
            if warning is None:
1085
 
                self.assertEqual(0, len(warnings))
1086
 
            else:
1087
 
                self.assertEqual(1, len(warnings))
1088
 
                self.assertEqual(warning, warnings[0])
1089
 
        branch = self.make_branch('.')
1090
 
        conf = branch.get_config()
1091
 
        set_option(config.STORE_GLOBAL)
1092
 
        assertWarning(None)
1093
 
        set_option(config.STORE_BRANCH)
1094
 
        assertWarning(None)
1095
 
        set_option(config.STORE_GLOBAL)
1096
 
        assertWarning('Value "4" is masked by "3" from branch.conf')
1097
 
        set_option(config.STORE_GLOBAL, warn_masked=False)
1098
 
        assertWarning(None)
1099
 
        set_option(config.STORE_LOCATION)
1100
 
        assertWarning(None)
1101
 
        set_option(config.STORE_BRANCH)
1102
 
        assertWarning('Value "3" is masked by "0" from locations.conf')
1103
 
        set_option(config.STORE_BRANCH, warn_masked=False)
1104
 
        assertWarning(None)
1105
 
 
1106
 
 
1107
 
class TestGlobalConfigItems(tests.TestCaseInTempDir):
1108
 
 
1109
 
    def test_user_id(self):
1110
 
        my_config = config.GlobalConfig.from_string(sample_config_text)
1111
 
        self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
1112
 
                         my_config._get_user_id())
1113
 
 
1114
 
    def test_absent_user_id(self):
1115
 
        my_config = config.GlobalConfig()
1116
 
        self.assertEqual(None, my_config._get_user_id())
1117
 
 
1118
 
    def test_configured_editor(self):
1119
 
        my_config = config.GlobalConfig.from_string(sample_config_text)
1120
 
        self.assertEqual("vim", my_config.get_editor())
1121
 
 
1122
 
    def test_signatures_always(self):
1123
 
        my_config = config.GlobalConfig.from_string(sample_always_signatures)
1124
 
        self.assertEqual(config.CHECK_NEVER,
1125
 
                         my_config.signature_checking())
1126
 
        self.assertEqual(config.SIGN_ALWAYS,
1127
 
                         my_config.signing_policy())
1128
 
        self.assertEqual(True, my_config.signature_needed())
1129
 
 
1130
 
    def test_signatures_if_possible(self):
1131
 
        my_config = config.GlobalConfig.from_string(sample_maybe_signatures)
1132
 
        self.assertEqual(config.CHECK_NEVER,
1133
 
                         my_config.signature_checking())
1134
 
        self.assertEqual(config.SIGN_WHEN_REQUIRED,
1135
 
                         my_config.signing_policy())
1136
 
        self.assertEqual(False, my_config.signature_needed())
1137
 
 
1138
 
    def test_signatures_ignore(self):
1139
 
        my_config = config.GlobalConfig.from_string(sample_ignore_signatures)
1140
 
        self.assertEqual(config.CHECK_ALWAYS,
1141
 
                         my_config.signature_checking())
1142
 
        self.assertEqual(config.SIGN_NEVER,
1143
 
                         my_config.signing_policy())
1144
 
        self.assertEqual(False, my_config.signature_needed())
1145
 
 
1146
 
    def _get_sample_config(self):
1147
 
        my_config = config.GlobalConfig.from_string(sample_config_text)
1148
 
        return my_config
1149
 
 
1150
 
    def test_gpg_signing_command(self):
1151
 
        my_config = self._get_sample_config()
1152
 
        self.assertEqual("gnome-gpg", my_config.gpg_signing_command())
1153
 
        self.assertEqual(False, my_config.signature_needed())
1154
 
 
1155
 
    def _get_empty_config(self):
1156
 
        my_config = config.GlobalConfig()
1157
 
        return my_config
1158
 
 
1159
 
    def test_gpg_signing_command_unset(self):
1160
 
        my_config = self._get_empty_config()
1161
 
        self.assertEqual("gpg", my_config.gpg_signing_command())
1162
 
 
1163
 
    def test_get_user_option_default(self):
1164
 
        my_config = self._get_empty_config()
1165
 
        self.assertEqual(None, my_config.get_user_option('no_option'))
1166
 
 
1167
 
    def test_get_user_option_global(self):
1168
 
        my_config = self._get_sample_config()
1169
 
        self.assertEqual("something",
1170
 
                         my_config.get_user_option('user_global_option'))
1171
 
 
1172
 
    def test_post_commit_default(self):
1173
 
        my_config = self._get_sample_config()
1174
 
        self.assertEqual(None, my_config.post_commit())
1175
 
 
1176
 
    def test_configured_logformat(self):
1177
 
        my_config = self._get_sample_config()
1178
 
        self.assertEqual("short", my_config.log_format())
1179
 
 
1180
 
    def test_get_alias(self):
1181
 
        my_config = self._get_sample_config()
1182
 
        self.assertEqual('help', my_config.get_alias('h'))
1183
 
 
1184
 
    def test_get_aliases(self):
1185
 
        my_config = self._get_sample_config()
1186
 
        aliases = my_config.get_aliases()
1187
 
        self.assertEqual(2, len(aliases))
1188
 
        sorted_keys = sorted(aliases)
1189
 
        self.assertEqual('help', aliases[sorted_keys[0]])
1190
 
        self.assertEqual(sample_long_alias, aliases[sorted_keys[1]])
1191
 
 
1192
 
    def test_get_no_alias(self):
1193
 
        my_config = self._get_sample_config()
1194
 
        self.assertEqual(None, my_config.get_alias('foo'))
1195
 
 
1196
 
    def test_get_long_alias(self):
1197
 
        my_config = self._get_sample_config()
1198
 
        self.assertEqual(sample_long_alias, my_config.get_alias('ll'))
1199
 
 
1200
 
    def test_get_change_editor(self):
1201
 
        my_config = self._get_sample_config()
1202
 
        change_editor = my_config.get_change_editor('old', 'new')
1203
 
        self.assertIs(diff.DiffFromTool, change_editor.__class__)
1204
 
        self.assertEqual('vimdiff -of @new_path @old_path',
1205
 
                         ' '.join(change_editor.command_template))
1206
 
 
1207
 
    def test_get_no_change_editor(self):
1208
 
        my_config = self._get_empty_config()
1209
 
        change_editor = my_config.get_change_editor('old', 'new')
1210
 
        self.assertIs(None, change_editor)
1211
 
 
1212
 
    def test_get_merge_tools(self):
1213
 
        conf = self._get_sample_config()
1214
 
        tools = conf.get_merge_tools()
1215
 
        self.log(repr(tools))
1216
 
        self.assertEqual(
1217
 
            {u'funkytool' : u'funkytool "arg with spaces" {this_temp}',
1218
 
            u'sometool' : u'sometool {base} {this} {other} -o {result}'},
1219
 
            tools)
1220
 
 
1221
 
    def test_get_merge_tools_empty(self):
1222
 
        conf = self._get_empty_config()
1223
 
        tools = conf.get_merge_tools()
1224
 
        self.assertEqual({}, tools)
1225
 
 
1226
 
    def test_find_merge_tool(self):
1227
 
        conf = self._get_sample_config()
1228
 
        cmdline = conf.find_merge_tool('sometool')
1229
 
        self.assertEqual('sometool {base} {this} {other} -o {result}', cmdline)
1230
 
 
1231
 
    def test_find_merge_tool_not_found(self):
1232
 
        conf = self._get_sample_config()
1233
 
        cmdline = conf.find_merge_tool('DOES NOT EXIST')
1234
 
        self.assertIs(cmdline, None)
1235
 
 
1236
 
    def test_find_merge_tool_known(self):
1237
 
        conf = self._get_empty_config()
1238
 
        cmdline = conf.find_merge_tool('kdiff3')
1239
 
        self.assertEquals('kdiff3 {base} {this} {other} -o {result}', cmdline)
1240
 
 
1241
 
    def test_find_merge_tool_override_known(self):
1242
 
        conf = self._get_empty_config()
1243
 
        conf.set_user_option('bzr.mergetool.kdiff3', 'kdiff3 blah')
1244
 
        cmdline = conf.find_merge_tool('kdiff3')
1245
 
        self.assertEqual('kdiff3 blah', cmdline)
1246
 
 
1247
 
 
1248
 
class TestGlobalConfigSavingOptions(tests.TestCaseInTempDir):
1249
 
 
1250
 
    def test_empty(self):
1251
 
        my_config = config.GlobalConfig()
1252
 
        self.assertEqual(0, len(my_config.get_aliases()))
1253
 
 
1254
 
    def test_set_alias(self):
1255
 
        my_config = config.GlobalConfig()
1256
 
        alias_value = 'commit --strict'
1257
 
        my_config.set_alias('commit', alias_value)
1258
 
        new_config = config.GlobalConfig()
1259
 
        self.assertEqual(alias_value, new_config.get_alias('commit'))
1260
 
 
1261
 
    def test_remove_alias(self):
1262
 
        my_config = config.GlobalConfig()
1263
 
        my_config.set_alias('commit', 'commit --strict')
1264
 
        # Now remove the alias again.
1265
 
        my_config.unset_alias('commit')
1266
 
        new_config = config.GlobalConfig()
1267
 
        self.assertIs(None, new_config.get_alias('commit'))
1268
 
 
1269
 
 
1270
 
class TestLocationConfig(tests.TestCaseInTempDir, TestOptionsMixin):
1271
 
 
1272
 
    def test_constructs(self):
1273
 
        my_config = config.LocationConfig('http://example.com')
1274
 
        self.assertRaises(TypeError, config.LocationConfig)
1275
 
 
1276
 
    def test_branch_calls_read_filenames(self):
1277
 
        # This is testing the correct file names are provided.
1278
 
        # TODO: consolidate with the test for GlobalConfigs filename checks.
1279
 
        #
1280
 
        # replace the class that is constructed, to check its parameters
1281
 
        oldparserclass = config.ConfigObj
1282
 
        config.ConfigObj = InstrumentedConfigObj
1283
 
        try:
1284
 
            my_config = config.LocationConfig('http://www.example.com')
1285
 
            parser = my_config._get_parser()
1286
 
        finally:
1287
 
            config.ConfigObj = oldparserclass
1288
 
        self.assertIsInstance(parser, InstrumentedConfigObj)
1289
 
        self.assertEqual(parser._calls,
1290
 
                         [('__init__', config.locations_config_filename(),
1291
 
                           'utf-8')])
1292
 
 
1293
 
    def test_get_global_config(self):
1294
 
        my_config = config.BranchConfig(FakeBranch('http://example.com'))
1295
 
        global_config = my_config._get_global_config()
1296
 
        self.assertIsInstance(global_config, config.GlobalConfig)
1297
 
        self.assertIs(global_config, my_config._get_global_config())
1298
 
 
1299
 
    def assertLocationMatching(self, expected):
1300
 
        self.assertEqual(expected,
1301
 
                         list(self.my_location_config._get_matching_sections()))
1302
 
 
1303
 
    def test__get_matching_sections_no_match(self):
1304
 
        self.get_branch_config('/')
1305
 
        self.assertLocationMatching([])
1306
 
 
1307
 
    def test__get_matching_sections_exact(self):
1308
 
        self.get_branch_config('http://www.example.com')
1309
 
        self.assertLocationMatching([('http://www.example.com', '')])
1310
 
 
1311
 
    def test__get_matching_sections_suffix_does_not(self):
1312
 
        self.get_branch_config('http://www.example.com-com')
1313
 
        self.assertLocationMatching([])
1314
 
 
1315
 
    def test__get_matching_sections_subdir_recursive(self):
1316
 
        self.get_branch_config('http://www.example.com/com')
1317
 
        self.assertLocationMatching([('http://www.example.com', 'com')])
1318
 
 
1319
 
    def test__get_matching_sections_ignoreparent(self):
1320
 
        self.get_branch_config('http://www.example.com/ignoreparent')
1321
 
        self.assertLocationMatching([('http://www.example.com/ignoreparent',
1322
 
                                      '')])
1323
 
 
1324
 
    def test__get_matching_sections_ignoreparent_subdir(self):
1325
 
        self.get_branch_config(
1326
 
            'http://www.example.com/ignoreparent/childbranch')
1327
 
        self.assertLocationMatching([('http://www.example.com/ignoreparent',
1328
 
                                      'childbranch')])
1329
 
 
1330
 
    def test__get_matching_sections_subdir_trailing_slash(self):
1331
 
        self.get_branch_config('/b')
1332
 
        self.assertLocationMatching([('/b/', '')])
1333
 
 
1334
 
    def test__get_matching_sections_subdir_child(self):
1335
 
        self.get_branch_config('/a/foo')
1336
 
        self.assertLocationMatching([('/a/*', ''), ('/a/', 'foo')])
1337
 
 
1338
 
    def test__get_matching_sections_subdir_child_child(self):
1339
 
        self.get_branch_config('/a/foo/bar')
1340
 
        self.assertLocationMatching([('/a/*', 'bar'), ('/a/', 'foo/bar')])
1341
 
 
1342
 
    def test__get_matching_sections_trailing_slash_with_children(self):
1343
 
        self.get_branch_config('/a/')
1344
 
        self.assertLocationMatching([('/a/', '')])
1345
 
 
1346
 
    def test__get_matching_sections_explicit_over_glob(self):
1347
 
        # XXX: 2006-09-08 jamesh
1348
 
        # This test only passes because ord('c') > ord('*').  If there
1349
 
        # was a config section for '/a/?', it would get precedence
1350
 
        # over '/a/c'.
1351
 
        self.get_branch_config('/a/c')
1352
 
        self.assertLocationMatching([('/a/c', ''), ('/a/*', ''), ('/a/', 'c')])
1353
 
 
1354
 
    def test__get_option_policy_normal(self):
1355
 
        self.get_branch_config('http://www.example.com')
1356
 
        self.assertEqual(
1357
 
            self.my_location_config._get_config_policy(
1358
 
            'http://www.example.com', 'normal_option'),
1359
 
            config.POLICY_NONE)
1360
 
 
1361
 
    def test__get_option_policy_norecurse(self):
1362
 
        self.get_branch_config('http://www.example.com')
1363
 
        self.assertEqual(
1364
 
            self.my_location_config._get_option_policy(
1365
 
            'http://www.example.com', 'norecurse_option'),
1366
 
            config.POLICY_NORECURSE)
1367
 
        # Test old recurse=False setting:
1368
 
        self.assertEqual(
1369
 
            self.my_location_config._get_option_policy(
1370
 
            'http://www.example.com/norecurse', 'normal_option'),
1371
 
            config.POLICY_NORECURSE)
1372
 
 
1373
 
    def test__get_option_policy_normal(self):
1374
 
        self.get_branch_config('http://www.example.com')
1375
 
        self.assertEqual(
1376
 
            self.my_location_config._get_option_policy(
1377
 
            'http://www.example.com', 'appendpath_option'),
1378
 
            config.POLICY_APPENDPATH)
1379
 
 
1380
 
    def test__get_options_with_policy(self):
1381
 
        self.get_branch_config('/dir/subdir',
1382
 
                               location_config="""\
1383
 
[/dir]
1384
 
other_url = /other-dir
1385
 
other_url:policy = appendpath
1386
 
[/dir/subdir]
1387
 
other_url = /other-subdir
1388
 
""")
1389
 
        self.assertOptions(
1390
 
            [(u'other_url', u'/other-subdir', u'/dir/subdir', 'locations'),
1391
 
             (u'other_url', u'/other-dir', u'/dir', 'locations'),
1392
 
             (u'other_url:policy', u'appendpath', u'/dir', 'locations')],
1393
 
            self.my_location_config)
1394
 
 
1395
 
    def test_location_without_username(self):
1396
 
        self.get_branch_config('http://www.example.com/ignoreparent')
1397
 
        self.assertEqual(u'Erik B\u00e5gfors <erik@bagfors.nu>',
1398
 
                         self.my_config.username())
1399
 
 
1400
 
    def test_location_not_listed(self):
1401
 
        """Test that the global username is used when no location matches"""
1402
 
        self.get_branch_config('/home/robertc/sources')
1403
 
        self.assertEqual(u'Erik B\u00e5gfors <erik@bagfors.nu>',
1404
 
                         self.my_config.username())
1405
 
 
1406
 
    def test_overriding_location(self):
1407
 
        self.get_branch_config('http://www.example.com/foo')
1408
 
        self.assertEqual('Robert Collins <robertc@example.org>',
1409
 
                         self.my_config.username())
1410
 
 
1411
 
    def test_signatures_not_set(self):
1412
 
        self.get_branch_config('http://www.example.com',
1413
 
                                 global_config=sample_ignore_signatures)
1414
 
        self.assertEqual(config.CHECK_ALWAYS,
1415
 
                         self.my_config.signature_checking())
1416
 
        self.assertEqual(config.SIGN_NEVER,
1417
 
                         self.my_config.signing_policy())
1418
 
 
1419
 
    def test_signatures_never(self):
1420
 
        self.get_branch_config('/a/c')
1421
 
        self.assertEqual(config.CHECK_NEVER,
1422
 
                         self.my_config.signature_checking())
1423
 
 
1424
 
    def test_signatures_when_available(self):
1425
 
        self.get_branch_config('/a/', global_config=sample_ignore_signatures)
1426
 
        self.assertEqual(config.CHECK_IF_POSSIBLE,
1427
 
                         self.my_config.signature_checking())
1428
 
 
1429
 
    def test_signatures_always(self):
1430
 
        self.get_branch_config('/b')
1431
 
        self.assertEqual(config.CHECK_ALWAYS,
1432
 
                         self.my_config.signature_checking())
1433
 
 
1434
 
    def test_gpg_signing_command(self):
1435
 
        self.get_branch_config('/b')
1436
 
        self.assertEqual("gnome-gpg", self.my_config.gpg_signing_command())
1437
 
 
1438
 
    def test_gpg_signing_command_missing(self):
1439
 
        self.get_branch_config('/a')
1440
 
        self.assertEqual("false", self.my_config.gpg_signing_command())
1441
 
 
1442
 
    def test_get_user_option_global(self):
1443
 
        self.get_branch_config('/a')
1444
 
        self.assertEqual('something',
1445
 
                         self.my_config.get_user_option('user_global_option'))
1446
 
 
1447
 
    def test_get_user_option_local(self):
1448
 
        self.get_branch_config('/a')
1449
 
        self.assertEqual('local',
1450
 
                         self.my_config.get_user_option('user_local_option'))
1451
 
 
1452
 
    def test_get_user_option_appendpath(self):
1453
 
        # returned as is for the base path:
1454
 
        self.get_branch_config('http://www.example.com')
1455
 
        self.assertEqual('append',
1456
 
                         self.my_config.get_user_option('appendpath_option'))
1457
 
        # Extra path components get appended:
1458
 
        self.get_branch_config('http://www.example.com/a/b/c')
1459
 
        self.assertEqual('append/a/b/c',
1460
 
                         self.my_config.get_user_option('appendpath_option'))
1461
 
        # Overriden for http://www.example.com/dir, where it is a
1462
 
        # normal option:
1463
 
        self.get_branch_config('http://www.example.com/dir/a/b/c')
1464
 
        self.assertEqual('normal',
1465
 
                         self.my_config.get_user_option('appendpath_option'))
1466
 
 
1467
 
    def test_get_user_option_norecurse(self):
1468
 
        self.get_branch_config('http://www.example.com')
1469
 
        self.assertEqual('norecurse',
1470
 
                         self.my_config.get_user_option('norecurse_option'))
1471
 
        self.get_branch_config('http://www.example.com/dir')
1472
 
        self.assertEqual(None,
1473
 
                         self.my_config.get_user_option('norecurse_option'))
1474
 
        # http://www.example.com/norecurse is a recurse=False section
1475
 
        # that redefines normal_option.  Subdirectories do not pick up
1476
 
        # this redefinition.
1477
 
        self.get_branch_config('http://www.example.com/norecurse')
1478
 
        self.assertEqual('norecurse',
1479
 
                         self.my_config.get_user_option('normal_option'))
1480
 
        self.get_branch_config('http://www.example.com/norecurse/subdir')
1481
 
        self.assertEqual('normal',
1482
 
                         self.my_config.get_user_option('normal_option'))
1483
 
 
1484
 
    def test_set_user_option_norecurse(self):
1485
 
        self.get_branch_config('http://www.example.com')
1486
 
        self.my_config.set_user_option('foo', 'bar',
1487
 
                                       store=config.STORE_LOCATION_NORECURSE)
1488
 
        self.assertEqual(
1489
 
            self.my_location_config._get_option_policy(
1490
 
            'http://www.example.com', 'foo'),
1491
 
            config.POLICY_NORECURSE)
1492
 
 
1493
 
    def test_set_user_option_appendpath(self):
1494
 
        self.get_branch_config('http://www.example.com')
1495
 
        self.my_config.set_user_option('foo', 'bar',
1496
 
                                       store=config.STORE_LOCATION_APPENDPATH)
1497
 
        self.assertEqual(
1498
 
            self.my_location_config._get_option_policy(
1499
 
            'http://www.example.com', 'foo'),
1500
 
            config.POLICY_APPENDPATH)
1501
 
 
1502
 
    def test_set_user_option_change_policy(self):
1503
 
        self.get_branch_config('http://www.example.com')
1504
 
        self.my_config.set_user_option('norecurse_option', 'normal',
1505
 
                                       store=config.STORE_LOCATION)
1506
 
        self.assertEqual(
1507
 
            self.my_location_config._get_option_policy(
1508
 
            'http://www.example.com', 'norecurse_option'),
1509
 
            config.POLICY_NONE)
1510
 
 
1511
 
    def test_set_user_option_recurse_false_section(self):
1512
 
        # The following section has recurse=False set.  The test is to
1513
 
        # make sure that a normal option can be added to the section,
1514
 
        # converting recurse=False to the norecurse policy.
1515
 
        self.get_branch_config('http://www.example.com/norecurse')
1516
 
        self.callDeprecated(['The recurse option is deprecated as of 0.14.  '
1517
 
                             'The section "http://www.example.com/norecurse" '
1518
 
                             'has been converted to use policies.'],
1519
 
                            self.my_config.set_user_option,
1520
 
                            'foo', 'bar', store=config.STORE_LOCATION)
1521
 
        self.assertEqual(
1522
 
            self.my_location_config._get_option_policy(
1523
 
            'http://www.example.com/norecurse', 'foo'),
1524
 
            config.POLICY_NONE)
1525
 
        # The previously existing option is still norecurse:
1526
 
        self.assertEqual(
1527
 
            self.my_location_config._get_option_policy(
1528
 
            'http://www.example.com/norecurse', 'normal_option'),
1529
 
            config.POLICY_NORECURSE)
1530
 
 
1531
 
    def test_post_commit_default(self):
1532
 
        self.get_branch_config('/a/c')
1533
 
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1534
 
                         self.my_config.post_commit())
1535
 
 
1536
 
    def get_branch_config(self, location, global_config=None,
1537
 
                          location_config=None):
1538
 
        my_branch = FakeBranch(location)
1539
 
        if global_config is None:
1540
 
            global_config = sample_config_text
1541
 
        if location_config is None:
1542
 
            location_config = sample_branches_text
1543
 
 
1544
 
        my_global_config = config.GlobalConfig.from_string(global_config,
1545
 
                                                           save=True)
1546
 
        my_location_config = config.LocationConfig.from_string(
1547
 
            location_config, my_branch.base, save=True)
1548
 
        my_config = config.BranchConfig(my_branch)
1549
 
        self.my_config = my_config
1550
 
        self.my_location_config = my_config._get_location_config()
1551
 
 
1552
 
    def test_set_user_setting_sets_and_saves(self):
1553
 
        self.get_branch_config('/a/c')
1554
 
        record = InstrumentedConfigObj("foo")
1555
 
        self.my_location_config._parser = record
1556
 
 
1557
 
        self.callDeprecated(['The recurse option is deprecated as of '
1558
 
                             '0.14.  The section "/a/c" has been '
1559
 
                             'converted to use policies.'],
1560
 
                            self.my_config.set_user_option,
1561
 
                            'foo', 'bar', store=config.STORE_LOCATION)
1562
 
        self.assertEqual([('reload',),
1563
 
                          ('__contains__', '/a/c'),
1564
 
                          ('__contains__', '/a/c/'),
1565
 
                          ('__setitem__', '/a/c', {}),
1566
 
                          ('__getitem__', '/a/c'),
1567
 
                          ('__setitem__', 'foo', 'bar'),
1568
 
                          ('__getitem__', '/a/c'),
1569
 
                          ('as_bool', 'recurse'),
1570
 
                          ('__getitem__', '/a/c'),
1571
 
                          ('__delitem__', 'recurse'),
1572
 
                          ('__getitem__', '/a/c'),
1573
 
                          ('keys',),
1574
 
                          ('__getitem__', '/a/c'),
1575
 
                          ('__contains__', 'foo:policy'),
1576
 
                          ('write',)],
1577
 
                         record._calls[1:])
1578
 
 
1579
 
    def test_set_user_setting_sets_and_saves2(self):
1580
 
        self.get_branch_config('/a/c')
1581
 
        self.assertIs(self.my_config.get_user_option('foo'), None)
1582
 
        self.my_config.set_user_option('foo', 'bar')
1583
 
        self.assertEqual(
1584
 
            self.my_config.branch.control_files.files['branch.conf'].strip(),
1585
 
            'foo = bar')
1586
 
        self.assertEqual(self.my_config.get_user_option('foo'), 'bar')
1587
 
        self.my_config.set_user_option('foo', 'baz',
1588
 
                                       store=config.STORE_LOCATION)
1589
 
        self.assertEqual(self.my_config.get_user_option('foo'), 'baz')
1590
 
        self.my_config.set_user_option('foo', 'qux')
1591
 
        self.assertEqual(self.my_config.get_user_option('foo'), 'baz')
1592
 
 
1593
 
    def test_get_bzr_remote_path(self):
1594
 
        my_config = config.LocationConfig('/a/c')
1595
 
        self.assertEqual('bzr', my_config.get_bzr_remote_path())
1596
 
        my_config.set_user_option('bzr_remote_path', '/path-bzr')
1597
 
        self.assertEqual('/path-bzr', my_config.get_bzr_remote_path())
1598
 
        self.overrideEnv('BZR_REMOTE_PATH', '/environ-bzr')
1599
 
        self.assertEqual('/environ-bzr', my_config.get_bzr_remote_path())
1600
 
 
1601
 
 
1602
 
precedence_global = 'option = global'
1603
 
precedence_branch = 'option = branch'
1604
 
precedence_location = """
1605
 
[http://]
1606
 
recurse = true
1607
 
option = recurse
1608
 
[http://example.com/specific]
1609
 
option = exact
1610
 
"""
1611
 
 
1612
 
class TestBranchConfigItems(tests.TestCaseInTempDir):
1613
 
 
1614
 
    def get_branch_config(self, global_config=None, location=None,
1615
 
                          location_config=None, branch_data_config=None):
1616
 
        my_branch = FakeBranch(location)
1617
 
        if global_config is not None:
1618
 
            my_global_config = config.GlobalConfig.from_string(global_config,
1619
 
                                                               save=True)
1620
 
        if location_config is not None:
1621
 
            my_location_config = config.LocationConfig.from_string(
1622
 
                location_config, my_branch.base, save=True)
1623
 
        my_config = config.BranchConfig(my_branch)
1624
 
        if branch_data_config is not None:
1625
 
            my_config.branch.control_files.files['branch.conf'] = \
1626
 
                branch_data_config
1627
 
        return my_config
1628
 
 
1629
 
    def test_user_id(self):
1630
 
        branch = FakeBranch(user_id='Robert Collins <robertc@example.net>')
1631
 
        my_config = config.BranchConfig(branch)
1632
 
        self.assertEqual("Robert Collins <robertc@example.net>",
1633
 
                         my_config.username())
1634
 
        my_config.branch.control_files.files['email'] = "John"
1635
 
        my_config.set_user_option('email',
1636
 
                                  "Robert Collins <robertc@example.org>")
1637
 
        self.assertEqual("John", my_config.username())
1638
 
        del my_config.branch.control_files.files['email']
1639
 
        self.assertEqual("Robert Collins <robertc@example.org>",
1640
 
                         my_config.username())
1641
 
 
1642
 
    def test_not_set_in_branch(self):
1643
 
        my_config = self.get_branch_config(global_config=sample_config_text)
1644
 
        self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
1645
 
                         my_config._get_user_id())
1646
 
        my_config.branch.control_files.files['email'] = "John"
1647
 
        self.assertEqual("John", my_config._get_user_id())
1648
 
 
1649
 
    def test_BZR_EMAIL_OVERRIDES(self):
1650
 
        self.overrideEnv('BZR_EMAIL', "Robert Collins <robertc@example.org>")
1651
 
        branch = FakeBranch()
1652
 
        my_config = config.BranchConfig(branch)
1653
 
        self.assertEqual("Robert Collins <robertc@example.org>",
1654
 
                         my_config.username())
1655
 
 
1656
 
    def test_signatures_forced(self):
1657
 
        my_config = self.get_branch_config(
1658
 
            global_config=sample_always_signatures)
1659
 
        self.assertEqual(config.CHECK_NEVER, my_config.signature_checking())
1660
 
        self.assertEqual(config.SIGN_ALWAYS, my_config.signing_policy())
1661
 
        self.assertTrue(my_config.signature_needed())
1662
 
 
1663
 
    def test_signatures_forced_branch(self):
1664
 
        my_config = self.get_branch_config(
1665
 
            global_config=sample_ignore_signatures,
1666
 
            branch_data_config=sample_always_signatures)
1667
 
        self.assertEqual(config.CHECK_NEVER, my_config.signature_checking())
1668
 
        self.assertEqual(config.SIGN_ALWAYS, my_config.signing_policy())
1669
 
        self.assertTrue(my_config.signature_needed())
1670
 
 
1671
 
    def test_gpg_signing_command(self):
1672
 
        my_config = self.get_branch_config(
1673
 
            global_config=sample_config_text,
1674
 
            # branch data cannot set gpg_signing_command
1675
 
            branch_data_config="gpg_signing_command=pgp")
1676
 
        self.assertEqual('gnome-gpg', my_config.gpg_signing_command())
1677
 
 
1678
 
    def test_get_user_option_global(self):
1679
 
        my_config = self.get_branch_config(global_config=sample_config_text)
1680
 
        self.assertEqual('something',
1681
 
                         my_config.get_user_option('user_global_option'))
1682
 
 
1683
 
    def test_post_commit_default(self):
1684
 
        my_config = self.get_branch_config(global_config=sample_config_text,
1685
 
                                      location='/a/c',
1686
 
                                      location_config=sample_branches_text)
1687
 
        self.assertEqual(my_config.branch.base, '/a/c')
1688
 
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1689
 
                         my_config.post_commit())
1690
 
        my_config.set_user_option('post_commit', 'rmtree_root')
1691
 
        # post-commit is ignored when present in branch data
1692
 
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1693
 
                         my_config.post_commit())
1694
 
        my_config.set_user_option('post_commit', 'rmtree_root',
1695
 
                                  store=config.STORE_LOCATION)
1696
 
        self.assertEqual('rmtree_root', my_config.post_commit())
1697
 
 
1698
 
    def test_config_precedence(self):
1699
 
        # FIXME: eager test, luckily no persitent config file makes it fail
1700
 
        # -- vila 20100716
1701
 
        my_config = self.get_branch_config(global_config=precedence_global)
1702
 
        self.assertEqual(my_config.get_user_option('option'), 'global')
1703
 
        my_config = self.get_branch_config(global_config=precedence_global,
1704
 
                                           branch_data_config=precedence_branch)
1705
 
        self.assertEqual(my_config.get_user_option('option'), 'branch')
1706
 
        my_config = self.get_branch_config(
1707
 
            global_config=precedence_global,
1708
 
            branch_data_config=precedence_branch,
1709
 
            location_config=precedence_location)
1710
 
        self.assertEqual(my_config.get_user_option('option'), 'recurse')
1711
 
        my_config = self.get_branch_config(
1712
 
            global_config=precedence_global,
1713
 
            branch_data_config=precedence_branch,
1714
 
            location_config=precedence_location,
1715
 
            location='http://example.com/specific')
1716
 
        self.assertEqual(my_config.get_user_option('option'), 'exact')
1717
 
 
1718
 
    def test_get_mail_client(self):
1719
 
        config = self.get_branch_config()
1720
 
        client = config.get_mail_client()
1721
 
        self.assertIsInstance(client, mail_client.DefaultMail)
1722
 
 
1723
 
        # Specific clients
1724
 
        config.set_user_option('mail_client', 'evolution')
1725
 
        client = config.get_mail_client()
1726
 
        self.assertIsInstance(client, mail_client.Evolution)
1727
 
 
1728
 
        config.set_user_option('mail_client', 'kmail')
1729
 
        client = config.get_mail_client()
1730
 
        self.assertIsInstance(client, mail_client.KMail)
1731
 
 
1732
 
        config.set_user_option('mail_client', 'mutt')
1733
 
        client = config.get_mail_client()
1734
 
        self.assertIsInstance(client, mail_client.Mutt)
1735
 
 
1736
 
        config.set_user_option('mail_client', 'thunderbird')
1737
 
        client = config.get_mail_client()
1738
 
        self.assertIsInstance(client, mail_client.Thunderbird)
1739
 
 
1740
 
        # Generic options
1741
 
        config.set_user_option('mail_client', 'default')
1742
 
        client = config.get_mail_client()
1743
 
        self.assertIsInstance(client, mail_client.DefaultMail)
1744
 
 
1745
 
        config.set_user_option('mail_client', 'editor')
1746
 
        client = config.get_mail_client()
1747
 
        self.assertIsInstance(client, mail_client.Editor)
1748
 
 
1749
 
        config.set_user_option('mail_client', 'mapi')
1750
 
        client = config.get_mail_client()
1751
 
        self.assertIsInstance(client, mail_client.MAPIClient)
1752
 
 
1753
 
        config.set_user_option('mail_client', 'xdg-email')
1754
 
        client = config.get_mail_client()
1755
 
        self.assertIsInstance(client, mail_client.XDGEmail)
1756
 
 
1757
 
        config.set_user_option('mail_client', 'firebird')
1758
 
        self.assertRaises(errors.UnknownMailClient, config.get_mail_client)
1759
 
 
1760
 
 
1761
 
class TestMailAddressExtraction(tests.TestCase):
1762
 
 
1763
 
    def test_extract_email_address(self):
1764
 
        self.assertEqual('jane@test.com',
1765
 
                         config.extract_email_address('Jane <jane@test.com>'))
1766
 
        self.assertRaises(errors.NoEmailInUsername,
1767
 
                          config.extract_email_address, 'Jane Tester')
1768
 
 
1769
 
    def test_parse_username(self):
1770
 
        self.assertEqual(('', 'jdoe@example.com'),
1771
 
                         config.parse_username('jdoe@example.com'))
1772
 
        self.assertEqual(('', 'jdoe@example.com'),
1773
 
                         config.parse_username('<jdoe@example.com>'))
1774
 
        self.assertEqual(('John Doe', 'jdoe@example.com'),
1775
 
                         config.parse_username('John Doe <jdoe@example.com>'))
1776
 
        self.assertEqual(('John Doe', ''),
1777
 
                         config.parse_username('John Doe'))
1778
 
        self.assertEqual(('John Doe', 'jdoe@example.com'),
1779
 
                         config.parse_username('John Doe jdoe@example.com'))
1780
 
 
1781
 
class TestTreeConfig(tests.TestCaseWithTransport):
1782
 
 
1783
 
    def test_get_value(self):
1784
 
        """Test that retreiving a value from a section is possible"""
1785
 
        branch = self.make_branch('.')
1786
 
        tree_config = config.TreeConfig(branch)
1787
 
        tree_config.set_option('value', 'key', 'SECTION')
1788
 
        tree_config.set_option('value2', 'key2')
1789
 
        tree_config.set_option('value3-top', 'key3')
1790
 
        tree_config.set_option('value3-section', 'key3', 'SECTION')
1791
 
        value = tree_config.get_option('key', 'SECTION')
1792
 
        self.assertEqual(value, 'value')
1793
 
        value = tree_config.get_option('key2')
1794
 
        self.assertEqual(value, 'value2')
1795
 
        self.assertEqual(tree_config.get_option('non-existant'), None)
1796
 
        value = tree_config.get_option('non-existant', 'SECTION')
1797
 
        self.assertEqual(value, None)
1798
 
        value = tree_config.get_option('non-existant', default='default')
1799
 
        self.assertEqual(value, 'default')
1800
 
        self.assertEqual(tree_config.get_option('key2', 'NOSECTION'), None)
1801
 
        value = tree_config.get_option('key2', 'NOSECTION', default='default')
1802
 
        self.assertEqual(value, 'default')
1803
 
        value = tree_config.get_option('key3')
1804
 
        self.assertEqual(value, 'value3-top')
1805
 
        value = tree_config.get_option('key3', 'SECTION')
1806
 
        self.assertEqual(value, 'value3-section')
1807
 
 
1808
 
 
1809
 
class TestTransportConfig(tests.TestCaseWithTransport):
1810
 
 
1811
 
    def test_get_value(self):
1812
 
        """Test that retreiving a value from a section is possible"""
1813
 
        bzrdir_config = config.TransportConfig(transport.get_transport('.'),
1814
 
                                               'control.conf')
1815
 
        bzrdir_config.set_option('value', 'key', 'SECTION')
1816
 
        bzrdir_config.set_option('value2', 'key2')
1817
 
        bzrdir_config.set_option('value3-top', 'key3')
1818
 
        bzrdir_config.set_option('value3-section', 'key3', 'SECTION')
1819
 
        value = bzrdir_config.get_option('key', 'SECTION')
1820
 
        self.assertEqual(value, 'value')
1821
 
        value = bzrdir_config.get_option('key2')
1822
 
        self.assertEqual(value, 'value2')
1823
 
        self.assertEqual(bzrdir_config.get_option('non-existant'), None)
1824
 
        value = bzrdir_config.get_option('non-existant', 'SECTION')
1825
 
        self.assertEqual(value, None)
1826
 
        value = bzrdir_config.get_option('non-existant', default='default')
1827
 
        self.assertEqual(value, 'default')
1828
 
        self.assertEqual(bzrdir_config.get_option('key2', 'NOSECTION'), None)
1829
 
        value = bzrdir_config.get_option('key2', 'NOSECTION',
1830
 
                                         default='default')
1831
 
        self.assertEqual(value, 'default')
1832
 
        value = bzrdir_config.get_option('key3')
1833
 
        self.assertEqual(value, 'value3-top')
1834
 
        value = bzrdir_config.get_option('key3', 'SECTION')
1835
 
        self.assertEqual(value, 'value3-section')
1836
 
 
1837
 
    def test_set_unset_default_stack_on(self):
1838
 
        my_dir = self.make_bzrdir('.')
1839
 
        bzrdir_config = config.BzrDirConfig(my_dir)
1840
 
        self.assertIs(None, bzrdir_config.get_default_stack_on())
1841
 
        bzrdir_config.set_default_stack_on('Foo')
1842
 
        self.assertEqual('Foo', bzrdir_config._config.get_option(
1843
 
                         'default_stack_on'))
1844
 
        self.assertEqual('Foo', bzrdir_config.get_default_stack_on())
1845
 
        bzrdir_config.set_default_stack_on(None)
1846
 
        self.assertIs(None, bzrdir_config.get_default_stack_on())
1847
 
 
1848
 
 
1849
 
class TestSection(tests.TestCase):
1850
 
 
1851
 
    # FIXME: Parametrize so that all sections produced by Stores run these
1852
 
    # tests -- vila 2011-04-01
1853
 
 
1854
 
    def test_get_a_value(self):
1855
 
        a_dict = dict(foo='bar')
1856
 
        section = config.Section('myID', a_dict)
1857
 
        self.assertEquals('bar', section.get('foo'))
1858
 
 
1859
 
    def test_get_unknown_option(self):
1860
 
        a_dict = dict()
1861
 
        section = config.Section(None, a_dict)
1862
 
        self.assertEquals('out of thin air',
1863
 
                          section.get('foo', 'out of thin air'))
1864
 
 
1865
 
    def test_options_is_shared(self):
1866
 
        a_dict = dict()
1867
 
        section = config.Section(None, a_dict)
1868
 
        self.assertIs(a_dict, section.options)
1869
 
 
1870
 
 
1871
 
class TestMutableSection(tests.TestCase):
1872
 
 
1873
 
    # FIXME: Parametrize so that all sections (including os.environ and the
1874
 
    # ones produced by Stores) run these tests -- vila 2011-04-01
1875
 
 
1876
 
    def test_set(self):
1877
 
        a_dict = dict(foo='bar')
1878
 
        section = config.MutableSection('myID', a_dict)
1879
 
        section.set('foo', 'new_value')
1880
 
        self.assertEquals('new_value', section.get('foo'))
1881
 
        # The change appears in the shared section
1882
 
        self.assertEquals('new_value', a_dict.get('foo'))
1883
 
        # We keep track of the change
1884
 
        self.assertTrue('foo' in section.orig)
1885
 
        self.assertEquals('bar', section.orig.get('foo'))
1886
 
 
1887
 
    def test_set_preserve_original_once(self):
1888
 
        a_dict = dict(foo='bar')
1889
 
        section = config.MutableSection('myID', a_dict)
1890
 
        section.set('foo', 'first_value')
1891
 
        section.set('foo', 'second_value')
1892
 
        # We keep track of the original value
1893
 
        self.assertTrue('foo' in section.orig)
1894
 
        self.assertEquals('bar', section.orig.get('foo'))
1895
 
 
1896
 
    def test_remove(self):
1897
 
        a_dict = dict(foo='bar')
1898
 
        section = config.MutableSection('myID', a_dict)
1899
 
        section.remove('foo')
1900
 
        # We get None for unknown options via the default value
1901
 
        self.assertEquals(None, section.get('foo'))
1902
 
        # Or we just get the default value
1903
 
        self.assertEquals('unknown', section.get('foo', 'unknown'))
1904
 
        self.assertFalse('foo' in section.options)
1905
 
        # We keep track of the deletion
1906
 
        self.assertTrue('foo' in section.orig)
1907
 
        self.assertEquals('bar', section.orig.get('foo'))
1908
 
 
1909
 
    def test_remove_new_option(self):
1910
 
        a_dict = dict()
1911
 
        section = config.MutableSection('myID', a_dict)
1912
 
        section.set('foo', 'bar')
1913
 
        section.remove('foo')
1914
 
        self.assertFalse('foo' in section.options)
1915
 
        # The option didn't exist initially so it we need to keep track of it
1916
 
        # with a special value
1917
 
        self.assertTrue('foo' in section.orig)
1918
 
        self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
1919
 
 
1920
 
 
1921
 
class TestStore(tests.TestCaseWithTransport):
1922
 
 
1923
 
    def assertSectionContent(self, expected, section):
1924
 
        """Assert that some options have the proper values in a section."""
1925
 
        expected_name, expected_options = expected
1926
 
        self.assertEquals(expected_name, section.id)
1927
 
        self.assertEquals(
1928
 
            expected_options,
1929
 
            dict([(k, section.get(k)) for k in expected_options.keys()]))
1930
 
 
1931
 
 
1932
 
class TestReadonlyStore(TestStore):
1933
 
 
1934
 
    scenarios = [(key, {'get_store': builder})
1935
 
                 for key, builder in test_store_builder_registry.iteritems()]
1936
 
 
1937
 
    def setUp(self):
1938
 
        super(TestReadonlyStore, self).setUp()
1939
 
        self.branch = self.make_branch('branch')
1940
 
 
1941
 
    def test_building_delays_load(self):
1942
 
        store = self.get_store(self)
1943
 
        self.assertEquals(False, store.is_loaded())
1944
 
        store._load_from_string('')
1945
 
        self.assertEquals(True, store.is_loaded())
1946
 
 
1947
 
    def test_get_no_sections_for_empty(self):
1948
 
        store = self.get_store(self)
1949
 
        store._load_from_string('')
1950
 
        self.assertEquals([], list(store.get_sections()))
1951
 
 
1952
 
    def test_get_default_section(self):
1953
 
        store = self.get_store(self)
1954
 
        store._load_from_string('foo=bar')
1955
 
        sections = list(store.get_sections())
1956
 
        self.assertLength(1, sections)
1957
 
        self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
1958
 
 
1959
 
    def test_get_named_section(self):
1960
 
        store = self.get_store(self)
1961
 
        store._load_from_string('[baz]\nfoo=bar')
1962
 
        sections = list(store.get_sections())
1963
 
        self.assertLength(1, sections)
1964
 
        self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
1965
 
 
1966
 
    def test_load_from_string_fails_for_non_empty_store(self):
1967
 
        store = self.get_store(self)
1968
 
        store._load_from_string('foo=bar')
1969
 
        self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
1970
 
 
1971
 
 
1972
 
class TestMutableStore(TestStore):
1973
 
 
1974
 
    scenarios = [(key, {'store_id': key, 'get_store': builder})
1975
 
                 for key, builder in test_store_builder_registry.iteritems()]
1976
 
 
1977
 
    def setUp(self):
1978
 
        super(TestMutableStore, self).setUp()
1979
 
        self.transport = self.get_transport()
1980
 
        self.branch = self.make_branch('branch')
1981
 
 
1982
 
    def has_store(self, store):
1983
 
        store_basename = urlutils.relative_url(self.transport.external_url(),
1984
 
                                               store.external_url())
1985
 
        return self.transport.has(store_basename)
1986
 
 
1987
 
    def test_save_empty_creates_no_file(self):
1988
 
        if self.store_id == 'branch':
1989
 
            raise tests.TestNotApplicable(
1990
 
                'branch.conf is *always* created when a branch is initialized')
1991
 
        store = self.get_store(self)
1992
 
        store.save()
1993
 
        self.assertEquals(False, self.has_store(store))
1994
 
 
1995
 
    def test_save_emptied_succeeds(self):
1996
 
        store = self.get_store(self)
1997
 
        store._load_from_string('foo=bar\n')
1998
 
        section = store.get_mutable_section(None)
1999
 
        section.remove('foo')
2000
 
        store.save()
2001
 
        self.assertEquals(True, self.has_store(store))
2002
 
        modified_store = self.get_store(self)
2003
 
        sections = list(modified_store.get_sections())
2004
 
        self.assertLength(0, sections)
2005
 
 
2006
 
    def test_save_with_content_succeeds(self):
2007
 
        if self.store_id == 'branch':
2008
 
            raise tests.TestNotApplicable(
2009
 
                'branch.conf is *always* created when a branch is initialized')
2010
 
        store = self.get_store(self)
2011
 
        store._load_from_string('foo=bar\n')
2012
 
        self.assertEquals(False, self.has_store(store))
2013
 
        store.save()
2014
 
        self.assertEquals(True, self.has_store(store))
2015
 
        modified_store = self.get_store(self)
2016
 
        sections = list(modified_store.get_sections())
2017
 
        self.assertLength(1, sections)
2018
 
        self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2019
 
 
2020
 
    def test_set_option_in_empty_store(self):
2021
 
        store = self.get_store(self)
2022
 
        section = store.get_mutable_section(None)
2023
 
        section.set('foo', 'bar')
2024
 
        store.save()
2025
 
        modified_store = self.get_store(self)
2026
 
        sections = list(modified_store.get_sections())
2027
 
        self.assertLength(1, sections)
2028
 
        self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2029
 
 
2030
 
    def test_set_option_in_default_section(self):
2031
 
        store = self.get_store(self)
2032
 
        store._load_from_string('')
2033
 
        section = store.get_mutable_section(None)
2034
 
        section.set('foo', 'bar')
2035
 
        store.save()
2036
 
        modified_store = self.get_store(self)
2037
 
        sections = list(modified_store.get_sections())
2038
 
        self.assertLength(1, sections)
2039
 
        self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2040
 
 
2041
 
    def test_set_option_in_named_section(self):
2042
 
        store = self.get_store(self)
2043
 
        store._load_from_string('')
2044
 
        section = store.get_mutable_section('baz')
2045
 
        section.set('foo', 'bar')
2046
 
        store.save()
2047
 
        modified_store = self.get_store(self)
2048
 
        sections = list(modified_store.get_sections())
2049
 
        self.assertLength(1, sections)
2050
 
        self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2051
 
 
2052
 
 
2053
 
class TestIniFileStore(TestStore):
2054
 
 
2055
 
    def test_loading_unknown_file_fails(self):
2056
 
        store = config.IniFileStore(self.get_transport(), 'I-do-not-exist')
2057
 
        self.assertRaises(errors.NoSuchFile, store.load)
2058
 
 
2059
 
    def test_invalid_content(self):
2060
 
        store = config.IniFileStore(self.get_transport(), 'foo.conf', )
2061
 
        self.assertEquals(False, store.is_loaded())
2062
 
        exc = self.assertRaises(
2063
 
            errors.ParseConfigError, store._load_from_string,
2064
 
            'this is invalid !')
2065
 
        self.assertEndsWith(exc.filename, 'foo.conf')
2066
 
        # And the load failed
2067
 
        self.assertEquals(False, store.is_loaded())
2068
 
 
2069
 
    def test_get_embedded_sections(self):
2070
 
        # A more complicated example (which also shows that section names and
2071
 
        # option names share the same name space...)
2072
 
        # FIXME: This should be fixed by forbidding dicts as values ?
2073
 
        # -- vila 2011-04-05
2074
 
        store = config.IniFileStore(self.get_transport(), 'foo.conf', )
2075
 
        store._load_from_string('''
2076
 
foo=bar
2077
 
l=1,2
2078
 
[DEFAULT]
2079
 
foo_in_DEFAULT=foo_DEFAULT
2080
 
[bar]
2081
 
foo_in_bar=barbar
2082
 
[baz]
2083
 
foo_in_baz=barbaz
2084
 
[[qux]]
2085
 
foo_in_qux=quux
2086
 
''')
2087
 
        sections = list(store.get_sections())
2088
 
        self.assertLength(4, sections)
2089
 
        # The default section has no name.
2090
 
        # List values are provided as lists
2091
 
        self.assertSectionContent((None, {'foo': 'bar', 'l': ['1', '2']}),
2092
 
                                  sections[0])
2093
 
        self.assertSectionContent(
2094
 
            ('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
2095
 
        self.assertSectionContent(
2096
 
            ('bar', {'foo_in_bar': 'barbar'}), sections[2])
2097
 
        # sub sections are provided as embedded dicts.
2098
 
        self.assertSectionContent(
2099
 
            ('baz', {'foo_in_baz': 'barbaz', 'qux': {'foo_in_qux': 'quux'}}),
2100
 
            sections[3])
2101
 
 
2102
 
 
2103
 
class TestLockableIniFileStore(TestStore):
2104
 
 
2105
 
    def test_create_store_in_created_dir(self):
2106
 
        t = self.get_transport('dir/subdir')
2107
 
        store = config.LockableIniFileStore(t, 'foo.conf')
2108
 
        store.get_mutable_section(None).set('foo', 'bar')
2109
 
        store.save()
2110
 
 
2111
 
    # FIXME: We should adapt the tests in TestLockableConfig about concurrent
2112
 
    # writes. Since this requires a clearer rewrite, I'll just rely on using
2113
 
    # the same code in LockableIniFileStore (copied from LockableConfig, but
2114
 
    # trivial enough, the main difference is that we add @needs_write_lock on
2115
 
    # save() instead of set_user_option() and remove_user_option()). The intent
2116
 
    # is to ensure that we always get a valid content for the store even when
2117
 
    # concurrent accesses occur, read/write, write/write. It may be worth
2118
 
    # looking into removing the lock dir when it;s not needed anymore and look
2119
 
    # at possible fallouts for concurrent lockers -- vila 20110-04-06
2120
 
 
2121
 
 
2122
 
class TestSectionMatcher(TestStore):
2123
 
 
2124
 
    scenarios = [('location', {'matcher': config.LocationMatcher})]
2125
 
 
2126
 
    def get_store(self, file_name):
2127
 
        return config.IniFileStore(self.get_readonly_transport(), file_name)
2128
 
 
2129
 
    def test_no_matches_for_empty_stores(self):
2130
 
        store = self.get_store('foo.conf')
2131
 
        store._load_from_string('')
2132
 
        matcher = self.matcher(store, '/bar')
2133
 
        self.assertEquals([], list(matcher.get_sections()))
2134
 
 
2135
 
    def test_build_doesnt_load_store(self):
2136
 
        store = self.get_store('foo.conf')
2137
 
        matcher = self.matcher(store, '/bar')
2138
 
        self.assertFalse(store.is_loaded())
2139
 
 
2140
 
 
2141
 
class TestLocationSection(tests.TestCase):
2142
 
 
2143
 
    def get_section(self, options, extra_path):
2144
 
        section = config.Section('foo', options)
2145
 
        # We don't care about the length so we use '0'
2146
 
        return config.LocationSection(section, 0, extra_path)
2147
 
 
2148
 
    def test_simple_option(self):
2149
 
        section = self.get_section({'foo': 'bar'}, '')
2150
 
        self.assertEquals('bar', section.get('foo'))
2151
 
 
2152
 
    def test_option_with_extra_path(self):
2153
 
        section = self.get_section({'foo': 'bar', 'foo:policy': 'appendpath'},
2154
 
                                   'baz')
2155
 
        self.assertEquals('bar/baz', section.get('foo'))
2156
 
 
2157
 
    def test_invalid_policy(self):
2158
 
        section = self.get_section({'foo': 'bar', 'foo:policy': 'die'},
2159
 
                                   'baz')
2160
 
        # invalid policies are ignored
2161
 
        self.assertEquals('bar', section.get('foo'))
2162
 
 
2163
 
 
2164
 
class TestLocationMatcher(TestStore):
2165
 
 
2166
 
    def get_store(self, file_name):
2167
 
        return config.IniFileStore(self.get_readonly_transport(), file_name)
2168
 
 
2169
 
    def test_more_specific_sections_first(self):
2170
 
        store = self.get_store('foo.conf')
2171
 
        store._load_from_string('''
2172
 
[/foo]
2173
 
section=/foo
2174
 
[/foo/bar]
2175
 
section=/foo/bar
2176
 
''')
2177
 
        self.assertEquals(['/foo', '/foo/bar'],
2178
 
                          [section.id for section in store.get_sections()])
2179
 
        matcher = config.LocationMatcher(store, '/foo/bar/baz')
2180
 
        sections = list(matcher.get_sections())
2181
 
        self.assertEquals([3, 2],
2182
 
                          [section.length for section in sections])
2183
 
        self.assertEquals(['/foo/bar', '/foo'],
2184
 
                          [section.id for section in sections])
2185
 
        self.assertEquals(['baz', 'bar/baz'],
2186
 
                          [section.extra_path for section in sections])
2187
 
 
2188
 
    def test_appendpath_in_no_name_section(self):
2189
 
        # It's a bit weird to allow appendpath in a no-name section, but
2190
 
        # someone may found a use for it
2191
 
        store = self.get_store('foo.conf')
2192
 
        store._load_from_string('''
2193
 
foo=bar
2194
 
foo:policy = appendpath
2195
 
''')
2196
 
        matcher = config.LocationMatcher(store, 'dir/subdir')
2197
 
        sections = list(matcher.get_sections())
2198
 
        self.assertLength(1, sections)
2199
 
        self.assertEquals('bar/dir/subdir', sections[0].get('foo'))
2200
 
 
2201
 
    def test_file_urls_are_normalized(self):
2202
 
        store = self.get_store('foo.conf')
2203
 
        if sys.platform == 'win32':
2204
 
            expected_url = 'file:///C:/dir/subdir'
2205
 
            expected_location = 'C:/dir/subdir'
2206
 
        else:
2207
 
            expected_url = 'file:///dir/subdir'
2208
 
            expected_location = '/dir/subdir'
2209
 
        matcher = config.LocationMatcher(store, expected_url)
2210
 
        self.assertEquals(expected_location, matcher.location)
2211
 
 
2212
 
 
2213
 
class TestStackGet(tests.TestCase):
2214
 
 
2215
 
    # FIXME: This should be parametrized for all known Stack or dedicated
2216
 
    # paramerized tests created to avoid bloating -- vila 2011-03-31
2217
 
 
2218
 
    def test_single_config_get(self):
2219
 
        conf = dict(foo='bar')
2220
 
        conf_stack = config.Stack([conf])
2221
 
        self.assertEquals('bar', conf_stack.get('foo'))
2222
 
 
2223
 
    def test_get_first_definition(self):
2224
 
        conf1 = dict(foo='bar')
2225
 
        conf2 = dict(foo='baz')
2226
 
        conf_stack = config.Stack([conf1, conf2])
2227
 
        self.assertEquals('bar', conf_stack.get('foo'))
2228
 
 
2229
 
    def test_get_embedded_definition(self):
2230
 
        conf1 = dict(yy='12')
2231
 
        conf2 = config.Stack([dict(xx='42'), dict(foo='baz')])
2232
 
        conf_stack = config.Stack([conf1, conf2])
2233
 
        self.assertEquals('baz', conf_stack.get('foo'))
2234
 
 
2235
 
    def test_get_for_empty_stack(self):
2236
 
        conf_stack = config.Stack([])
2237
 
        self.assertEquals(None, conf_stack.get('foo'))
2238
 
 
2239
 
    def test_get_for_empty_section_callable(self):
2240
 
        conf_stack = config.Stack([lambda : []])
2241
 
        self.assertEquals(None, conf_stack.get('foo'))
2242
 
 
2243
 
    def test_get_for_broken_callable(self):
2244
 
        # Trying to use and invalid callable raises an exception on first use
2245
 
        conf_stack = config.Stack([lambda : object()])
2246
 
        self.assertRaises(TypeError, conf_stack.get, 'foo')
2247
 
 
2248
 
 
2249
 
class TestStackWithTransport(tests.TestCaseWithTransport):
2250
 
 
2251
 
    def setUp(self):
2252
 
        super(TestStackWithTransport, self).setUp()
2253
 
        # FIXME: A more elaborate builder for the stack would avoid building a
2254
 
        # branch even for tests that don't need it.
2255
 
        self.branch = self.make_branch('branch')
2256
 
 
2257
 
 
2258
 
class TestStackSet(TestStackWithTransport):
2259
 
 
2260
 
    scenarios = [(key, {'get_stack': builder})
2261
 
                 for key, builder in test_stack_builder_registry.iteritems()]
2262
 
 
2263
 
    def test_simple_set(self):
2264
 
        conf = self.get_stack(self)
2265
 
        conf.store._load_from_string('foo=bar')
2266
 
        self.assertEquals('bar', conf.get('foo'))
2267
 
        conf.set('foo', 'baz')
2268
 
        # Did we get it back ?
2269
 
        self.assertEquals('baz', conf.get('foo'))
2270
 
 
2271
 
    def test_set_creates_a_new_section(self):
2272
 
        conf = self.get_stack(self)
2273
 
        conf.set('foo', 'baz')
2274
 
        self.assertEquals, 'baz', conf.get('foo')
2275
 
 
2276
 
 
2277
 
class TestStackRemove(TestStackWithTransport):
2278
 
 
2279
 
    scenarios = [(key, {'get_stack': builder})
2280
 
                 for key, builder in test_stack_builder_registry.iteritems()]
2281
 
 
2282
 
    def test_remove_existing(self):
2283
 
        conf = self.get_stack(self)
2284
 
        conf.store._load_from_string('foo=bar')
2285
 
        self.assertEquals('bar', conf.get('foo'))
2286
 
        conf.remove('foo')
2287
 
        # Did we get it back ?
2288
 
        self.assertEquals(None, conf.get('foo'))
2289
 
 
2290
 
    def test_remove_unknown(self):
2291
 
        conf = self.get_stack(self)
2292
 
        self.assertRaises(KeyError, conf.remove, 'I_do_not_exist')
2293
 
 
2294
 
 
2295
 
class TestConcreteStacks(TestStackWithTransport):
2296
 
 
2297
 
    scenarios = [(key, {'get_stack': builder})
2298
 
                 for key, builder in test_stack_builder_registry.iteritems()]
2299
 
 
2300
 
    def test_build_stack(self):
2301
 
        stack = self.get_stack(self)
2302
 
 
2303
 
 
2304
 
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
2305
 
 
2306
 
    def setUp(self):
2307
 
        super(TestConfigGetOptions, self).setUp()
2308
 
        create_configs(self)
2309
 
 
2310
 
    def test_no_variable(self):
2311
 
        # Using branch should query branch, locations and bazaar
2312
 
        self.assertOptions([], self.branch_config)
2313
 
 
2314
 
    def test_option_in_bazaar(self):
2315
 
        self.bazaar_config.set_user_option('file', 'bazaar')
2316
 
        self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
2317
 
                           self.bazaar_config)
2318
 
 
2319
 
    def test_option_in_locations(self):
2320
 
        self.locations_config.set_user_option('file', 'locations')
2321
 
        self.assertOptions(
2322
 
            [('file', 'locations', self.tree.basedir, 'locations')],
2323
 
            self.locations_config)
2324
 
 
2325
 
    def test_option_in_branch(self):
2326
 
        self.branch_config.set_user_option('file', 'branch')
2327
 
        self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
2328
 
                           self.branch_config)
2329
 
 
2330
 
    def test_option_in_bazaar_and_branch(self):
2331
 
        self.bazaar_config.set_user_option('file', 'bazaar')
2332
 
        self.branch_config.set_user_option('file', 'branch')
2333
 
        self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
2334
 
                            ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
2335
 
                           self.branch_config)
2336
 
 
2337
 
    def test_option_in_branch_and_locations(self):
2338
 
        # Hmm, locations override branch :-/
2339
 
        self.locations_config.set_user_option('file', 'locations')
2340
 
        self.branch_config.set_user_option('file', 'branch')
2341
 
        self.assertOptions(
2342
 
            [('file', 'locations', self.tree.basedir, 'locations'),
2343
 
             ('file', 'branch', 'DEFAULT', 'branch'),],
2344
 
            self.branch_config)
2345
 
 
2346
 
    def test_option_in_bazaar_locations_and_branch(self):
2347
 
        self.bazaar_config.set_user_option('file', 'bazaar')
2348
 
        self.locations_config.set_user_option('file', 'locations')
2349
 
        self.branch_config.set_user_option('file', 'branch')
2350
 
        self.assertOptions(
2351
 
            [('file', 'locations', self.tree.basedir, 'locations'),
2352
 
             ('file', 'branch', 'DEFAULT', 'branch'),
2353
 
             ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
2354
 
            self.branch_config)
2355
 
 
2356
 
 
2357
 
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
2358
 
 
2359
 
    def setUp(self):
2360
 
        super(TestConfigRemoveOption, self).setUp()
2361
 
        create_configs_with_file_option(self)
2362
 
 
2363
 
    def test_remove_in_locations(self):
2364
 
        self.locations_config.remove_user_option('file', self.tree.basedir)
2365
 
        self.assertOptions(
2366
 
            [('file', 'branch', 'DEFAULT', 'branch'),
2367
 
             ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
2368
 
            self.branch_config)
2369
 
 
2370
 
    def test_remove_in_branch(self):
2371
 
        self.branch_config.remove_user_option('file')
2372
 
        self.assertOptions(
2373
 
            [('file', 'locations', self.tree.basedir, 'locations'),
2374
 
             ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
2375
 
            self.branch_config)
2376
 
 
2377
 
    def test_remove_in_bazaar(self):
2378
 
        self.bazaar_config.remove_user_option('file')
2379
 
        self.assertOptions(
2380
 
            [('file', 'locations', self.tree.basedir, 'locations'),
2381
 
             ('file', 'branch', 'DEFAULT', 'branch'),],
2382
 
            self.branch_config)
2383
 
 
2384
 
 
2385
 
class TestConfigGetSections(tests.TestCaseWithTransport):
2386
 
 
2387
 
    def setUp(self):
2388
 
        super(TestConfigGetSections, self).setUp()
2389
 
        create_configs(self)
2390
 
 
2391
 
    def assertSectionNames(self, expected, conf, name=None):
2392
 
        """Check which sections are returned for a given config.
2393
 
 
2394
 
        If fallback configurations exist their sections can be included.
2395
 
 
2396
 
        :param expected: A list of section names.
2397
 
 
2398
 
        :param conf: The configuration that will be queried.
2399
 
 
2400
 
        :param name: An optional section name that will be passed to
2401
 
            get_sections().
2402
 
        """
2403
 
        sections = list(conf._get_sections(name))
2404
 
        self.assertLength(len(expected), sections)
2405
 
        self.assertEqual(expected, [name for name, _, _ in sections])
2406
 
 
2407
 
    def test_bazaar_default_section(self):
2408
 
        self.assertSectionNames(['DEFAULT'], self.bazaar_config)
2409
 
 
2410
 
    def test_locations_default_section(self):
2411
 
        # No sections are defined in an empty file
2412
 
        self.assertSectionNames([], self.locations_config)
2413
 
 
2414
 
    def test_locations_named_section(self):
2415
 
        self.locations_config.set_user_option('file', 'locations')
2416
 
        self.assertSectionNames([self.tree.basedir], self.locations_config)
2417
 
 
2418
 
    def test_locations_matching_sections(self):
2419
 
        loc_config = self.locations_config
2420
 
        loc_config.set_user_option('file', 'locations')
2421
 
        # We need to cheat a bit here to create an option in sections above and
2422
 
        # below the 'location' one.
2423
 
        parser = loc_config._get_parser()
2424
 
        # locations.cong deals with '/' ignoring native os.sep
2425
 
        location_names = self.tree.basedir.split('/')
2426
 
        parent = '/'.join(location_names[:-1])
2427
 
        child = '/'.join(location_names + ['child'])
2428
 
        parser[parent] = {}
2429
 
        parser[parent]['file'] = 'parent'
2430
 
        parser[child] = {}
2431
 
        parser[child]['file'] = 'child'
2432
 
        self.assertSectionNames([self.tree.basedir, parent], loc_config)
2433
 
 
2434
 
    def test_branch_data_default_section(self):
2435
 
        self.assertSectionNames([None],
2436
 
                                self.branch_config._get_branch_data_config())
2437
 
 
2438
 
    def test_branch_default_sections(self):
2439
 
        # No sections are defined in an empty locations file
2440
 
        self.assertSectionNames([None, 'DEFAULT'],
2441
 
                                self.branch_config)
2442
 
        # Unless we define an option
2443
 
        self.branch_config._get_location_config().set_user_option(
2444
 
            'file', 'locations')
2445
 
        self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
2446
 
                                self.branch_config)
2447
 
 
2448
 
    def test_bazaar_named_section(self):
2449
 
        # We need to cheat as the API doesn't give direct access to sections
2450
 
        # other than DEFAULT.
2451
 
        self.bazaar_config.set_alias('bazaar', 'bzr')
2452
 
        self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
2453
 
 
2454
 
 
2455
 
class TestAuthenticationConfigFile(tests.TestCase):
2456
 
    """Test the authentication.conf file matching"""
2457
 
 
2458
 
    def _got_user_passwd(self, expected_user, expected_password,
2459
 
                         config, *args, **kwargs):
2460
 
        credentials = config.get_credentials(*args, **kwargs)
2461
 
        if credentials is None:
2462
 
            user = None
2463
 
            password = None
2464
 
        else:
2465
 
            user = credentials['user']
2466
 
            password = credentials['password']
2467
 
        self.assertEquals(expected_user, user)
2468
 
        self.assertEquals(expected_password, password)
2469
 
 
2470
 
    def test_empty_config(self):
2471
 
        conf = config.AuthenticationConfig(_file=StringIO())
2472
 
        self.assertEquals({}, conf._get_config())
2473
 
        self._got_user_passwd(None, None, conf, 'http', 'foo.net')
2474
 
 
2475
 
    def test_missing_auth_section_header(self):
2476
 
        conf = config.AuthenticationConfig(_file=StringIO('foo = bar'))
2477
 
        self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
2478
 
 
2479
 
    def test_auth_section_header_not_closed(self):
2480
 
        conf = config.AuthenticationConfig(_file=StringIO('[DEF'))
2481
 
        self.assertRaises(errors.ParseConfigError, conf._get_config)
2482
 
 
2483
 
    def test_auth_value_not_boolean(self):
2484
 
        conf = config.AuthenticationConfig(_file=StringIO(
2485
 
                """[broken]
2486
 
scheme=ftp
2487
 
user=joe
2488
 
verify_certificates=askme # Error: Not a boolean
2489
 
"""))
2490
 
        self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
2491
 
 
2492
 
    def test_auth_value_not_int(self):
2493
 
        conf = config.AuthenticationConfig(_file=StringIO(
2494
 
                """[broken]
2495
 
scheme=ftp
2496
 
user=joe
2497
 
port=port # Error: Not an int
2498
 
"""))
2499
 
        self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
2500
 
 
2501
 
    def test_unknown_password_encoding(self):
2502
 
        conf = config.AuthenticationConfig(_file=StringIO(
2503
 
                """[broken]
2504
 
scheme=ftp
2505
 
user=joe
2506
 
password_encoding=unknown
2507
 
"""))
2508
 
        self.assertRaises(ValueError, conf.get_password,
2509
 
                          'ftp', 'foo.net', 'joe')
2510
 
 
2511
 
    def test_credentials_for_scheme_host(self):
2512
 
        conf = config.AuthenticationConfig(_file=StringIO(
2513
 
                """# Identity on foo.net
2514
 
[ftp definition]
2515
 
scheme=ftp
2516
 
host=foo.net
2517
 
user=joe
2518
 
password=secret-pass
2519
 
"""))
2520
 
        # Basic matching
2521
 
        self._got_user_passwd('joe', 'secret-pass', conf, 'ftp', 'foo.net')
2522
 
        # different scheme
2523
 
        self._got_user_passwd(None, None, conf, 'http', 'foo.net')
2524
 
        # different host
2525
 
        self._got_user_passwd(None, None, conf, 'ftp', 'bar.net')
2526
 
 
2527
 
    def test_credentials_for_host_port(self):
2528
 
        conf = config.AuthenticationConfig(_file=StringIO(
2529
 
                """# Identity on foo.net
2530
 
[ftp definition]
2531
 
scheme=ftp
2532
 
port=10021
2533
 
host=foo.net
2534
 
user=joe
2535
 
password=secret-pass
2536
 
"""))
2537
 
        # No port
2538
 
        self._got_user_passwd('joe', 'secret-pass',
2539
 
                              conf, 'ftp', 'foo.net', port=10021)
2540
 
        # different port
2541
 
        self._got_user_passwd(None, None, conf, 'ftp', 'foo.net')
2542
 
 
2543
 
    def test_for_matching_host(self):
2544
 
        conf = config.AuthenticationConfig(_file=StringIO(
2545
 
                """# Identity on foo.net
2546
 
[sourceforge]
2547
 
scheme=bzr
2548
 
host=bzr.sf.net
2549
 
user=joe
2550
 
password=joepass
2551
 
[sourceforge domain]
2552
 
scheme=bzr
2553
 
host=.bzr.sf.net
2554
 
user=georges
2555
 
password=bendover
2556
 
"""))
2557
 
        # matching domain
2558
 
        self._got_user_passwd('georges', 'bendover',
2559
 
                              conf, 'bzr', 'foo.bzr.sf.net')
2560
 
        # phishing attempt
2561
 
        self._got_user_passwd(None, None,
2562
 
                              conf, 'bzr', 'bbzr.sf.net')
2563
 
 
2564
 
    def test_for_matching_host_None(self):
2565
 
        conf = config.AuthenticationConfig(_file=StringIO(
2566
 
                """# Identity on foo.net
2567
 
[catchup bzr]
2568
 
scheme=bzr
2569
 
user=joe
2570
 
password=joepass
2571
 
[DEFAULT]
2572
 
user=georges
2573
 
password=bendover
2574
 
"""))
2575
 
        # match no host
2576
 
        self._got_user_passwd('joe', 'joepass',
2577
 
                              conf, 'bzr', 'quux.net')
2578
 
        # no host but different scheme
2579
 
        self._got_user_passwd('georges', 'bendover',
2580
 
                              conf, 'ftp', 'quux.net')
2581
 
 
2582
 
    def test_credentials_for_path(self):
2583
 
        conf = config.AuthenticationConfig(_file=StringIO(
2584
 
                """
2585
 
[http dir1]
2586
 
scheme=http
2587
 
host=bar.org
2588
 
path=/dir1
2589
 
user=jim
2590
 
password=jimpass
2591
 
[http dir2]
2592
 
scheme=http
2593
 
host=bar.org
2594
 
path=/dir2
2595
 
user=georges
2596
 
password=bendover
2597
 
"""))
2598
 
        # no path no dice
2599
 
        self._got_user_passwd(None, None,
2600
 
                              conf, 'http', host='bar.org', path='/dir3')
2601
 
        # matching path
2602
 
        self._got_user_passwd('georges', 'bendover',
2603
 
                              conf, 'http', host='bar.org', path='/dir2')
2604
 
        # matching subdir
2605
 
        self._got_user_passwd('jim', 'jimpass',
2606
 
                              conf, 'http', host='bar.org',path='/dir1/subdir')
2607
 
 
2608
 
    def test_credentials_for_user(self):
2609
 
        conf = config.AuthenticationConfig(_file=StringIO(
2610
 
                """
2611
 
[with user]
2612
 
scheme=http
2613
 
host=bar.org
2614
 
user=jim
2615
 
password=jimpass
2616
 
"""))
2617
 
        # Get user
2618
 
        self._got_user_passwd('jim', 'jimpass',
2619
 
                              conf, 'http', 'bar.org')
2620
 
        # Get same user
2621
 
        self._got_user_passwd('jim', 'jimpass',
2622
 
                              conf, 'http', 'bar.org', user='jim')
2623
 
        # Don't get a different user if one is specified
2624
 
        self._got_user_passwd(None, None,
2625
 
                              conf, 'http', 'bar.org', user='georges')
2626
 
 
2627
 
    def test_credentials_for_user_without_password(self):
2628
 
        conf = config.AuthenticationConfig(_file=StringIO(
2629
 
                """
2630
 
[without password]
2631
 
scheme=http
2632
 
host=bar.org
2633
 
user=jim
2634
 
"""))
2635
 
        # Get user but no password
2636
 
        self._got_user_passwd('jim', None,
2637
 
                              conf, 'http', 'bar.org')
2638
 
 
2639
 
    def test_verify_certificates(self):
2640
 
        conf = config.AuthenticationConfig(_file=StringIO(
2641
 
                """
2642
 
[self-signed]
2643
 
scheme=https
2644
 
host=bar.org
2645
 
user=jim
2646
 
password=jimpass
2647
 
verify_certificates=False
2648
 
[normal]
2649
 
scheme=https
2650
 
host=foo.net
2651
 
user=georges
2652
 
password=bendover
2653
 
"""))
2654
 
        credentials = conf.get_credentials('https', 'bar.org')
2655
 
        self.assertEquals(False, credentials.get('verify_certificates'))
2656
 
        credentials = conf.get_credentials('https', 'foo.net')
2657
 
        self.assertEquals(True, credentials.get('verify_certificates'))
2658
 
 
2659
 
 
2660
 
class TestAuthenticationStorage(tests.TestCaseInTempDir):
2661
 
 
2662
 
    def test_set_credentials(self):
2663
 
        conf = config.AuthenticationConfig()
2664
 
        conf.set_credentials('name', 'host', 'user', 'scheme', 'password',
2665
 
        99, path='/foo', verify_certificates=False, realm='realm')
2666
 
        credentials = conf.get_credentials(host='host', scheme='scheme',
2667
 
                                           port=99, path='/foo',
2668
 
                                           realm='realm')
2669
 
        CREDENTIALS = {'name': 'name', 'user': 'user', 'password': 'password',
2670
 
                       'verify_certificates': False, 'scheme': 'scheme', 
2671
 
                       'host': 'host', 'port': 99, 'path': '/foo', 
2672
 
                       'realm': 'realm'}
2673
 
        self.assertEqual(CREDENTIALS, credentials)
2674
 
        credentials_from_disk = config.AuthenticationConfig().get_credentials(
2675
 
            host='host', scheme='scheme', port=99, path='/foo', realm='realm')
2676
 
        self.assertEqual(CREDENTIALS, credentials_from_disk)
2677
 
 
2678
 
    def test_reset_credentials_different_name(self):
2679
 
        conf = config.AuthenticationConfig()
2680
 
        conf.set_credentials('name', 'host', 'user', 'scheme', 'password'),
2681
 
        conf.set_credentials('name2', 'host', 'user2', 'scheme', 'password'),
2682
 
        self.assertIs(None, conf._get_config().get('name'))
2683
 
        credentials = conf.get_credentials(host='host', scheme='scheme')
2684
 
        CREDENTIALS = {'name': 'name2', 'user': 'user2', 'password':
2685
 
                       'password', 'verify_certificates': True, 
2686
 
                       'scheme': 'scheme', 'host': 'host', 'port': None, 
2687
 
                       'path': None, 'realm': None}
2688
 
        self.assertEqual(CREDENTIALS, credentials)
2689
 
 
2690
 
 
2691
 
class TestAuthenticationConfig(tests.TestCase):
2692
 
    """Test AuthenticationConfig behaviour"""
2693
 
 
2694
 
    def _check_default_password_prompt(self, expected_prompt_format, scheme,
2695
 
                                       host=None, port=None, realm=None,
2696
 
                                       path=None):
2697
 
        if host is None:
2698
 
            host = 'bar.org'
2699
 
        user, password = 'jim', 'precious'
2700
 
        expected_prompt = expected_prompt_format % {
2701
 
            'scheme': scheme, 'host': host, 'port': port,
2702
 
            'user': user, 'realm': realm}
2703
 
 
2704
 
        stdout = tests.StringIOWrapper()
2705
 
        stderr = tests.StringIOWrapper()
2706
 
        ui.ui_factory = tests.TestUIFactory(stdin=password + '\n',
2707
 
                                            stdout=stdout, stderr=stderr)
2708
 
        # We use an empty conf so that the user is always prompted
2709
 
        conf = config.AuthenticationConfig()
2710
 
        self.assertEquals(password,
2711
 
                          conf.get_password(scheme, host, user, port=port,
2712
 
                                            realm=realm, path=path))
2713
 
        self.assertEquals(expected_prompt, stderr.getvalue())
2714
 
        self.assertEquals('', stdout.getvalue())
2715
 
 
2716
 
    def _check_default_username_prompt(self, expected_prompt_format, scheme,
2717
 
                                       host=None, port=None, realm=None,
2718
 
                                       path=None):
2719
 
        if host is None:
2720
 
            host = 'bar.org'
2721
 
        username = 'jim'
2722
 
        expected_prompt = expected_prompt_format % {
2723
 
            'scheme': scheme, 'host': host, 'port': port,
2724
 
            'realm': realm}
2725
 
        stdout = tests.StringIOWrapper()
2726
 
        stderr = tests.StringIOWrapper()
2727
 
        ui.ui_factory = tests.TestUIFactory(stdin=username+ '\n',
2728
 
                                            stdout=stdout, stderr=stderr)
2729
 
        # We use an empty conf so that the user is always prompted
2730
 
        conf = config.AuthenticationConfig()
2731
 
        self.assertEquals(username, conf.get_user(scheme, host, port=port,
2732
 
                          realm=realm, path=path, ask=True))
2733
 
        self.assertEquals(expected_prompt, stderr.getvalue())
2734
 
        self.assertEquals('', stdout.getvalue())
2735
 
 
2736
 
    def test_username_defaults_prompts(self):
2737
 
        # HTTP prompts can't be tested here, see test_http.py
2738
 
        self._check_default_username_prompt(u'FTP %(host)s username: ', 'ftp')
2739
 
        self._check_default_username_prompt(
2740
 
            u'FTP %(host)s:%(port)d username: ', 'ftp', port=10020)
2741
 
        self._check_default_username_prompt(
2742
 
            u'SSH %(host)s:%(port)d username: ', 'ssh', port=12345)
2743
 
 
2744
 
    def test_username_default_no_prompt(self):
2745
 
        conf = config.AuthenticationConfig()
2746
 
        self.assertEquals(None,
2747
 
            conf.get_user('ftp', 'example.com'))
2748
 
        self.assertEquals("explicitdefault",
2749
 
            conf.get_user('ftp', 'example.com', default="explicitdefault"))
2750
 
 
2751
 
    def test_password_default_prompts(self):
2752
 
        # HTTP prompts can't be tested here, see test_http.py
2753
 
        self._check_default_password_prompt(
2754
 
            u'FTP %(user)s@%(host)s password: ', 'ftp')
2755
 
        self._check_default_password_prompt(
2756
 
            u'FTP %(user)s@%(host)s:%(port)d password: ', 'ftp', port=10020)
2757
 
        self._check_default_password_prompt(
2758
 
            u'SSH %(user)s@%(host)s:%(port)d password: ', 'ssh', port=12345)
2759
 
        # SMTP port handling is a bit special (it's handled if embedded in the
2760
 
        # host too)
2761
 
        # FIXME: should we: forbid that, extend it to other schemes, leave
2762
 
        # things as they are that's fine thank you ?
2763
 
        self._check_default_password_prompt(
2764
 
            u'SMTP %(user)s@%(host)s password: ', 'smtp')
2765
 
        self._check_default_password_prompt(
2766
 
            u'SMTP %(user)s@%(host)s password: ', 'smtp', host='bar.org:10025')
2767
 
        self._check_default_password_prompt(
2768
 
            u'SMTP %(user)s@%(host)s:%(port)d password: ', 'smtp', port=10025)
2769
 
 
2770
 
    def test_ssh_password_emits_warning(self):
2771
 
        conf = config.AuthenticationConfig(_file=StringIO(
2772
 
                """
2773
 
[ssh with password]
2774
 
scheme=ssh
2775
 
host=bar.org
2776
 
user=jim
2777
 
password=jimpass
2778
 
"""))
2779
 
        entered_password = 'typed-by-hand'
2780
 
        stdout = tests.StringIOWrapper()
2781
 
        stderr = tests.StringIOWrapper()
2782
 
        ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
2783
 
                                            stdout=stdout, stderr=stderr)
2784
 
 
2785
 
        # Since the password defined in the authentication config is ignored,
2786
 
        # the user is prompted
2787
 
        self.assertEquals(entered_password,
2788
 
                          conf.get_password('ssh', 'bar.org', user='jim'))
2789
 
        self.assertContainsRe(
2790
 
            self.get_log(),
2791
 
            'password ignored in section \[ssh with password\]')
2792
 
 
2793
 
    def test_ssh_without_password_doesnt_emit_warning(self):
2794
 
        conf = config.AuthenticationConfig(_file=StringIO(
2795
 
                """
2796
 
[ssh with password]
2797
 
scheme=ssh
2798
 
host=bar.org
2799
 
user=jim
2800
 
"""))
2801
 
        entered_password = 'typed-by-hand'
2802
 
        stdout = tests.StringIOWrapper()
2803
 
        stderr = tests.StringIOWrapper()
2804
 
        ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
2805
 
                                            stdout=stdout,
2806
 
                                            stderr=stderr)
2807
 
 
2808
 
        # Since the password defined in the authentication config is ignored,
2809
 
        # the user is prompted
2810
 
        self.assertEquals(entered_password,
2811
 
                          conf.get_password('ssh', 'bar.org', user='jim'))
2812
 
        # No warning shoud be emitted since there is no password. We are only
2813
 
        # providing "user".
2814
 
        self.assertNotContainsRe(
2815
 
            self.get_log(),
2816
 
            'password ignored in section \[ssh with password\]')
2817
 
 
2818
 
    def test_uses_fallback_stores(self):
2819
 
        self.overrideAttr(config, 'credential_store_registry',
2820
 
                          config.CredentialStoreRegistry())
2821
 
        store = StubCredentialStore()
2822
 
        store.add_credentials("http", "example.com", "joe", "secret")
2823
 
        config.credential_store_registry.register("stub", store, fallback=True)
2824
 
        conf = config.AuthenticationConfig(_file=StringIO())
2825
 
        creds = conf.get_credentials("http", "example.com")
2826
 
        self.assertEquals("joe", creds["user"])
2827
 
        self.assertEquals("secret", creds["password"])
2828
 
 
2829
 
 
2830
 
class StubCredentialStore(config.CredentialStore):
2831
 
 
2832
 
    def __init__(self):
2833
 
        self._username = {}
2834
 
        self._password = {}
2835
 
 
2836
 
    def add_credentials(self, scheme, host, user, password=None):
2837
 
        self._username[(scheme, host)] = user
2838
 
        self._password[(scheme, host)] = password
2839
 
 
2840
 
    def get_credentials(self, scheme, host, port=None, user=None,
2841
 
        path=None, realm=None):
2842
 
        key = (scheme, host)
2843
 
        if not key in self._username:
2844
 
            return None
2845
 
        return { "scheme": scheme, "host": host, "port": port,
2846
 
                "user": self._username[key], "password": self._password[key]}
2847
 
 
2848
 
 
2849
 
class CountingCredentialStore(config.CredentialStore):
2850
 
 
2851
 
    def __init__(self):
2852
 
        self._calls = 0
2853
 
 
2854
 
    def get_credentials(self, scheme, host, port=None, user=None,
2855
 
        path=None, realm=None):
2856
 
        self._calls += 1
2857
 
        return None
2858
 
 
2859
 
 
2860
 
class TestCredentialStoreRegistry(tests.TestCase):
2861
 
 
2862
 
    def _get_cs_registry(self):
2863
 
        return config.credential_store_registry
2864
 
 
2865
 
    def test_default_credential_store(self):
2866
 
        r = self._get_cs_registry()
2867
 
        default = r.get_credential_store(None)
2868
 
        self.assertIsInstance(default, config.PlainTextCredentialStore)
2869
 
 
2870
 
    def test_unknown_credential_store(self):
2871
 
        r = self._get_cs_registry()
2872
 
        # It's hard to imagine someone creating a credential store named
2873
 
        # 'unknown' so we use that as an never registered key.
2874
 
        self.assertRaises(KeyError, r.get_credential_store, 'unknown')
2875
 
 
2876
 
    def test_fallback_none_registered(self):
2877
 
        r = config.CredentialStoreRegistry()
2878
 
        self.assertEquals(None,
2879
 
                          r.get_fallback_credentials("http", "example.com"))
2880
 
 
2881
 
    def test_register(self):
2882
 
        r = config.CredentialStoreRegistry()
2883
 
        r.register("stub", StubCredentialStore(), fallback=False)
2884
 
        r.register("another", StubCredentialStore(), fallback=True)
2885
 
        self.assertEquals(["another", "stub"], r.keys())
2886
 
 
2887
 
    def test_register_lazy(self):
2888
 
        r = config.CredentialStoreRegistry()
2889
 
        r.register_lazy("stub", "bzrlib.tests.test_config",
2890
 
                        "StubCredentialStore", fallback=False)
2891
 
        self.assertEquals(["stub"], r.keys())
2892
 
        self.assertIsInstance(r.get_credential_store("stub"),
2893
 
                              StubCredentialStore)
2894
 
 
2895
 
    def test_is_fallback(self):
2896
 
        r = config.CredentialStoreRegistry()
2897
 
        r.register("stub1", None, fallback=False)
2898
 
        r.register("stub2", None, fallback=True)
2899
 
        self.assertEquals(False, r.is_fallback("stub1"))
2900
 
        self.assertEquals(True, r.is_fallback("stub2"))
2901
 
 
2902
 
    def test_no_fallback(self):
2903
 
        r = config.CredentialStoreRegistry()
2904
 
        store = CountingCredentialStore()
2905
 
        r.register("count", store, fallback=False)
2906
 
        self.assertEquals(None,
2907
 
                          r.get_fallback_credentials("http", "example.com"))
2908
 
        self.assertEquals(0, store._calls)
2909
 
 
2910
 
    def test_fallback_credentials(self):
2911
 
        r = config.CredentialStoreRegistry()
2912
 
        store = StubCredentialStore()
2913
 
        store.add_credentials("http", "example.com",
2914
 
                              "somebody", "geheim")
2915
 
        r.register("stub", store, fallback=True)
2916
 
        creds = r.get_fallback_credentials("http", "example.com")
2917
 
        self.assertEquals("somebody", creds["user"])
2918
 
        self.assertEquals("geheim", creds["password"])
2919
 
 
2920
 
    def test_fallback_first_wins(self):
2921
 
        r = config.CredentialStoreRegistry()
2922
 
        stub1 = StubCredentialStore()
2923
 
        stub1.add_credentials("http", "example.com",
2924
 
                              "somebody", "stub1")
2925
 
        r.register("stub1", stub1, fallback=True)
2926
 
        stub2 = StubCredentialStore()
2927
 
        stub2.add_credentials("http", "example.com",
2928
 
                              "somebody", "stub2")
2929
 
        r.register("stub2", stub1, fallback=True)
2930
 
        creds = r.get_fallback_credentials("http", "example.com")
2931
 
        self.assertEquals("somebody", creds["user"])
2932
 
        self.assertEquals("stub1", creds["password"])
2933
 
 
2934
 
 
2935
 
class TestPlainTextCredentialStore(tests.TestCase):
2936
 
 
2937
 
    def test_decode_password(self):
2938
 
        r = config.credential_store_registry
2939
 
        plain_text = r.get_credential_store()
2940
 
        decoded = plain_text.decode_password(dict(password='secret'))
2941
 
        self.assertEquals('secret', decoded)
2942
 
 
2943
 
 
2944
 
# FIXME: Once we have a way to declare authentication to all test servers, we
2945
 
# can implement generic tests.
2946
 
# test_user_password_in_url
2947
 
# test_user_in_url_password_from_config
2948
 
# test_user_in_url_password_prompted
2949
 
# test_user_in_config
2950
 
# test_user_getpass.getuser
2951
 
# test_user_prompted ?
2952
 
class TestAuthenticationRing(tests.TestCaseWithTransport):
2953
 
    pass
2954
 
 
2955
 
 
2956
 
class TestAutoUserId(tests.TestCase):
2957
 
    """Test inferring an automatic user name."""
2958
 
 
2959
 
    def test_auto_user_id(self):
2960
 
        """Automatic inference of user name.
2961
 
        
2962
 
        This is a bit hard to test in an isolated way, because it depends on
2963
 
        system functions that go direct to /etc or perhaps somewhere else.
2964
 
        But it's reasonable to say that on Unix, with an /etc/mailname, we ought
2965
 
        to be able to choose a user name with no configuration.
2966
 
        """
2967
 
        if sys.platform == 'win32':
2968
 
            raise TestSkipped("User name inference not implemented on win32")
2969
 
        realname, address = config._auto_user_id()
2970
 
        if os.path.exists('/etc/mailname'):
2971
 
            self.assertIsNot(None, realname)
2972
 
            self.assertIsNot(None, address)
2973
 
        else:
2974
 
            self.assertEquals((None, None), (realname, address))
2975