1
# Copyright (C) 2005-2011 Canonical Ltd
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.
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.
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
17
"""Tests for finding and reading the bzr config file[s]."""
18
# import system imports here
19
from cStringIO import StringIO
25
from testtools import matchers
27
#import bzrlib specific imports here
44
from bzrlib.tests import (
49
from bzrlib.util.configobj import configobj
52
def lockable_config_scenarios():
55
{'config_class': config.GlobalConfig,
57
'config_section': 'DEFAULT'}),
59
{'config_class': config.LocationConfig,
61
'config_section': '.'}),]
64
load_tests = scenarios.load_tests_apply_scenarios
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.
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(),
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))
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))
98
sample_long_alias="log -r-15..-1 --line"
99
sample_config_text = u"""
101
email=Erik B\u00e5gfors <erik@bagfors.nu>
103
change_editor=vimdiff -of @new_path @old_path
104
gpg_signing_command=gnome-gpg
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
112
ll=""" + sample_long_alias + "\n"
115
sample_always_signatures = """
117
check_signatures=ignore
118
create_signatures=always
121
sample_ignore_signatures = """
123
check_signatures=require
124
create_signatures=never
127
sample_maybe_signatures = """
129
check_signatures=ignore
130
create_signatures=when-required
133
sample_branches_text = """
134
[http://www.example.com]
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
145
[http://www.example.com/norecurse]
146
# configuration items that only apply to this dir
148
normal_option = norecurse
149
[http://www.example.com/dir]
150
appendpath_option = normal
152
check_signatures=require
153
# test trailing / matching with no children
155
check_signatures=check-available
156
gpg_signing_command=false
157
user_local_option=local
158
# test trailing / matching
160
#subdirs will match but not the parent
162
check_signatures=ignore
163
post_commit=bzrlib.tests.test_config.post_commit
164
#testing explicit beats globs
168
def create_configs(test):
169
"""Create configuration files for a given test.
171
This requires creating a tree (and populate the ``test.tree`` attribute)
172
and its associated branch and will populate the following attributes:
174
- branch_config: A BranchConfig for the associated branch.
176
- locations_config : A LocationConfig for the associated branch
178
- bazaar_config: A GlobalConfig.
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.
183
tree = test.make_branch_and_tree('tree')
185
test.branch_config = config.BranchConfig(tree.branch)
186
test.locations_config = config.LocationConfig(tree.basedir)
187
test.bazaar_config = config.GlobalConfig()
190
def create_configs_with_file_option(test):
191
"""Create configuration files with a ``file`` option set in each.
193
This builds on ``create_configs`` and add one ``file`` option in each
194
configuration with a value which allows identifying the configuration file.
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')
202
class TestOptionsMixin:
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))
211
class InstrumentedConfigObj(object):
212
"""A config obj look-enough-alike to record calls made to it."""
214
def __contains__(self, thing):
215
self._calls.append(('__contains__', thing))
218
def __getitem__(self, key):
219
self._calls.append(('__getitem__', key))
222
def __init__(self, input, encoding=None):
223
self._calls = [('__init__', input, encoding)]
225
def __setitem__(self, key, value):
226
self._calls.append(('__setitem__', key, value))
228
def __delitem__(self, key):
229
self._calls.append(('__delitem__', key))
232
self._calls.append(('keys',))
236
self._calls.append(('reload',))
238
def write(self, arg):
239
self._calls.append(('write',))
241
def as_bool(self, value):
242
self._calls.append(('as_bool', value))
245
def get_value(self, section, name):
246
self._calls.append(('get_value', section, name))
250
class FakeBranch(object):
252
def __init__(self, base=None, user_id=None):
254
self.base = "http://example.com/branches/demo"
257
self._transport = self.control_files = \
258
FakeControlFilesAndTransport(user_id=user_id)
260
def _get_config(self):
261
return config.TransportConfig(self._transport, 'branch.conf')
263
def lock_write(self):
270
class FakeControlFilesAndTransport(object):
272
def __init__(self, user_id=None):
275
self.files['email'] = user_id
276
self._transport = self
278
def get_utf8(self, filename):
280
raise AssertionError("get_utf8 should no longer be used")
282
def get(self, filename):
285
return StringIO(self.files[filename])
287
raise errors.NoSuchFile(filename)
289
def get_bytes(self, filename):
292
return self.files[filename]
294
raise errors.NoSuchFile(filename)
296
def put(self, filename, fileobj):
297
self.files[filename] = fileobj.read()
299
def put_file(self, filename, fileobj):
300
return self.put(filename, fileobj)
303
class InstrumentedConfig(config.Config):
304
"""An instrumented config that supplies stubs for template methods."""
307
super(InstrumentedConfig, self).__init__()
309
self._signatures = config.CHECK_NEVER
311
def _get_user_id(self):
312
self._calls.append('_get_user_id')
313
return "Robert Collins <robert.collins@example.org>"
315
def _get_signature_checking(self):
316
self._calls.append('_get_signature_checking')
317
return self._signatures
319
def _get_change_editor(self):
320
self._calls.append('_get_change_editor')
321
return 'vimdiff -fo @new_path @old_path'
324
bool_config = """[DEFAULT]
333
class TestConfigObj(tests.TestCase):
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)
342
def test_hash_sign_in_value(self):
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)
347
co = config.ConfigObj()
348
co['test'] = 'foo#bar'
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')
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 """
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
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'])
381
erroneous_config = """[section] # line 1
384
whocares=notme # line 4
388
class TestConfigObjErrors(tests.TestCase):
390
def test_duplicate_section_name_error_line(self):
392
co = configobj.ConfigObj(StringIO(erroneous_config),
394
except config.configobj.DuplicateError, e:
395
self.assertEqual(3, e.line_number)
397
self.fail('Error in config file not detected')
400
class TestConfig(tests.TestCase):
402
def test_constructs(self):
405
def test_no_default_editor(self):
406
self.assertRaises(NotImplementedError, config.Config().get_editor)
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)
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)
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())
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)
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)
439
def test_gpg_signing_command_default(self):
440
my_config = config.Config()
441
self.assertEqual('gpg', my_config.gpg_signing_command())
443
def test_get_user_option_default(self):
444
my_config = config.Config()
445
self.assertEqual(None, my_config.get_user_option('no_option'))
447
def test_post_commit_default(self):
448
my_config = config.Config()
449
self.assertEqual(None, my_config.post_commit())
451
def test_log_format_default(self):
452
my_config = config.Config()
453
self.assertEqual('long', my_config.log_format())
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)
464
class TestConfigPath(tests.TestCase):
467
super(TestConfigPath, self).setUp()
468
self.overrideEnv('HOME', '/home/bogus')
469
self.overrideEnv('XDG_CACHE_DIR', '')
470
if sys.platform == 'win32':
472
'BZR_HOME', r'C:\Documents and Settings\bogus\Application Data')
474
'C:/Documents and Settings/bogus/Application Data/bazaar/2.0'
476
self.bzr_home = '/home/bogus/.bazaar'
478
def test_config_dir(self):
479
self.assertEqual(config.config_dir(), self.bzr_home)
481
def test_config_filename(self):
482
self.assertEqual(config.config_filename(),
483
self.bzr_home + '/bazaar.conf')
485
def test_locations_config_filename(self):
486
self.assertEqual(config.locations_config_filename(),
487
self.bzr_home + '/locations.conf')
489
def test_authentication_config_filename(self):
490
self.assertEqual(config.authentication_config_filename(),
491
self.bzr_home + '/authentication.conf')
493
def test_xdg_cache_dir(self):
494
self.assertEqual(config.xdg_cache_dir(),
495
'/home/bogus/.cache')
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
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)
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')
515
self.assertEqual(config.config_dir(), newdir)
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')
523
self.assertEqual(config.config_dir(), newdir)
526
class TestIniConfig(tests.TestCaseInTempDir):
528
def make_config_parser(self, s):
529
conf = config.IniBasedConfig.from_string(s)
530
return conf, conf._get_parser()
533
class TestIniConfigBuilding(TestIniConfig):
535
def test_contructs(self):
536
my_config = config.IniBasedConfig()
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)
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)
547
def _dummy_chown(self, path, uid, gid):
548
self.path, self.uid, self.gid = path, uid, gid
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))
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)
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)
577
class TestIniConfigSaving(tests.TestCaseInTempDir):
579
def test_cant_save_without_a_file_name(self):
580
conf = config.IniBasedConfig()
581
self.assertRaises(AssertionError, conf._write_config_file)
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')
590
class TestIniConfigOptionExpansionDefaultValue(tests.TestCaseInTempDir):
591
"""What is the default value of expand for config options.
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
598
Note that these tests relies on config._expand_default_value being already
599
overwritten in the parent class setUp.
603
super(TestIniConfigOptionExpansionDefaultValue, self).setUp()
607
self.warnings.append(args[0] % args[1:])
608
self.overrideAttr(trace, 'warning', warning)
610
def get_config(self, expand):
611
c = config.GlobalConfig.from_string('bzr.config.expand=%s' % (expand,),
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)
620
def test_default_is_None(self):
621
self.assertEquals(None, config._expand_default_value)
623
def test_default_is_False_even_if_None(self):
624
self.config = self.get_config(None)
625
self.assertExpandIs(False)
627
def test_default_is_False_even_if_invalid(self):
628
self.config = self.get_config('<your choice>')
629
self.assertExpandIs(False)
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)
635
'Value "<your choice>" is not a boolean for "bzr.config.expand"',
638
def test_default_is_True(self):
639
self.config = self.get_config(True)
640
self.assertExpandIs(True)
642
def test_default_is_False(self):
643
self.config = self.get_config(False)
644
self.assertExpandIs(False)
647
class TestIniConfigOptionExpansion(tests.TestCase):
648
"""Test option expansion from the IniConfig level.
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
654
# FIXME: This should be rewritten when all configs share a storage
655
# implementation -- vila 2011-02-18
657
def get_config(self, string=None):
660
c = config.IniBasedConfig.from_string(string)
663
def assertExpansion(self, expected, conf, string, env=None):
664
self.assertEquals(expected, conf.expand_options(string, env))
666
def test_no_expansion(self):
667
c = self.get_config('')
668
self.assertExpansion('foo', c, 'foo')
670
def test_env_adding_options(self):
671
c = self.get_config('')
672
self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
674
def test_env_overriding_options(self):
675
c = self.get_config('foo=baz')
676
self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
678
def test_simple_ref(self):
679
c = self.get_config('foo=xxx')
680
self.assertExpansion('xxx', c, '{foo}')
682
def test_unknown_ref(self):
683
c = self.get_config('')
684
self.assertRaises(errors.ExpandingUnknownOption,
685
c.expand_options, '{foo}')
687
def test_indirect_ref(self):
688
c = self.get_config('''
692
self.assertExpansion('xxx', c, '{bar}')
694
def test_embedded_ref(self):
695
c = self.get_config('''
699
self.assertExpansion('xxx', c, '{{bar}}')
701
def test_simple_loop(self):
702
c = self.get_config('foo={foo}')
703
self.assertRaises(errors.OptionExpansionLoop, c.expand_options, '{foo}')
705
def test_indirect_loop(self):
706
c = self.get_config('''
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)
716
conf = self.get_config('''
720
list={foo},{bar},{baz}
722
self.assertEquals(['start', 'middle', 'end'],
723
conf.get_user_option('list', expand=True))
725
def test_cascading_list(self):
726
conf = self.get_config('''
732
self.assertEquals(['start', 'middle', 'end'],
733
conf.get_user_option('list', expand=True))
735
def test_pathological_hidden_list(self):
736
conf = self.get_config('''
742
hidden={start}{middle}{end}
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))
749
class TestLocationConfigOptionExpansion(tests.TestCaseInTempDir):
751
def get_config(self, location, string=None):
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)
759
def test_dont_cross_unrelated_section(self):
760
c = self.get_config('/another/branch/path','''
765
[/another/branch/path]
768
self.assertRaises(errors.ExpandingUnknownOption,
769
c.get_user_option, 'bar', expand=True)
771
def test_cross_related_sections(self):
772
c = self.get_config('/project/branch/path','''
776
[/project/branch/path]
779
self.assertEquals('quux', c.get_user_option('bar', expand=True))
782
class TestIniBaseConfigOnDisk(tests.TestCaseInTempDir):
784
def test_cannot_reload_without_name(self):
785
conf = config.IniBasedConfig.from_string(sample_config_text)
786
self.assertRaises(AssertionError, conf.reload)
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
799
self.assertEqual('emacs', c1.get_user_option('editor'))
802
class TestLockableConfig(tests.TestCaseInTempDir):
804
scenarios = lockable_config_scenarios()
809
config_section = None
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)
816
def get_existing_config(self):
817
return self.config_class(*self.config_args)
819
def create_config(self, content):
820
kwargs = dict(save=True)
821
c = self.config_class.from_string(content, *self.config_args, **kwargs)
824
def test_simple_read_access(self):
825
self.assertEquals('1', self.config.get_user_option('one'))
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'))
831
def test_listen_to_the_last_speaker(self):
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'))
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.
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
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'))
855
def test_writes_are_serialized(self):
857
c2 = self.get_existing_config()
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():
867
# The lock is held. We wait for the main thread to decide when to
870
c1._write_config_file = c1_write_config_file
872
c1.set_user_option('one', 'c1')
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)
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
888
c2.set_user_option('one', 'c2')
889
self.assertEquals('c2', c2.get_user_option('one'))
891
def test_read_while_writing(self):
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():
900
# The lock is held. We wait for the main thread to decide when to
905
c1._write_config_file = c1_write_config_file
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)
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
924
# Now we get the updated value
925
c3 = self.get_existing_config()
926
self.assertEquals('c1', c3.get_user_option('one'))
929
class TestGetUserOptionAs(TestIniConfig):
931
def test_get_user_option_as_bool(self):
932
conf, parser = self.make_config_parser("""
935
an_invalid_bool = maybe
936
a_list = hmm, who knows ? # This is interpreted as a list !
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'))
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])
949
self.assertIs(None, get_bool('not_defined_in_this_config'))
950
self.assertEquals([], warnings)
952
def test_get_user_option_as_list(self):
953
conf, parser = self.make_config_parser("""
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'))
966
class TestSupressWarning(TestIniConfig):
968
def make_warnings_config(self, s):
969
conf, parser = self.make_config_parser(s)
970
return conf.suppress_warning
972
def test_suppress_warning_unknown(self):
973
suppress_warning = self.make_warnings_config('')
974
self.assertEqual(False, suppress_warning('unknown_warning'))
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'))
983
class TestGetConfig(tests.TestCase):
985
def test_constructs(self):
986
my_config = config.GlobalConfig()
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()
994
parser = my_config._get_parser()
996
config.ConfigObj = oldparserclass
997
self.assertIsInstance(parser, InstrumentedConfigObj)
998
self.assertEqual(parser._calls, [('__init__', config.config_filename(),
1002
class TestBranchConfig(tests.TestCaseWithTransport):
1004
def test_constructs(self):
1005
branch = FakeBranch()
1006
my_config = config.BranchConfig(branch)
1007
self.assertRaises(TypeError, config.BranchConfig)
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())
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')
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')
1029
def test_has_explicit_nickname(self):
1030
b = self.make_branch('.')
1031
self.assertFalse(b.get_config().has_explicit_nickname())
1033
self.assertTrue(b.get_config().has_explicit_nickname())
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)
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)
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)
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)
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(),
1065
'push_location = http://foobar\n'
1066
'push_location:policy = norecurse\n'
1069
def test_autonick_urlencoded(self):
1070
b = self.make_branch('!repo')
1071
self.assertEqual('!repo', b.get_config().get_nickname())
1073
def test_warn_if_masked(self):
1076
warnings.append(args[0] % args[1:])
1077
self.overrideAttr(trace, 'warning', warning)
1079
def set_option(store, warn_masked=True):
1081
conf.set_user_option('example_option', repr(store), store=store,
1082
warn_masked=warn_masked)
1083
def assertWarning(warning):
1085
self.assertEqual(0, len(warnings))
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)
1093
set_option(config.STORE_BRANCH)
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)
1099
set_option(config.STORE_LOCATION)
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)
1107
class TestGlobalConfigItems(tests.TestCaseInTempDir):
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())
1114
def test_absent_user_id(self):
1115
my_config = config.GlobalConfig()
1116
self.assertEqual(None, my_config._get_user_id())
1118
def test_configured_editor(self):
1119
my_config = config.GlobalConfig.from_string(sample_config_text)
1120
self.assertEqual("vim", my_config.get_editor())
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())
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())
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())
1146
def _get_sample_config(self):
1147
my_config = config.GlobalConfig.from_string(sample_config_text)
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())
1155
def _get_empty_config(self):
1156
my_config = config.GlobalConfig()
1159
def test_gpg_signing_command_unset(self):
1160
my_config = self._get_empty_config()
1161
self.assertEqual("gpg", my_config.gpg_signing_command())
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'))
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'))
1172
def test_post_commit_default(self):
1173
my_config = self._get_sample_config()
1174
self.assertEqual(None, my_config.post_commit())
1176
def test_configured_logformat(self):
1177
my_config = self._get_sample_config()
1178
self.assertEqual("short", my_config.log_format())
1180
def test_get_alias(self):
1181
my_config = self._get_sample_config()
1182
self.assertEqual('help', my_config.get_alias('h'))
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]])
1192
def test_get_no_alias(self):
1193
my_config = self._get_sample_config()
1194
self.assertEqual(None, my_config.get_alias('foo'))
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'))
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))
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)
1212
def test_get_merge_tools(self):
1213
conf = self._get_sample_config()
1214
tools = conf.get_merge_tools()
1215
self.log(repr(tools))
1217
{u'funkytool' : u'funkytool "arg with spaces" {this_temp}',
1218
u'sometool' : u'sometool {base} {this} {other} -o {result}'},
1221
def test_get_merge_tools_empty(self):
1222
conf = self._get_empty_config()
1223
tools = conf.get_merge_tools()
1224
self.assertEqual({}, tools)
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)
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)
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)
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)
1248
class TestGlobalConfigSavingOptions(tests.TestCaseInTempDir):
1250
def test_empty(self):
1251
my_config = config.GlobalConfig()
1252
self.assertEqual(0, len(my_config.get_aliases()))
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'))
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'))
1270
class TestLocationConfig(tests.TestCaseInTempDir, TestOptionsMixin):
1272
def test_constructs(self):
1273
my_config = config.LocationConfig('http://example.com')
1274
self.assertRaises(TypeError, config.LocationConfig)
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.
1280
# replace the class that is constructed, to check its parameters
1281
oldparserclass = config.ConfigObj
1282
config.ConfigObj = InstrumentedConfigObj
1284
my_config = config.LocationConfig('http://www.example.com')
1285
parser = my_config._get_parser()
1287
config.ConfigObj = oldparserclass
1288
self.assertIsInstance(parser, InstrumentedConfigObj)
1289
self.assertEqual(parser._calls,
1290
[('__init__', config.locations_config_filename(),
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())
1299
def assertLocationMatching(self, expected):
1300
self.assertEqual(expected,
1301
list(self.my_location_config._get_matching_sections()))
1303
def test__get_matching_sections_no_match(self):
1304
self.get_branch_config('/')
1305
self.assertLocationMatching([])
1307
def test__get_matching_sections_exact(self):
1308
self.get_branch_config('http://www.example.com')
1309
self.assertLocationMatching([('http://www.example.com', '')])
1311
def test__get_matching_sections_suffix_does_not(self):
1312
self.get_branch_config('http://www.example.com-com')
1313
self.assertLocationMatching([])
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')])
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',
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',
1330
def test__get_matching_sections_subdir_trailing_slash(self):
1331
self.get_branch_config('/b')
1332
self.assertLocationMatching([('/b/', '')])
1334
def test__get_matching_sections_subdir_child(self):
1335
self.get_branch_config('/a/foo')
1336
self.assertLocationMatching([('/a/*', ''), ('/a/', 'foo')])
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')])
1342
def test__get_matching_sections_trailing_slash_with_children(self):
1343
self.get_branch_config('/a/')
1344
self.assertLocationMatching([('/a/', '')])
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
1351
self.get_branch_config('/a/c')
1352
self.assertLocationMatching([('/a/c', ''), ('/a/*', ''), ('/a/', 'c')])
1354
def test__get_option_policy_normal(self):
1355
self.get_branch_config('http://www.example.com')
1357
self.my_location_config._get_config_policy(
1358
'http://www.example.com', 'normal_option'),
1361
def test__get_option_policy_norecurse(self):
1362
self.get_branch_config('http://www.example.com')
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:
1369
self.my_location_config._get_option_policy(
1370
'http://www.example.com/norecurse', 'normal_option'),
1371
config.POLICY_NORECURSE)
1373
def test__get_option_policy_normal(self):
1374
self.get_branch_config('http://www.example.com')
1376
self.my_location_config._get_option_policy(
1377
'http://www.example.com', 'appendpath_option'),
1378
config.POLICY_APPENDPATH)
1380
def test__get_options_with_policy(self):
1381
self.get_branch_config('/dir/subdir',
1382
location_config="""\
1384
other_url = /other-dir
1385
other_url:policy = appendpath
1387
other_url = /other-subdir
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)
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())
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())
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())
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())
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())
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())
1429
def test_signatures_always(self):
1430
self.get_branch_config('/b')
1431
self.assertEqual(config.CHECK_ALWAYS,
1432
self.my_config.signature_checking())
1434
def test_gpg_signing_command(self):
1435
self.get_branch_config('/b')
1436
self.assertEqual("gnome-gpg", self.my_config.gpg_signing_command())
1438
def test_gpg_signing_command_missing(self):
1439
self.get_branch_config('/a')
1440
self.assertEqual("false", self.my_config.gpg_signing_command())
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'))
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'))
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
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'))
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'))
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)
1489
self.my_location_config._get_option_policy(
1490
'http://www.example.com', 'foo'),
1491
config.POLICY_NORECURSE)
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)
1498
self.my_location_config._get_option_policy(
1499
'http://www.example.com', 'foo'),
1500
config.POLICY_APPENDPATH)
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)
1507
self.my_location_config._get_option_policy(
1508
'http://www.example.com', 'norecurse_option'),
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)
1522
self.my_location_config._get_option_policy(
1523
'http://www.example.com/norecurse', 'foo'),
1525
# The previously existing option is still norecurse:
1527
self.my_location_config._get_option_policy(
1528
'http://www.example.com/norecurse', 'normal_option'),
1529
config.POLICY_NORECURSE)
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())
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
1544
my_global_config = config.GlobalConfig.from_string(global_config,
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()
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
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'),
1574
('__getitem__', '/a/c'),
1575
('__contains__', 'foo:policy'),
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')
1584
self.my_config.branch.control_files.files['branch.conf'].strip(),
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')
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())
1602
precedence_global = 'option = global'
1603
precedence_branch = 'option = branch'
1604
precedence_location = """
1608
[http://example.com/specific]
1612
class TestBranchConfigItems(tests.TestCaseInTempDir):
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,
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'] = \
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())
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())
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())
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())
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())
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())
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'))
1683
def test_post_commit_default(self):
1684
my_config = self.get_branch_config(global_config=sample_config_text,
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())
1698
def test_config_precedence(self):
1699
# FIXME: eager test, luckily no persitent config file makes it fail
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')
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)
1724
config.set_user_option('mail_client', 'evolution')
1725
client = config.get_mail_client()
1726
self.assertIsInstance(client, mail_client.Evolution)
1728
config.set_user_option('mail_client', 'kmail')
1729
client = config.get_mail_client()
1730
self.assertIsInstance(client, mail_client.KMail)
1732
config.set_user_option('mail_client', 'mutt')
1733
client = config.get_mail_client()
1734
self.assertIsInstance(client, mail_client.Mutt)
1736
config.set_user_option('mail_client', 'thunderbird')
1737
client = config.get_mail_client()
1738
self.assertIsInstance(client, mail_client.Thunderbird)
1741
config.set_user_option('mail_client', 'default')
1742
client = config.get_mail_client()
1743
self.assertIsInstance(client, mail_client.DefaultMail)
1745
config.set_user_option('mail_client', 'editor')
1746
client = config.get_mail_client()
1747
self.assertIsInstance(client, mail_client.Editor)
1749
config.set_user_option('mail_client', 'mapi')
1750
client = config.get_mail_client()
1751
self.assertIsInstance(client, mail_client.MAPIClient)
1753
config.set_user_option('mail_client', 'xdg-email')
1754
client = config.get_mail_client()
1755
self.assertIsInstance(client, mail_client.XDGEmail)
1757
config.set_user_option('mail_client', 'firebird')
1758
self.assertRaises(errors.UnknownMailClient, config.get_mail_client)
1761
class TestMailAddressExtraction(tests.TestCase):
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')
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'))
1781
class TestTreeConfig(tests.TestCaseWithTransport):
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')
1809
class TestTransportConfig(tests.TestCaseWithTransport):
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('.'),
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',
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')
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())
1849
class TestSection(tests.TestCase):
1851
# FIXME: Parametrize so that all sections produced by Stores run these
1852
# tests -- vila 2011-04-01
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'))
1859
def test_get_unknown_option(self):
1861
section = config.Section(None, a_dict)
1862
self.assertEquals('out of thin air',
1863
section.get('foo', 'out of thin air'))
1865
def test_options_is_shared(self):
1867
section = config.Section(None, a_dict)
1868
self.assertIs(a_dict, section.options)
1871
class TestMutableSection(tests.TestCase):
1873
# FIXME: Parametrize so that all sections (including os.environ and the
1874
# ones produced by Stores) run these tests -- vila 2011-04-01
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'))
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'))
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'))
1909
def test_remove_new_option(self):
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'])
1921
class TestStore(tests.TestCaseWithTransport):
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)
1929
dict([(k, section.get(k)) for k in expected_options.keys()]))
1932
class TestReadonlyStore(TestStore):
1934
scenarios = [(key, {'get_store': builder})
1935
for key, builder in test_store_builder_registry.iteritems()]
1938
super(TestReadonlyStore, self).setUp()
1939
self.branch = self.make_branch('branch')
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())
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()))
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])
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])
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')
1972
class TestMutableStore(TestStore):
1974
scenarios = [(key, {'store_id': key, 'get_store': builder})
1975
for key, builder in test_store_builder_registry.iteritems()]
1978
super(TestMutableStore, self).setUp()
1979
self.transport = self.get_transport()
1980
self.branch = self.make_branch('branch')
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)
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)
1993
self.assertEquals(False, self.has_store(store))
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')
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)
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))
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])
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')
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])
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')
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])
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')
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])
2053
class TestIniFileStore(TestStore):
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)
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())
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('''
2079
foo_in_DEFAULT=foo_DEFAULT
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']}),
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'}}),
2103
class TestLockableIniFileStore(TestStore):
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')
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
2122
class TestSectionMatcher(TestStore):
2124
scenarios = [('location', {'matcher': config.LocationMatcher})]
2126
def get_store(self, file_name):
2127
return config.IniFileStore(self.get_readonly_transport(), file_name)
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()))
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())
2141
class TestLocationSection(tests.TestCase):
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)
2148
def test_simple_option(self):
2149
section = self.get_section({'foo': 'bar'}, '')
2150
self.assertEquals('bar', section.get('foo'))
2152
def test_option_with_extra_path(self):
2153
section = self.get_section({'foo': 'bar', 'foo:policy': 'appendpath'},
2155
self.assertEquals('bar/baz', section.get('foo'))
2157
def test_invalid_policy(self):
2158
section = self.get_section({'foo': 'bar', 'foo:policy': 'die'},
2160
# invalid policies are ignored
2161
self.assertEquals('bar', section.get('foo'))
2164
class TestLocationMatcher(TestStore):
2166
def get_store(self, file_name):
2167
return config.IniFileStore(self.get_readonly_transport(), file_name)
2169
def test_more_specific_sections_first(self):
2170
store = self.get_store('foo.conf')
2171
store._load_from_string('''
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])
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('''
2194
foo:policy = appendpath
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'))
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'
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)
2213
class TestStackGet(tests.TestCase):
2215
# FIXME: This should be parametrized for all known Stack or dedicated
2216
# paramerized tests created to avoid bloating -- vila 2011-03-31
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'))
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'))
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'))
2235
def test_get_for_empty_stack(self):
2236
conf_stack = config.Stack([])
2237
self.assertEquals(None, conf_stack.get('foo'))
2239
def test_get_for_empty_section_callable(self):
2240
conf_stack = config.Stack([lambda : []])
2241
self.assertEquals(None, conf_stack.get('foo'))
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')
2249
class TestStackWithTransport(tests.TestCaseWithTransport):
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')
2258
class TestStackSet(TestStackWithTransport):
2260
scenarios = [(key, {'get_stack': builder})
2261
for key, builder in test_stack_builder_registry.iteritems()]
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'))
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')
2277
class TestStackRemove(TestStackWithTransport):
2279
scenarios = [(key, {'get_stack': builder})
2280
for key, builder in test_stack_builder_registry.iteritems()]
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'))
2287
# Did we get it back ?
2288
self.assertEquals(None, conf.get('foo'))
2290
def test_remove_unknown(self):
2291
conf = self.get_stack(self)
2292
self.assertRaises(KeyError, conf.remove, 'I_do_not_exist')
2295
class TestConcreteStacks(TestStackWithTransport):
2297
scenarios = [(key, {'get_stack': builder})
2298
for key, builder in test_stack_builder_registry.iteritems()]
2300
def test_build_stack(self):
2301
stack = self.get_stack(self)
2304
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
2307
super(TestConfigGetOptions, self).setUp()
2308
create_configs(self)
2310
def test_no_variable(self):
2311
# Using branch should query branch, locations and bazaar
2312
self.assertOptions([], self.branch_config)
2314
def test_option_in_bazaar(self):
2315
self.bazaar_config.set_user_option('file', 'bazaar')
2316
self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
2319
def test_option_in_locations(self):
2320
self.locations_config.set_user_option('file', 'locations')
2322
[('file', 'locations', self.tree.basedir, 'locations')],
2323
self.locations_config)
2325
def test_option_in_branch(self):
2326
self.branch_config.set_user_option('file', 'branch')
2327
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
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'),],
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')
2342
[('file', 'locations', self.tree.basedir, 'locations'),
2343
('file', 'branch', 'DEFAULT', 'branch'),],
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')
2351
[('file', 'locations', self.tree.basedir, 'locations'),
2352
('file', 'branch', 'DEFAULT', 'branch'),
2353
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
2357
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
2360
super(TestConfigRemoveOption, self).setUp()
2361
create_configs_with_file_option(self)
2363
def test_remove_in_locations(self):
2364
self.locations_config.remove_user_option('file', self.tree.basedir)
2366
[('file', 'branch', 'DEFAULT', 'branch'),
2367
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
2370
def test_remove_in_branch(self):
2371
self.branch_config.remove_user_option('file')
2373
[('file', 'locations', self.tree.basedir, 'locations'),
2374
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
2377
def test_remove_in_bazaar(self):
2378
self.bazaar_config.remove_user_option('file')
2380
[('file', 'locations', self.tree.basedir, 'locations'),
2381
('file', 'branch', 'DEFAULT', 'branch'),],
2385
class TestConfigGetSections(tests.TestCaseWithTransport):
2388
super(TestConfigGetSections, self).setUp()
2389
create_configs(self)
2391
def assertSectionNames(self, expected, conf, name=None):
2392
"""Check which sections are returned for a given config.
2394
If fallback configurations exist their sections can be included.
2396
:param expected: A list of section names.
2398
:param conf: The configuration that will be queried.
2400
:param name: An optional section name that will be passed to
2403
sections = list(conf._get_sections(name))
2404
self.assertLength(len(expected), sections)
2405
self.assertEqual(expected, [name for name, _, _ in sections])
2407
def test_bazaar_default_section(self):
2408
self.assertSectionNames(['DEFAULT'], self.bazaar_config)
2410
def test_locations_default_section(self):
2411
# No sections are defined in an empty file
2412
self.assertSectionNames([], self.locations_config)
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)
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'])
2429
parser[parent]['file'] = 'parent'
2431
parser[child]['file'] = 'child'
2432
self.assertSectionNames([self.tree.basedir, parent], loc_config)
2434
def test_branch_data_default_section(self):
2435
self.assertSectionNames([None],
2436
self.branch_config._get_branch_data_config())
2438
def test_branch_default_sections(self):
2439
# No sections are defined in an empty locations file
2440
self.assertSectionNames([None, 'DEFAULT'],
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'],
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')
2455
class TestAuthenticationConfigFile(tests.TestCase):
2456
"""Test the authentication.conf file matching"""
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:
2465
user = credentials['user']
2466
password = credentials['password']
2467
self.assertEquals(expected_user, user)
2468
self.assertEquals(expected_password, password)
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')
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')
2479
def test_auth_section_header_not_closed(self):
2480
conf = config.AuthenticationConfig(_file=StringIO('[DEF'))
2481
self.assertRaises(errors.ParseConfigError, conf._get_config)
2483
def test_auth_value_not_boolean(self):
2484
conf = config.AuthenticationConfig(_file=StringIO(
2488
verify_certificates=askme # Error: Not a boolean
2490
self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
2492
def test_auth_value_not_int(self):
2493
conf = config.AuthenticationConfig(_file=StringIO(
2497
port=port # Error: Not an int
2499
self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
2501
def test_unknown_password_encoding(self):
2502
conf = config.AuthenticationConfig(_file=StringIO(
2506
password_encoding=unknown
2508
self.assertRaises(ValueError, conf.get_password,
2509
'ftp', 'foo.net', 'joe')
2511
def test_credentials_for_scheme_host(self):
2512
conf = config.AuthenticationConfig(_file=StringIO(
2513
"""# Identity on foo.net
2518
password=secret-pass
2521
self._got_user_passwd('joe', 'secret-pass', conf, 'ftp', 'foo.net')
2523
self._got_user_passwd(None, None, conf, 'http', 'foo.net')
2525
self._got_user_passwd(None, None, conf, 'ftp', 'bar.net')
2527
def test_credentials_for_host_port(self):
2528
conf = config.AuthenticationConfig(_file=StringIO(
2529
"""# Identity on foo.net
2535
password=secret-pass
2538
self._got_user_passwd('joe', 'secret-pass',
2539
conf, 'ftp', 'foo.net', port=10021)
2541
self._got_user_passwd(None, None, conf, 'ftp', 'foo.net')
2543
def test_for_matching_host(self):
2544
conf = config.AuthenticationConfig(_file=StringIO(
2545
"""# Identity on foo.net
2551
[sourceforge domain]
2558
self._got_user_passwd('georges', 'bendover',
2559
conf, 'bzr', 'foo.bzr.sf.net')
2561
self._got_user_passwd(None, None,
2562
conf, 'bzr', 'bbzr.sf.net')
2564
def test_for_matching_host_None(self):
2565
conf = config.AuthenticationConfig(_file=StringIO(
2566
"""# Identity on foo.net
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')
2582
def test_credentials_for_path(self):
2583
conf = config.AuthenticationConfig(_file=StringIO(
2599
self._got_user_passwd(None, None,
2600
conf, 'http', host='bar.org', path='/dir3')
2602
self._got_user_passwd('georges', 'bendover',
2603
conf, 'http', host='bar.org', path='/dir2')
2605
self._got_user_passwd('jim', 'jimpass',
2606
conf, 'http', host='bar.org',path='/dir1/subdir')
2608
def test_credentials_for_user(self):
2609
conf = config.AuthenticationConfig(_file=StringIO(
2618
self._got_user_passwd('jim', 'jimpass',
2619
conf, 'http', 'bar.org')
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')
2627
def test_credentials_for_user_without_password(self):
2628
conf = config.AuthenticationConfig(_file=StringIO(
2635
# Get user but no password
2636
self._got_user_passwd('jim', None,
2637
conf, 'http', 'bar.org')
2639
def test_verify_certificates(self):
2640
conf = config.AuthenticationConfig(_file=StringIO(
2647
verify_certificates=False
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'))
2660
class TestAuthenticationStorage(tests.TestCaseInTempDir):
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',
2669
CREDENTIALS = {'name': 'name', 'user': 'user', 'password': 'password',
2670
'verify_certificates': False, 'scheme': 'scheme',
2671
'host': 'host', 'port': 99, 'path': '/foo',
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)
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)
2691
class TestAuthenticationConfig(tests.TestCase):
2692
"""Test AuthenticationConfig behaviour"""
2694
def _check_default_password_prompt(self, expected_prompt_format, scheme,
2695
host=None, port=None, realm=None,
2699
user, password = 'jim', 'precious'
2700
expected_prompt = expected_prompt_format % {
2701
'scheme': scheme, 'host': host, 'port': port,
2702
'user': user, 'realm': realm}
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())
2716
def _check_default_username_prompt(self, expected_prompt_format, scheme,
2717
host=None, port=None, realm=None,
2722
expected_prompt = expected_prompt_format % {
2723
'scheme': scheme, 'host': host, 'port': port,
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())
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('FTP %(host)s username: ', 'ftp')
2739
self._check_default_username_prompt(
2740
'FTP %(host)s:%(port)d username: ', 'ftp', port=10020)
2741
self._check_default_username_prompt(
2742
'SSH %(host)s:%(port)d username: ', 'ssh', port=12345)
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"))
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
'FTP %(user)s@%(host)s password: ', 'ftp')
2755
self._check_default_password_prompt(
2756
'FTP %(user)s@%(host)s:%(port)d password: ', 'ftp', port=10020)
2757
self._check_default_password_prompt(
2758
'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
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('SMTP %(user)s@%(host)s password: ',
2765
self._check_default_password_prompt('SMTP %(user)s@%(host)s password: ',
2766
'smtp', host='bar.org:10025')
2767
self._check_default_password_prompt(
2768
'SMTP %(user)s@%(host)s:%(port)d password: ',
2771
def test_ssh_password_emits_warning(self):
2772
conf = config.AuthenticationConfig(_file=StringIO(
2780
entered_password = 'typed-by-hand'
2781
stdout = tests.StringIOWrapper()
2782
stderr = tests.StringIOWrapper()
2783
ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
2784
stdout=stdout, stderr=stderr)
2786
# Since the password defined in the authentication config is ignored,
2787
# the user is prompted
2788
self.assertEquals(entered_password,
2789
conf.get_password('ssh', 'bar.org', user='jim'))
2790
self.assertContainsRe(
2792
'password ignored in section \[ssh with password\]')
2794
def test_ssh_without_password_doesnt_emit_warning(self):
2795
conf = config.AuthenticationConfig(_file=StringIO(
2802
entered_password = 'typed-by-hand'
2803
stdout = tests.StringIOWrapper()
2804
stderr = tests.StringIOWrapper()
2805
ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
2809
# Since the password defined in the authentication config is ignored,
2810
# the user is prompted
2811
self.assertEquals(entered_password,
2812
conf.get_password('ssh', 'bar.org', user='jim'))
2813
# No warning shoud be emitted since there is no password. We are only
2815
self.assertNotContainsRe(
2817
'password ignored in section \[ssh with password\]')
2819
def test_uses_fallback_stores(self):
2820
self.overrideAttr(config, 'credential_store_registry',
2821
config.CredentialStoreRegistry())
2822
store = StubCredentialStore()
2823
store.add_credentials("http", "example.com", "joe", "secret")
2824
config.credential_store_registry.register("stub", store, fallback=True)
2825
conf = config.AuthenticationConfig(_file=StringIO())
2826
creds = conf.get_credentials("http", "example.com")
2827
self.assertEquals("joe", creds["user"])
2828
self.assertEquals("secret", creds["password"])
2831
class StubCredentialStore(config.CredentialStore):
2837
def add_credentials(self, scheme, host, user, password=None):
2838
self._username[(scheme, host)] = user
2839
self._password[(scheme, host)] = password
2841
def get_credentials(self, scheme, host, port=None, user=None,
2842
path=None, realm=None):
2843
key = (scheme, host)
2844
if not key in self._username:
2846
return { "scheme": scheme, "host": host, "port": port,
2847
"user": self._username[key], "password": self._password[key]}
2850
class CountingCredentialStore(config.CredentialStore):
2855
def get_credentials(self, scheme, host, port=None, user=None,
2856
path=None, realm=None):
2861
class TestCredentialStoreRegistry(tests.TestCase):
2863
def _get_cs_registry(self):
2864
return config.credential_store_registry
2866
def test_default_credential_store(self):
2867
r = self._get_cs_registry()
2868
default = r.get_credential_store(None)
2869
self.assertIsInstance(default, config.PlainTextCredentialStore)
2871
def test_unknown_credential_store(self):
2872
r = self._get_cs_registry()
2873
# It's hard to imagine someone creating a credential store named
2874
# 'unknown' so we use that as an never registered key.
2875
self.assertRaises(KeyError, r.get_credential_store, 'unknown')
2877
def test_fallback_none_registered(self):
2878
r = config.CredentialStoreRegistry()
2879
self.assertEquals(None,
2880
r.get_fallback_credentials("http", "example.com"))
2882
def test_register(self):
2883
r = config.CredentialStoreRegistry()
2884
r.register("stub", StubCredentialStore(), fallback=False)
2885
r.register("another", StubCredentialStore(), fallback=True)
2886
self.assertEquals(["another", "stub"], r.keys())
2888
def test_register_lazy(self):
2889
r = config.CredentialStoreRegistry()
2890
r.register_lazy("stub", "bzrlib.tests.test_config",
2891
"StubCredentialStore", fallback=False)
2892
self.assertEquals(["stub"], r.keys())
2893
self.assertIsInstance(r.get_credential_store("stub"),
2894
StubCredentialStore)
2896
def test_is_fallback(self):
2897
r = config.CredentialStoreRegistry()
2898
r.register("stub1", None, fallback=False)
2899
r.register("stub2", None, fallback=True)
2900
self.assertEquals(False, r.is_fallback("stub1"))
2901
self.assertEquals(True, r.is_fallback("stub2"))
2903
def test_no_fallback(self):
2904
r = config.CredentialStoreRegistry()
2905
store = CountingCredentialStore()
2906
r.register("count", store, fallback=False)
2907
self.assertEquals(None,
2908
r.get_fallback_credentials("http", "example.com"))
2909
self.assertEquals(0, store._calls)
2911
def test_fallback_credentials(self):
2912
r = config.CredentialStoreRegistry()
2913
store = StubCredentialStore()
2914
store.add_credentials("http", "example.com",
2915
"somebody", "geheim")
2916
r.register("stub", store, fallback=True)
2917
creds = r.get_fallback_credentials("http", "example.com")
2918
self.assertEquals("somebody", creds["user"])
2919
self.assertEquals("geheim", creds["password"])
2921
def test_fallback_first_wins(self):
2922
r = config.CredentialStoreRegistry()
2923
stub1 = StubCredentialStore()
2924
stub1.add_credentials("http", "example.com",
2925
"somebody", "stub1")
2926
r.register("stub1", stub1, fallback=True)
2927
stub2 = StubCredentialStore()
2928
stub2.add_credentials("http", "example.com",
2929
"somebody", "stub2")
2930
r.register("stub2", stub1, fallback=True)
2931
creds = r.get_fallback_credentials("http", "example.com")
2932
self.assertEquals("somebody", creds["user"])
2933
self.assertEquals("stub1", creds["password"])
2936
class TestPlainTextCredentialStore(tests.TestCase):
2938
def test_decode_password(self):
2939
r = config.credential_store_registry
2940
plain_text = r.get_credential_store()
2941
decoded = plain_text.decode_password(dict(password='secret'))
2942
self.assertEquals('secret', decoded)
2945
# FIXME: Once we have a way to declare authentication to all test servers, we
2946
# can implement generic tests.
2947
# test_user_password_in_url
2948
# test_user_in_url_password_from_config
2949
# test_user_in_url_password_prompted
2950
# test_user_in_config
2951
# test_user_getpass.getuser
2952
# test_user_prompted ?
2953
class TestAuthenticationRing(tests.TestCaseWithTransport):
2957
class TestAutoUserId(tests.TestCase):
2958
"""Test inferring an automatic user name."""
2960
def test_auto_user_id(self):
2961
"""Automatic inference of user name.
2963
This is a bit hard to test in an isolated way, because it depends on
2964
system functions that go direct to /etc or perhaps somewhere else.
2965
But it's reasonable to say that on Unix, with an /etc/mailname, we ought
2966
to be able to choose a user name with no configuration.
2968
if sys.platform == 'win32':
2969
raise TestSkipped("User name inference not implemented on win32")
2970
realname, address = config._auto_user_id()
2971
if os.path.exists('/etc/mailname'):
2972
self.assertIsNot(None, realname)
2973
self.assertIsNot(None, address)
2975
self.assertEquals((None, None), (realname, address))