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
45
from bzrlib.symbol_versioning import (
49
from bzrlib.transport import remote as transport_remote
50
from bzrlib.tests import (
55
from bzrlib.util.configobj import configobj
58
def lockable_config_scenarios():
61
{'config_class': config.GlobalConfig,
63
'config_section': 'DEFAULT'}),
65
{'config_class': config.LocationConfig,
67
'config_section': '.'}),]
70
load_tests = scenarios.load_tests_apply_scenarios
72
# Register helpers to build stores
73
config.test_store_builder_registry.register(
74
'configobj', lambda test: config.IniFileStore(test.get_transport(),
76
config.test_store_builder_registry.register(
77
'bazaar', lambda test: config.GlobalStore())
78
config.test_store_builder_registry.register(
79
'location', lambda test: config.LocationStore())
82
def build_backing_branch(test, relpath,
83
transport_class=None, server_class=None):
84
"""Test helper to create a backing branch only once.
86
Some tests needs multiple stores/stacks to check concurrent update
87
behaviours. As such, they need to build different branch *objects* even if
88
they share the branch on disk.
90
:param relpath: The relative path to the branch. (Note that the helper
91
should always specify the same relpath).
93
:param transport_class: The Transport class the test needs to use.
95
:param server_class: The server associated with the ``transport_class``
98
Either both or neither of ``transport_class`` and ``server_class`` should
101
if transport_class is not None and server_class is not None:
102
test.transport_class = transport_class
103
test.transport_server = server_class
104
elif not (transport_class is None and server_class is None):
105
raise AssertionError('Specify both ``transport_class`` and '
106
'``server_class`` or neither of them')
107
if getattr(test, 'backing_branch', None) is None:
108
# First call, let's build the branch on disk
109
test.backing_branch = test.make_branch(relpath)
112
def build_branch_store(test):
113
build_backing_branch(test, 'branch')
114
b = branch.Branch.open('branch')
115
return config.BranchStore(b)
116
config.test_store_builder_registry.register('branch', build_branch_store)
119
def build_remote_branch_store(test):
120
# There is only one permutation (but we won't be able to handle more with
121
# this design anyway)
123
server_class) = transport_remote.get_test_permutations()[0]
124
build_backing_branch(test, 'branch', transport_class, server_class)
125
b = branch.Branch.open(test.get_url('branch'))
126
return config.BranchStore(b)
127
config.test_store_builder_registry.register('remote_branch',
128
build_remote_branch_store)
131
config.test_stack_builder_registry.register(
132
'bazaar', lambda test: config.GlobalStack())
133
config.test_stack_builder_registry.register(
134
'location', lambda test: config.LocationStack('.'))
137
def build_branch_stack(test):
138
build_backing_branch(test, 'branch')
139
b = branch.Branch.open('branch')
140
return config.BranchStack(b)
141
config.test_stack_builder_registry.register('branch', build_branch_stack)
144
def build_remote_branch_stack(test):
145
# There is only one permutation (but we won't be able to handle more with
146
# this design anyway)
148
server_class) = transport_remote.get_test_permutations()[0]
149
build_backing_branch(test, 'branch', transport_class, server_class)
150
b = branch.Branch.open(test.get_url('branch'))
151
return config.BranchStack(b)
152
config.test_stack_builder_registry.register('remote_branch',
153
build_remote_branch_stack)
156
sample_long_alias="log -r-15..-1 --line"
157
sample_config_text = u"""
159
email=Erik B\u00e5gfors <erik@bagfors.nu>
161
change_editor=vimdiff -of @new_path @old_path
162
gpg_signing_command=gnome-gpg
163
gpg_signing_key=DD4D5088
165
validate_signatures_in_log=true
167
user_global_option=something
168
bzr.mergetool.sometool=sometool {base} {this} {other} -o {result}
169
bzr.mergetool.funkytool=funkytool "arg with spaces" {this_temp}
170
bzr.default_mergetool=sometool
173
ll=""" + sample_long_alias + "\n"
176
sample_always_signatures = """
178
check_signatures=ignore
179
create_signatures=always
182
sample_ignore_signatures = """
184
check_signatures=require
185
create_signatures=never
188
sample_maybe_signatures = """
190
check_signatures=ignore
191
create_signatures=when-required
194
sample_branches_text = """
195
[http://www.example.com]
197
email=Robert Collins <robertc@example.org>
198
normal_option = normal
199
appendpath_option = append
200
appendpath_option:policy = appendpath
201
norecurse_option = norecurse
202
norecurse_option:policy = norecurse
203
[http://www.example.com/ignoreparent]
204
# different project: ignore parent dir config
206
[http://www.example.com/norecurse]
207
# configuration items that only apply to this dir
209
normal_option = norecurse
210
[http://www.example.com/dir]
211
appendpath_option = normal
213
check_signatures=require
214
# test trailing / matching with no children
216
check_signatures=check-available
217
gpg_signing_command=false
218
gpg_signing_key=default
219
user_local_option=local
220
# test trailing / matching
222
#subdirs will match but not the parent
224
check_signatures=ignore
225
post_commit=bzrlib.tests.test_config.post_commit
226
#testing explicit beats globs
230
def create_configs(test):
231
"""Create configuration files for a given test.
233
This requires creating a tree (and populate the ``test.tree`` attribute)
234
and its associated branch and will populate the following attributes:
236
- branch_config: A BranchConfig for the associated branch.
238
- locations_config : A LocationConfig for the associated branch
240
- bazaar_config: A GlobalConfig.
242
The tree and branch are created in a 'tree' subdirectory so the tests can
243
still use the test directory to stay outside of the branch.
245
tree = test.make_branch_and_tree('tree')
247
test.branch_config = config.BranchConfig(tree.branch)
248
test.locations_config = config.LocationConfig(tree.basedir)
249
test.bazaar_config = config.GlobalConfig()
252
def create_configs_with_file_option(test):
253
"""Create configuration files with a ``file`` option set in each.
255
This builds on ``create_configs`` and add one ``file`` option in each
256
configuration with a value which allows identifying the configuration file.
259
test.bazaar_config.set_user_option('file', 'bazaar')
260
test.locations_config.set_user_option('file', 'locations')
261
test.branch_config.set_user_option('file', 'branch')
264
class TestOptionsMixin:
266
def assertOptions(self, expected, conf):
267
# We don't care about the parser (as it will make tests hard to write
268
# and error-prone anyway)
269
self.assertThat([opt[:4] for opt in conf._get_options()],
270
matchers.Equals(expected))
273
class InstrumentedConfigObj(object):
274
"""A config obj look-enough-alike to record calls made to it."""
276
def __contains__(self, thing):
277
self._calls.append(('__contains__', thing))
280
def __getitem__(self, key):
281
self._calls.append(('__getitem__', key))
284
def __init__(self, input, encoding=None):
285
self._calls = [('__init__', input, encoding)]
287
def __setitem__(self, key, value):
288
self._calls.append(('__setitem__', key, value))
290
def __delitem__(self, key):
291
self._calls.append(('__delitem__', key))
294
self._calls.append(('keys',))
298
self._calls.append(('reload',))
300
def write(self, arg):
301
self._calls.append(('write',))
303
def as_bool(self, value):
304
self._calls.append(('as_bool', value))
307
def get_value(self, section, name):
308
self._calls.append(('get_value', section, name))
312
class FakeBranch(object):
314
def __init__(self, base=None, user_id=None):
316
self.base = "http://example.com/branches/demo"
319
self._transport = self.control_files = \
320
FakeControlFilesAndTransport(user_id=user_id)
322
def _get_config(self):
323
return config.TransportConfig(self._transport, 'branch.conf')
325
def lock_write(self):
332
class FakeControlFilesAndTransport(object):
334
def __init__(self, user_id=None):
337
self.files['email'] = user_id
338
self._transport = self
340
def get_utf8(self, filename):
342
raise AssertionError("get_utf8 should no longer be used")
344
def get(self, filename):
347
return StringIO(self.files[filename])
349
raise errors.NoSuchFile(filename)
351
def get_bytes(self, filename):
354
return self.files[filename]
356
raise errors.NoSuchFile(filename)
358
def put(self, filename, fileobj):
359
self.files[filename] = fileobj.read()
361
def put_file(self, filename, fileobj):
362
return self.put(filename, fileobj)
365
class InstrumentedConfig(config.Config):
366
"""An instrumented config that supplies stubs for template methods."""
369
super(InstrumentedConfig, self).__init__()
371
self._signatures = config.CHECK_NEVER
373
def _get_user_id(self):
374
self._calls.append('_get_user_id')
375
return "Robert Collins <robert.collins@example.org>"
377
def _get_signature_checking(self):
378
self._calls.append('_get_signature_checking')
379
return self._signatures
381
def _get_change_editor(self):
382
self._calls.append('_get_change_editor')
383
return 'vimdiff -fo @new_path @old_path'
386
bool_config = """[DEFAULT]
395
class TestConfigObj(tests.TestCase):
397
def test_get_bool(self):
398
co = config.ConfigObj(StringIO(bool_config))
399
self.assertIs(co.get_bool('DEFAULT', 'active'), True)
400
self.assertIs(co.get_bool('DEFAULT', 'inactive'), False)
401
self.assertIs(co.get_bool('UPPERCASE', 'active'), True)
402
self.assertIs(co.get_bool('UPPERCASE', 'nonactive'), False)
404
def test_hash_sign_in_value(self):
406
Before 4.5.0, ConfigObj did not quote # signs in values, so they'd be
407
treated as comments when read in again. (#86838)
409
co = config.ConfigObj()
410
co['test'] = 'foo#bar'
412
co.write(outfile=outfile)
413
lines = outfile.getvalue().splitlines()
414
self.assertEqual(lines, ['test = "foo#bar"'])
415
co2 = config.ConfigObj(lines)
416
self.assertEqual(co2['test'], 'foo#bar')
418
def test_triple_quotes(self):
419
# Bug #710410: if the value string has triple quotes
420
# then ConfigObj versions up to 4.7.2 will quote them wrong
421
# and won't able to read them back
422
triple_quotes_value = '''spam
423
""" that's my spam """
425
co = config.ConfigObj()
426
co['test'] = triple_quotes_value
427
# While writing this test another bug in ConfigObj has been found:
428
# method co.write() without arguments produces list of lines
429
# one option per line, and multiline values are not split
430
# across multiple lines,
431
# and that breaks the parsing these lines back by ConfigObj.
432
# This issue only affects test, but it's better to avoid
433
# `co.write()` construct at all.
434
# [bialix 20110222] bug report sent to ConfigObj's author
436
co.write(outfile=outfile)
437
output = outfile.getvalue()
438
# now we're trying to read it back
439
co2 = config.ConfigObj(StringIO(output))
440
self.assertEquals(triple_quotes_value, co2['test'])
443
erroneous_config = """[section] # line 1
446
whocares=notme # line 4
450
class TestConfigObjErrors(tests.TestCase):
452
def test_duplicate_section_name_error_line(self):
454
co = configobj.ConfigObj(StringIO(erroneous_config),
456
except config.configobj.DuplicateError, e:
457
self.assertEqual(3, e.line_number)
459
self.fail('Error in config file not detected')
462
class TestConfig(tests.TestCase):
464
def test_constructs(self):
467
def test_no_default_editor(self):
470
self.applyDeprecated, deprecated_in((2, 4, 0)),
471
config.Config().get_editor)
473
def test_user_email(self):
474
my_config = InstrumentedConfig()
475
self.assertEqual('robert.collins@example.org', my_config.user_email())
476
self.assertEqual(['_get_user_id'], my_config._calls)
478
def test_username(self):
479
my_config = InstrumentedConfig()
480
self.assertEqual('Robert Collins <robert.collins@example.org>',
481
my_config.username())
482
self.assertEqual(['_get_user_id'], my_config._calls)
484
def test_signatures_default(self):
485
my_config = config.Config()
486
self.assertFalse(my_config.signature_needed())
487
self.assertEqual(config.CHECK_IF_POSSIBLE,
488
my_config.signature_checking())
489
self.assertEqual(config.SIGN_WHEN_REQUIRED,
490
my_config.signing_policy())
492
def test_signatures_template_method(self):
493
my_config = InstrumentedConfig()
494
self.assertEqual(config.CHECK_NEVER, my_config.signature_checking())
495
self.assertEqual(['_get_signature_checking'], my_config._calls)
497
def test_signatures_template_method_none(self):
498
my_config = InstrumentedConfig()
499
my_config._signatures = None
500
self.assertEqual(config.CHECK_IF_POSSIBLE,
501
my_config.signature_checking())
502
self.assertEqual(['_get_signature_checking'], my_config._calls)
504
def test_gpg_signing_command_default(self):
505
my_config = config.Config()
506
self.assertEqual('gpg', my_config.gpg_signing_command())
508
def test_get_user_option_default(self):
509
my_config = config.Config()
510
self.assertEqual(None, my_config.get_user_option('no_option'))
512
def test_post_commit_default(self):
513
my_config = config.Config()
514
self.assertEqual(None, my_config.post_commit())
516
def test_log_format_default(self):
517
my_config = config.Config()
518
self.assertEqual('long', my_config.log_format())
520
def test_acceptable_keys_default(self):
521
my_config = config.Config()
522
self.assertEqual(None, my_config.acceptable_keys())
524
def test_validate_signatures_in_log_default(self):
525
my_config = config.Config()
526
self.assertEqual(False, my_config.validate_signatures_in_log())
528
def test_get_change_editor(self):
529
my_config = InstrumentedConfig()
530
change_editor = my_config.get_change_editor('old_tree', 'new_tree')
531
self.assertEqual(['_get_change_editor'], my_config._calls)
532
self.assertIs(diff.DiffFromTool, change_editor.__class__)
533
self.assertEqual(['vimdiff', '-fo', '@new_path', '@old_path'],
534
change_editor.command_template)
537
class TestConfigPath(tests.TestCase):
540
super(TestConfigPath, self).setUp()
541
self.overrideEnv('HOME', '/home/bogus')
542
self.overrideEnv('XDG_CACHE_DIR', '')
543
if sys.platform == 'win32':
545
'BZR_HOME', r'C:\Documents and Settings\bogus\Application Data')
547
'C:/Documents and Settings/bogus/Application Data/bazaar/2.0'
549
self.bzr_home = '/home/bogus/.bazaar'
551
def test_config_dir(self):
552
self.assertEqual(config.config_dir(), self.bzr_home)
554
def test_config_filename(self):
555
self.assertEqual(config.config_filename(),
556
self.bzr_home + '/bazaar.conf')
558
def test_locations_config_filename(self):
559
self.assertEqual(config.locations_config_filename(),
560
self.bzr_home + '/locations.conf')
562
def test_authentication_config_filename(self):
563
self.assertEqual(config.authentication_config_filename(),
564
self.bzr_home + '/authentication.conf')
566
def test_xdg_cache_dir(self):
567
self.assertEqual(config.xdg_cache_dir(),
568
'/home/bogus/.cache')
571
class TestXDGConfigDir(tests.TestCaseInTempDir):
572
# must be in temp dir because config tests for the existence of the bazaar
573
# subdirectory of $XDG_CONFIG_HOME
576
if sys.platform in ('darwin', 'win32'):
577
raise tests.TestNotApplicable(
578
'XDG config dir not used on this platform')
579
super(TestXDGConfigDir, self).setUp()
580
self.overrideEnv('HOME', self.test_home_dir)
581
# BZR_HOME overrides everything we want to test so unset it.
582
self.overrideEnv('BZR_HOME', None)
584
def test_xdg_config_dir_exists(self):
585
"""When ~/.config/bazaar exists, use it as the config dir."""
586
newdir = osutils.pathjoin(self.test_home_dir, '.config', 'bazaar')
588
self.assertEqual(config.config_dir(), newdir)
590
def test_xdg_config_home(self):
591
"""When XDG_CONFIG_HOME is set, use it."""
592
xdgconfigdir = osutils.pathjoin(self.test_home_dir, 'xdgconfig')
593
self.overrideEnv('XDG_CONFIG_HOME', xdgconfigdir)
594
newdir = osutils.pathjoin(xdgconfigdir, 'bazaar')
596
self.assertEqual(config.config_dir(), newdir)
599
class TestIniConfig(tests.TestCaseInTempDir):
601
def make_config_parser(self, s):
602
conf = config.IniBasedConfig.from_string(s)
603
return conf, conf._get_parser()
606
class TestIniConfigBuilding(TestIniConfig):
608
def test_contructs(self):
609
my_config = config.IniBasedConfig()
611
def test_from_fp(self):
612
my_config = config.IniBasedConfig.from_string(sample_config_text)
613
self.assertIsInstance(my_config._get_parser(), configobj.ConfigObj)
615
def test_cached(self):
616
my_config = config.IniBasedConfig.from_string(sample_config_text)
617
parser = my_config._get_parser()
618
self.assertTrue(my_config._get_parser() is parser)
620
def _dummy_chown(self, path, uid, gid):
621
self.path, self.uid, self.gid = path, uid, gid
623
def test_ini_config_ownership(self):
624
"""Ensure that chown is happening during _write_config_file"""
625
self.requireFeature(features.chown_feature)
626
self.overrideAttr(os, 'chown', self._dummy_chown)
627
self.path = self.uid = self.gid = None
628
conf = config.IniBasedConfig(file_name='./foo.conf')
629
conf._write_config_file()
630
self.assertEquals(self.path, './foo.conf')
631
self.assertTrue(isinstance(self.uid, int))
632
self.assertTrue(isinstance(self.gid, int))
634
def test_get_filename_parameter_is_deprecated_(self):
635
conf = self.callDeprecated([
636
'IniBasedConfig.__init__(get_filename) was deprecated in 2.3.'
637
' Use file_name instead.'],
638
config.IniBasedConfig, lambda: 'ini.conf')
639
self.assertEqual('ini.conf', conf.file_name)
641
def test_get_parser_file_parameter_is_deprecated_(self):
642
config_file = StringIO(sample_config_text.encode('utf-8'))
643
conf = config.IniBasedConfig.from_string(sample_config_text)
644
conf = self.callDeprecated([
645
'IniBasedConfig._get_parser(file=xxx) was deprecated in 2.3.'
646
' Use IniBasedConfig(_content=xxx) instead.'],
647
conf._get_parser, file=config_file)
650
class TestIniConfigSaving(tests.TestCaseInTempDir):
652
def test_cant_save_without_a_file_name(self):
653
conf = config.IniBasedConfig()
654
self.assertRaises(AssertionError, conf._write_config_file)
656
def test_saved_with_content(self):
657
content = 'foo = bar\n'
658
conf = config.IniBasedConfig.from_string(
659
content, file_name='./test.conf', save=True)
660
self.assertFileEqual(content, 'test.conf')
663
class TestIniConfigOptionExpansionDefaultValue(tests.TestCaseInTempDir):
664
"""What is the default value of expand for config options.
666
This is an opt-in beta feature used to evaluate whether or not option
667
references can appear in dangerous place raising exceptions, disapearing
668
(and as such corrupting data) or if it's safe to activate the option by
671
Note that these tests relies on config._expand_default_value being already
672
overwritten in the parent class setUp.
676
super(TestIniConfigOptionExpansionDefaultValue, self).setUp()
680
self.warnings.append(args[0] % args[1:])
681
self.overrideAttr(trace, 'warning', warning)
683
def get_config(self, expand):
684
c = config.GlobalConfig.from_string('bzr.config.expand=%s' % (expand,),
688
def assertExpandIs(self, expected):
689
actual = config._get_expand_default_value()
690
#self.config.get_user_option_as_bool('bzr.config.expand')
691
self.assertEquals(expected, actual)
693
def test_default_is_None(self):
694
self.assertEquals(None, config._expand_default_value)
696
def test_default_is_False_even_if_None(self):
697
self.config = self.get_config(None)
698
self.assertExpandIs(False)
700
def test_default_is_False_even_if_invalid(self):
701
self.config = self.get_config('<your choice>')
702
self.assertExpandIs(False)
704
# Huh ? My choice is False ? Thanks, always happy to hear that :D
705
# Wait, you've been warned !
706
self.assertLength(1, self.warnings)
708
'Value "<your choice>" is not a boolean for "bzr.config.expand"',
711
def test_default_is_True(self):
712
self.config = self.get_config(True)
713
self.assertExpandIs(True)
715
def test_default_is_False(self):
716
self.config = self.get_config(False)
717
self.assertExpandIs(False)
720
class TestIniConfigOptionExpansion(tests.TestCase):
721
"""Test option expansion from the IniConfig level.
723
What we really want here is to test the Config level, but the class being
724
abstract as far as storing values is concerned, this can't be done
727
# FIXME: This should be rewritten when all configs share a storage
728
# implementation -- vila 2011-02-18
730
def get_config(self, string=None):
733
c = config.IniBasedConfig.from_string(string)
736
def assertExpansion(self, expected, conf, string, env=None):
737
self.assertEquals(expected, conf.expand_options(string, env))
739
def test_no_expansion(self):
740
c = self.get_config('')
741
self.assertExpansion('foo', c, 'foo')
743
def test_env_adding_options(self):
744
c = self.get_config('')
745
self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
747
def test_env_overriding_options(self):
748
c = self.get_config('foo=baz')
749
self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
751
def test_simple_ref(self):
752
c = self.get_config('foo=xxx')
753
self.assertExpansion('xxx', c, '{foo}')
755
def test_unknown_ref(self):
756
c = self.get_config('')
757
self.assertRaises(errors.ExpandingUnknownOption,
758
c.expand_options, '{foo}')
760
def test_indirect_ref(self):
761
c = self.get_config('''
765
self.assertExpansion('xxx', c, '{bar}')
767
def test_embedded_ref(self):
768
c = self.get_config('''
772
self.assertExpansion('xxx', c, '{{bar}}')
774
def test_simple_loop(self):
775
c = self.get_config('foo={foo}')
776
self.assertRaises(errors.OptionExpansionLoop, c.expand_options, '{foo}')
778
def test_indirect_loop(self):
779
c = self.get_config('''
783
e = self.assertRaises(errors.OptionExpansionLoop,
784
c.expand_options, '{foo}')
785
self.assertEquals('foo->bar->baz', e.refs)
786
self.assertEquals('{foo}', e.string)
789
conf = self.get_config('''
793
list={foo},{bar},{baz}
795
self.assertEquals(['start', 'middle', 'end'],
796
conf.get_user_option('list', expand=True))
798
def test_cascading_list(self):
799
conf = self.get_config('''
805
self.assertEquals(['start', 'middle', 'end'],
806
conf.get_user_option('list', expand=True))
808
def test_pathological_hidden_list(self):
809
conf = self.get_config('''
815
hidden={start}{middle}{end}
817
# Nope, it's either a string or a list, and the list wins as soon as a
818
# ',' appears, so the string concatenation never occur.
819
self.assertEquals(['{foo', '}', '{', 'bar}'],
820
conf.get_user_option('hidden', expand=True))
822
class TestLocationConfigOptionExpansion(tests.TestCaseInTempDir):
824
def get_config(self, location, string=None):
827
# Since we don't save the config we won't strictly require to inherit
828
# from TestCaseInTempDir, but an error occurs so quickly...
829
c = config.LocationConfig.from_string(string, location)
832
def test_dont_cross_unrelated_section(self):
833
c = self.get_config('/another/branch/path','''
838
[/another/branch/path]
841
self.assertRaises(errors.ExpandingUnknownOption,
842
c.get_user_option, 'bar', expand=True)
844
def test_cross_related_sections(self):
845
c = self.get_config('/project/branch/path','''
849
[/project/branch/path]
852
self.assertEquals('quux', c.get_user_option('bar', expand=True))
855
class TestIniBaseConfigOnDisk(tests.TestCaseInTempDir):
857
def test_cannot_reload_without_name(self):
858
conf = config.IniBasedConfig.from_string(sample_config_text)
859
self.assertRaises(AssertionError, conf.reload)
861
def test_reload_see_new_value(self):
862
c1 = config.IniBasedConfig.from_string('editor=vim\n',
863
file_name='./test/conf')
864
c1._write_config_file()
865
c2 = config.IniBasedConfig.from_string('editor=emacs\n',
866
file_name='./test/conf')
867
c2._write_config_file()
868
self.assertEqual('vim', c1.get_user_option('editor'))
869
self.assertEqual('emacs', c2.get_user_option('editor'))
870
# Make sure we get the Right value
872
self.assertEqual('emacs', c1.get_user_option('editor'))
875
class TestLockableConfig(tests.TestCaseInTempDir):
877
scenarios = lockable_config_scenarios()
882
config_section = None
885
super(TestLockableConfig, self).setUp()
886
self._content = '[%s]\none=1\ntwo=2\n' % (self.config_section,)
887
self.config = self.create_config(self._content)
889
def get_existing_config(self):
890
return self.config_class(*self.config_args)
892
def create_config(self, content):
893
kwargs = dict(save=True)
894
c = self.config_class.from_string(content, *self.config_args, **kwargs)
897
def test_simple_read_access(self):
898
self.assertEquals('1', self.config.get_user_option('one'))
900
def test_simple_write_access(self):
901
self.config.set_user_option('one', 'one')
902
self.assertEquals('one', self.config.get_user_option('one'))
904
def test_listen_to_the_last_speaker(self):
906
c2 = self.get_existing_config()
907
c1.set_user_option('one', 'ONE')
908
c2.set_user_option('two', 'TWO')
909
self.assertEquals('ONE', c1.get_user_option('one'))
910
self.assertEquals('TWO', c2.get_user_option('two'))
911
# The second update respect the first one
912
self.assertEquals('ONE', c2.get_user_option('one'))
914
def test_last_speaker_wins(self):
915
# If the same config is not shared, the same variable modified twice
916
# can only see a single result.
918
c2 = self.get_existing_config()
919
c1.set_user_option('one', 'c1')
920
c2.set_user_option('one', 'c2')
921
self.assertEquals('c2', c2._get_user_option('one'))
922
# The first modification is still available until another refresh
924
self.assertEquals('c1', c1._get_user_option('one'))
925
c1.set_user_option('two', 'done')
926
self.assertEquals('c2', c1._get_user_option('one'))
928
def test_writes_are_serialized(self):
930
c2 = self.get_existing_config()
932
# We spawn a thread that will pause *during* the write
933
before_writing = threading.Event()
934
after_writing = threading.Event()
935
writing_done = threading.Event()
936
c1_orig = c1._write_config_file
937
def c1_write_config_file():
940
# The lock is held. We wait for the main thread to decide when to
943
c1._write_config_file = c1_write_config_file
945
c1.set_user_option('one', 'c1')
947
t1 = threading.Thread(target=c1_set_option)
948
# Collect the thread after the test
949
self.addCleanup(t1.join)
950
# Be ready to unblock the thread if the test goes wrong
951
self.addCleanup(after_writing.set)
953
before_writing.wait()
954
self.assertTrue(c1._lock.is_held)
955
self.assertRaises(errors.LockContention,
956
c2.set_user_option, 'one', 'c2')
957
self.assertEquals('c1', c1.get_user_option('one'))
958
# Let the lock be released
961
c2.set_user_option('one', 'c2')
962
self.assertEquals('c2', c2.get_user_option('one'))
964
def test_read_while_writing(self):
966
# We spawn a thread that will pause *during* the write
967
ready_to_write = threading.Event()
968
do_writing = threading.Event()
969
writing_done = threading.Event()
970
c1_orig = c1._write_config_file
971
def c1_write_config_file():
973
# The lock is held. We wait for the main thread to decide when to
978
c1._write_config_file = c1_write_config_file
980
c1.set_user_option('one', 'c1')
981
t1 = threading.Thread(target=c1_set_option)
982
# Collect the thread after the test
983
self.addCleanup(t1.join)
984
# Be ready to unblock the thread if the test goes wrong
985
self.addCleanup(do_writing.set)
987
# Ensure the thread is ready to write
988
ready_to_write.wait()
989
self.assertTrue(c1._lock.is_held)
990
self.assertEquals('c1', c1.get_user_option('one'))
991
# If we read during the write, we get the old value
992
c2 = self.get_existing_config()
993
self.assertEquals('1', c2.get_user_option('one'))
994
# Let the writing occur and ensure it occurred
997
# Now we get the updated value
998
c3 = self.get_existing_config()
999
self.assertEquals('c1', c3.get_user_option('one'))
1002
class TestGetUserOptionAs(TestIniConfig):
1004
def test_get_user_option_as_bool(self):
1005
conf, parser = self.make_config_parser("""
1008
an_invalid_bool = maybe
1009
a_list = hmm, who knows ? # This is interpreted as a list !
1011
get_bool = conf.get_user_option_as_bool
1012
self.assertEqual(True, get_bool('a_true_bool'))
1013
self.assertEqual(False, get_bool('a_false_bool'))
1016
warnings.append(args[0] % args[1:])
1017
self.overrideAttr(trace, 'warning', warning)
1018
msg = 'Value "%s" is not a boolean for "%s"'
1019
self.assertIs(None, get_bool('an_invalid_bool'))
1020
self.assertEquals(msg % ('maybe', 'an_invalid_bool'), warnings[0])
1022
self.assertIs(None, get_bool('not_defined_in_this_config'))
1023
self.assertEquals([], warnings)
1025
def test_get_user_option_as_list(self):
1026
conf, parser = self.make_config_parser("""
1031
get_list = conf.get_user_option_as_list
1032
self.assertEqual(['a', 'b', 'c'], get_list('a_list'))
1033
self.assertEqual(['1'], get_list('length_1'))
1034
self.assertEqual('x', conf.get_user_option('one_item'))
1035
# automatically cast to list
1036
self.assertEqual(['x'], get_list('one_item'))
1039
class TestSupressWarning(TestIniConfig):
1041
def make_warnings_config(self, s):
1042
conf, parser = self.make_config_parser(s)
1043
return conf.suppress_warning
1045
def test_suppress_warning_unknown(self):
1046
suppress_warning = self.make_warnings_config('')
1047
self.assertEqual(False, suppress_warning('unknown_warning'))
1049
def test_suppress_warning_known(self):
1050
suppress_warning = self.make_warnings_config('suppress_warnings=a,b')
1051
self.assertEqual(False, suppress_warning('c'))
1052
self.assertEqual(True, suppress_warning('a'))
1053
self.assertEqual(True, suppress_warning('b'))
1056
class TestGetConfig(tests.TestCase):
1058
def test_constructs(self):
1059
my_config = config.GlobalConfig()
1061
def test_calls_read_filenames(self):
1062
# replace the class that is constructed, to check its parameters
1063
oldparserclass = config.ConfigObj
1064
config.ConfigObj = InstrumentedConfigObj
1065
my_config = config.GlobalConfig()
1067
parser = my_config._get_parser()
1069
config.ConfigObj = oldparserclass
1070
self.assertIsInstance(parser, InstrumentedConfigObj)
1071
self.assertEqual(parser._calls, [('__init__', config.config_filename(),
1075
class TestBranchConfig(tests.TestCaseWithTransport):
1077
def test_constructs(self):
1078
branch = FakeBranch()
1079
my_config = config.BranchConfig(branch)
1080
self.assertRaises(TypeError, config.BranchConfig)
1082
def test_get_location_config(self):
1083
branch = FakeBranch()
1084
my_config = config.BranchConfig(branch)
1085
location_config = my_config._get_location_config()
1086
self.assertEqual(branch.base, location_config.location)
1087
self.assertIs(location_config, my_config._get_location_config())
1089
def test_get_config(self):
1090
"""The Branch.get_config method works properly"""
1091
b = bzrdir.BzrDir.create_standalone_workingtree('.').branch
1092
my_config = b.get_config()
1093
self.assertIs(my_config.get_user_option('wacky'), None)
1094
my_config.set_user_option('wacky', 'unlikely')
1095
self.assertEqual(my_config.get_user_option('wacky'), 'unlikely')
1097
# Ensure we get the same thing if we start again
1098
b2 = branch.Branch.open('.')
1099
my_config2 = b2.get_config()
1100
self.assertEqual(my_config2.get_user_option('wacky'), 'unlikely')
1102
def test_has_explicit_nickname(self):
1103
b = self.make_branch('.')
1104
self.assertFalse(b.get_config().has_explicit_nickname())
1106
self.assertTrue(b.get_config().has_explicit_nickname())
1108
def test_config_url(self):
1109
"""The Branch.get_config will use section that uses a local url"""
1110
branch = self.make_branch('branch')
1111
self.assertEqual('branch', branch.nick)
1113
local_url = urlutils.local_path_to_url('branch')
1114
conf = config.LocationConfig.from_string(
1115
'[%s]\nnickname = foobar' % (local_url,),
1116
local_url, save=True)
1117
self.assertEqual('foobar', branch.nick)
1119
def test_config_local_path(self):
1120
"""The Branch.get_config will use a local system path"""
1121
branch = self.make_branch('branch')
1122
self.assertEqual('branch', branch.nick)
1124
local_path = osutils.getcwd().encode('utf8')
1125
conf = config.LocationConfig.from_string(
1126
'[%s/branch]\nnickname = barry' % (local_path,),
1127
'branch', save=True)
1128
self.assertEqual('barry', branch.nick)
1130
def test_config_creates_local(self):
1131
"""Creating a new entry in config uses a local path."""
1132
branch = self.make_branch('branch', format='knit')
1133
branch.set_push_location('http://foobar')
1134
local_path = osutils.getcwd().encode('utf8')
1135
# Surprisingly ConfigObj doesn't create a trailing newline
1136
self.check_file_contents(config.locations_config_filename(),
1138
'push_location = http://foobar\n'
1139
'push_location:policy = norecurse\n'
1142
def test_autonick_urlencoded(self):
1143
b = self.make_branch('!repo')
1144
self.assertEqual('!repo', b.get_config().get_nickname())
1146
def test_warn_if_masked(self):
1149
warnings.append(args[0] % args[1:])
1150
self.overrideAttr(trace, 'warning', warning)
1152
def set_option(store, warn_masked=True):
1154
conf.set_user_option('example_option', repr(store), store=store,
1155
warn_masked=warn_masked)
1156
def assertWarning(warning):
1158
self.assertEqual(0, len(warnings))
1160
self.assertEqual(1, len(warnings))
1161
self.assertEqual(warning, warnings[0])
1162
branch = self.make_branch('.')
1163
conf = branch.get_config()
1164
set_option(config.STORE_GLOBAL)
1166
set_option(config.STORE_BRANCH)
1168
set_option(config.STORE_GLOBAL)
1169
assertWarning('Value "4" is masked by "3" from branch.conf')
1170
set_option(config.STORE_GLOBAL, warn_masked=False)
1172
set_option(config.STORE_LOCATION)
1174
set_option(config.STORE_BRANCH)
1175
assertWarning('Value "3" is masked by "0" from locations.conf')
1176
set_option(config.STORE_BRANCH, warn_masked=False)
1180
class TestGlobalConfigItems(tests.TestCaseInTempDir):
1182
def test_user_id(self):
1183
my_config = config.GlobalConfig.from_string(sample_config_text)
1184
self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
1185
my_config._get_user_id())
1187
def test_absent_user_id(self):
1188
my_config = config.GlobalConfig()
1189
self.assertEqual(None, my_config._get_user_id())
1191
def test_configured_editor(self):
1192
my_config = config.GlobalConfig.from_string(sample_config_text)
1193
editor = self.applyDeprecated(
1194
deprecated_in((2, 4, 0)), my_config.get_editor)
1195
self.assertEqual('vim', editor)
1197
def test_signatures_always(self):
1198
my_config = config.GlobalConfig.from_string(sample_always_signatures)
1199
self.assertEqual(config.CHECK_NEVER,
1200
my_config.signature_checking())
1201
self.assertEqual(config.SIGN_ALWAYS,
1202
my_config.signing_policy())
1203
self.assertEqual(True, my_config.signature_needed())
1205
def test_signatures_if_possible(self):
1206
my_config = config.GlobalConfig.from_string(sample_maybe_signatures)
1207
self.assertEqual(config.CHECK_NEVER,
1208
my_config.signature_checking())
1209
self.assertEqual(config.SIGN_WHEN_REQUIRED,
1210
my_config.signing_policy())
1211
self.assertEqual(False, my_config.signature_needed())
1213
def test_signatures_ignore(self):
1214
my_config = config.GlobalConfig.from_string(sample_ignore_signatures)
1215
self.assertEqual(config.CHECK_ALWAYS,
1216
my_config.signature_checking())
1217
self.assertEqual(config.SIGN_NEVER,
1218
my_config.signing_policy())
1219
self.assertEqual(False, my_config.signature_needed())
1221
def _get_sample_config(self):
1222
my_config = config.GlobalConfig.from_string(sample_config_text)
1225
def test_gpg_signing_command(self):
1226
my_config = self._get_sample_config()
1227
self.assertEqual("gnome-gpg", my_config.gpg_signing_command())
1228
self.assertEqual(False, my_config.signature_needed())
1230
def test_gpg_signing_key(self):
1231
my_config = self._get_sample_config()
1232
self.assertEqual("DD4D5088", my_config.gpg_signing_key())
1234
def _get_empty_config(self):
1235
my_config = config.GlobalConfig()
1238
def test_gpg_signing_command_unset(self):
1239
my_config = self._get_empty_config()
1240
self.assertEqual("gpg", my_config.gpg_signing_command())
1242
def test_get_user_option_default(self):
1243
my_config = self._get_empty_config()
1244
self.assertEqual(None, my_config.get_user_option('no_option'))
1246
def test_get_user_option_global(self):
1247
my_config = self._get_sample_config()
1248
self.assertEqual("something",
1249
my_config.get_user_option('user_global_option'))
1251
def test_post_commit_default(self):
1252
my_config = self._get_sample_config()
1253
self.assertEqual(None, my_config.post_commit())
1255
def test_configured_logformat(self):
1256
my_config = self._get_sample_config()
1257
self.assertEqual("short", my_config.log_format())
1259
def test_configured_acceptable_keys(self):
1260
my_config = self._get_sample_config()
1261
self.assertEqual("amy", my_config.acceptable_keys())
1263
def test_configured_validate_signatures_in_log(self):
1264
my_config = self._get_sample_config()
1265
self.assertEqual(True, my_config.validate_signatures_in_log())
1267
def test_get_alias(self):
1268
my_config = self._get_sample_config()
1269
self.assertEqual('help', my_config.get_alias('h'))
1271
def test_get_aliases(self):
1272
my_config = self._get_sample_config()
1273
aliases = my_config.get_aliases()
1274
self.assertEqual(2, len(aliases))
1275
sorted_keys = sorted(aliases)
1276
self.assertEqual('help', aliases[sorted_keys[0]])
1277
self.assertEqual(sample_long_alias, aliases[sorted_keys[1]])
1279
def test_get_no_alias(self):
1280
my_config = self._get_sample_config()
1281
self.assertEqual(None, my_config.get_alias('foo'))
1283
def test_get_long_alias(self):
1284
my_config = self._get_sample_config()
1285
self.assertEqual(sample_long_alias, my_config.get_alias('ll'))
1287
def test_get_change_editor(self):
1288
my_config = self._get_sample_config()
1289
change_editor = my_config.get_change_editor('old', 'new')
1290
self.assertIs(diff.DiffFromTool, change_editor.__class__)
1291
self.assertEqual('vimdiff -of @new_path @old_path',
1292
' '.join(change_editor.command_template))
1294
def test_get_no_change_editor(self):
1295
my_config = self._get_empty_config()
1296
change_editor = my_config.get_change_editor('old', 'new')
1297
self.assertIs(None, change_editor)
1299
def test_get_merge_tools(self):
1300
conf = self._get_sample_config()
1301
tools = conf.get_merge_tools()
1302
self.log(repr(tools))
1304
{u'funkytool' : u'funkytool "arg with spaces" {this_temp}',
1305
u'sometool' : u'sometool {base} {this} {other} -o {result}'},
1308
def test_get_merge_tools_empty(self):
1309
conf = self._get_empty_config()
1310
tools = conf.get_merge_tools()
1311
self.assertEqual({}, tools)
1313
def test_find_merge_tool(self):
1314
conf = self._get_sample_config()
1315
cmdline = conf.find_merge_tool('sometool')
1316
self.assertEqual('sometool {base} {this} {other} -o {result}', cmdline)
1318
def test_find_merge_tool_not_found(self):
1319
conf = self._get_sample_config()
1320
cmdline = conf.find_merge_tool('DOES NOT EXIST')
1321
self.assertIs(cmdline, None)
1323
def test_find_merge_tool_known(self):
1324
conf = self._get_empty_config()
1325
cmdline = conf.find_merge_tool('kdiff3')
1326
self.assertEquals('kdiff3 {base} {this} {other} -o {result}', cmdline)
1328
def test_find_merge_tool_override_known(self):
1329
conf = self._get_empty_config()
1330
conf.set_user_option('bzr.mergetool.kdiff3', 'kdiff3 blah')
1331
cmdline = conf.find_merge_tool('kdiff3')
1332
self.assertEqual('kdiff3 blah', cmdline)
1335
class TestGlobalConfigSavingOptions(tests.TestCaseInTempDir):
1337
def test_empty(self):
1338
my_config = config.GlobalConfig()
1339
self.assertEqual(0, len(my_config.get_aliases()))
1341
def test_set_alias(self):
1342
my_config = config.GlobalConfig()
1343
alias_value = 'commit --strict'
1344
my_config.set_alias('commit', alias_value)
1345
new_config = config.GlobalConfig()
1346
self.assertEqual(alias_value, new_config.get_alias('commit'))
1348
def test_remove_alias(self):
1349
my_config = config.GlobalConfig()
1350
my_config.set_alias('commit', 'commit --strict')
1351
# Now remove the alias again.
1352
my_config.unset_alias('commit')
1353
new_config = config.GlobalConfig()
1354
self.assertIs(None, new_config.get_alias('commit'))
1357
class TestLocationConfig(tests.TestCaseInTempDir, TestOptionsMixin):
1359
def test_constructs(self):
1360
my_config = config.LocationConfig('http://example.com')
1361
self.assertRaises(TypeError, config.LocationConfig)
1363
def test_branch_calls_read_filenames(self):
1364
# This is testing the correct file names are provided.
1365
# TODO: consolidate with the test for GlobalConfigs filename checks.
1367
# replace the class that is constructed, to check its parameters
1368
oldparserclass = config.ConfigObj
1369
config.ConfigObj = InstrumentedConfigObj
1371
my_config = config.LocationConfig('http://www.example.com')
1372
parser = my_config._get_parser()
1374
config.ConfigObj = oldparserclass
1375
self.assertIsInstance(parser, InstrumentedConfigObj)
1376
self.assertEqual(parser._calls,
1377
[('__init__', config.locations_config_filename(),
1380
def test_get_global_config(self):
1381
my_config = config.BranchConfig(FakeBranch('http://example.com'))
1382
global_config = my_config._get_global_config()
1383
self.assertIsInstance(global_config, config.GlobalConfig)
1384
self.assertIs(global_config, my_config._get_global_config())
1386
def assertLocationMatching(self, expected):
1387
self.assertEqual(expected,
1388
list(self.my_location_config._get_matching_sections()))
1390
def test__get_matching_sections_no_match(self):
1391
self.get_branch_config('/')
1392
self.assertLocationMatching([])
1394
def test__get_matching_sections_exact(self):
1395
self.get_branch_config('http://www.example.com')
1396
self.assertLocationMatching([('http://www.example.com', '')])
1398
def test__get_matching_sections_suffix_does_not(self):
1399
self.get_branch_config('http://www.example.com-com')
1400
self.assertLocationMatching([])
1402
def test__get_matching_sections_subdir_recursive(self):
1403
self.get_branch_config('http://www.example.com/com')
1404
self.assertLocationMatching([('http://www.example.com', 'com')])
1406
def test__get_matching_sections_ignoreparent(self):
1407
self.get_branch_config('http://www.example.com/ignoreparent')
1408
self.assertLocationMatching([('http://www.example.com/ignoreparent',
1411
def test__get_matching_sections_ignoreparent_subdir(self):
1412
self.get_branch_config(
1413
'http://www.example.com/ignoreparent/childbranch')
1414
self.assertLocationMatching([('http://www.example.com/ignoreparent',
1417
def test__get_matching_sections_subdir_trailing_slash(self):
1418
self.get_branch_config('/b')
1419
self.assertLocationMatching([('/b/', '')])
1421
def test__get_matching_sections_subdir_child(self):
1422
self.get_branch_config('/a/foo')
1423
self.assertLocationMatching([('/a/*', ''), ('/a/', 'foo')])
1425
def test__get_matching_sections_subdir_child_child(self):
1426
self.get_branch_config('/a/foo/bar')
1427
self.assertLocationMatching([('/a/*', 'bar'), ('/a/', 'foo/bar')])
1429
def test__get_matching_sections_trailing_slash_with_children(self):
1430
self.get_branch_config('/a/')
1431
self.assertLocationMatching([('/a/', '')])
1433
def test__get_matching_sections_explicit_over_glob(self):
1434
# XXX: 2006-09-08 jamesh
1435
# This test only passes because ord('c') > ord('*'). If there
1436
# was a config section for '/a/?', it would get precedence
1438
self.get_branch_config('/a/c')
1439
self.assertLocationMatching([('/a/c', ''), ('/a/*', ''), ('/a/', 'c')])
1441
def test__get_option_policy_normal(self):
1442
self.get_branch_config('http://www.example.com')
1444
self.my_location_config._get_config_policy(
1445
'http://www.example.com', 'normal_option'),
1448
def test__get_option_policy_norecurse(self):
1449
self.get_branch_config('http://www.example.com')
1451
self.my_location_config._get_option_policy(
1452
'http://www.example.com', 'norecurse_option'),
1453
config.POLICY_NORECURSE)
1454
# Test old recurse=False setting:
1456
self.my_location_config._get_option_policy(
1457
'http://www.example.com/norecurse', 'normal_option'),
1458
config.POLICY_NORECURSE)
1460
def test__get_option_policy_normal(self):
1461
self.get_branch_config('http://www.example.com')
1463
self.my_location_config._get_option_policy(
1464
'http://www.example.com', 'appendpath_option'),
1465
config.POLICY_APPENDPATH)
1467
def test__get_options_with_policy(self):
1468
self.get_branch_config('/dir/subdir',
1469
location_config="""\
1471
other_url = /other-dir
1472
other_url:policy = appendpath
1474
other_url = /other-subdir
1477
[(u'other_url', u'/other-subdir', u'/dir/subdir', 'locations'),
1478
(u'other_url', u'/other-dir', u'/dir', 'locations'),
1479
(u'other_url:policy', u'appendpath', u'/dir', 'locations')],
1480
self.my_location_config)
1482
def test_location_without_username(self):
1483
self.get_branch_config('http://www.example.com/ignoreparent')
1484
self.assertEqual(u'Erik B\u00e5gfors <erik@bagfors.nu>',
1485
self.my_config.username())
1487
def test_location_not_listed(self):
1488
"""Test that the global username is used when no location matches"""
1489
self.get_branch_config('/home/robertc/sources')
1490
self.assertEqual(u'Erik B\u00e5gfors <erik@bagfors.nu>',
1491
self.my_config.username())
1493
def test_overriding_location(self):
1494
self.get_branch_config('http://www.example.com/foo')
1495
self.assertEqual('Robert Collins <robertc@example.org>',
1496
self.my_config.username())
1498
def test_signatures_not_set(self):
1499
self.get_branch_config('http://www.example.com',
1500
global_config=sample_ignore_signatures)
1501
self.assertEqual(config.CHECK_ALWAYS,
1502
self.my_config.signature_checking())
1503
self.assertEqual(config.SIGN_NEVER,
1504
self.my_config.signing_policy())
1506
def test_signatures_never(self):
1507
self.get_branch_config('/a/c')
1508
self.assertEqual(config.CHECK_NEVER,
1509
self.my_config.signature_checking())
1511
def test_signatures_when_available(self):
1512
self.get_branch_config('/a/', global_config=sample_ignore_signatures)
1513
self.assertEqual(config.CHECK_IF_POSSIBLE,
1514
self.my_config.signature_checking())
1516
def test_signatures_always(self):
1517
self.get_branch_config('/b')
1518
self.assertEqual(config.CHECK_ALWAYS,
1519
self.my_config.signature_checking())
1521
def test_gpg_signing_command(self):
1522
self.get_branch_config('/b')
1523
self.assertEqual("gnome-gpg", self.my_config.gpg_signing_command())
1525
def test_gpg_signing_command_missing(self):
1526
self.get_branch_config('/a')
1527
self.assertEqual("false", self.my_config.gpg_signing_command())
1529
def test_gpg_signing_key(self):
1530
self.get_branch_config('/b')
1531
self.assertEqual("DD4D5088", self.my_config.gpg_signing_key())
1533
def test_gpg_signing_key_default(self):
1534
self.get_branch_config('/a')
1535
self.assertEqual("erik@bagfors.nu", self.my_config.gpg_signing_key())
1537
def test_get_user_option_global(self):
1538
self.get_branch_config('/a')
1539
self.assertEqual('something',
1540
self.my_config.get_user_option('user_global_option'))
1542
def test_get_user_option_local(self):
1543
self.get_branch_config('/a')
1544
self.assertEqual('local',
1545
self.my_config.get_user_option('user_local_option'))
1547
def test_get_user_option_appendpath(self):
1548
# returned as is for the base path:
1549
self.get_branch_config('http://www.example.com')
1550
self.assertEqual('append',
1551
self.my_config.get_user_option('appendpath_option'))
1552
# Extra path components get appended:
1553
self.get_branch_config('http://www.example.com/a/b/c')
1554
self.assertEqual('append/a/b/c',
1555
self.my_config.get_user_option('appendpath_option'))
1556
# Overriden for http://www.example.com/dir, where it is a
1558
self.get_branch_config('http://www.example.com/dir/a/b/c')
1559
self.assertEqual('normal',
1560
self.my_config.get_user_option('appendpath_option'))
1562
def test_get_user_option_norecurse(self):
1563
self.get_branch_config('http://www.example.com')
1564
self.assertEqual('norecurse',
1565
self.my_config.get_user_option('norecurse_option'))
1566
self.get_branch_config('http://www.example.com/dir')
1567
self.assertEqual(None,
1568
self.my_config.get_user_option('norecurse_option'))
1569
# http://www.example.com/norecurse is a recurse=False section
1570
# that redefines normal_option. Subdirectories do not pick up
1571
# this redefinition.
1572
self.get_branch_config('http://www.example.com/norecurse')
1573
self.assertEqual('norecurse',
1574
self.my_config.get_user_option('normal_option'))
1575
self.get_branch_config('http://www.example.com/norecurse/subdir')
1576
self.assertEqual('normal',
1577
self.my_config.get_user_option('normal_option'))
1579
def test_set_user_option_norecurse(self):
1580
self.get_branch_config('http://www.example.com')
1581
self.my_config.set_user_option('foo', 'bar',
1582
store=config.STORE_LOCATION_NORECURSE)
1584
self.my_location_config._get_option_policy(
1585
'http://www.example.com', 'foo'),
1586
config.POLICY_NORECURSE)
1588
def test_set_user_option_appendpath(self):
1589
self.get_branch_config('http://www.example.com')
1590
self.my_config.set_user_option('foo', 'bar',
1591
store=config.STORE_LOCATION_APPENDPATH)
1593
self.my_location_config._get_option_policy(
1594
'http://www.example.com', 'foo'),
1595
config.POLICY_APPENDPATH)
1597
def test_set_user_option_change_policy(self):
1598
self.get_branch_config('http://www.example.com')
1599
self.my_config.set_user_option('norecurse_option', 'normal',
1600
store=config.STORE_LOCATION)
1602
self.my_location_config._get_option_policy(
1603
'http://www.example.com', 'norecurse_option'),
1606
def test_set_user_option_recurse_false_section(self):
1607
# The following section has recurse=False set. The test is to
1608
# make sure that a normal option can be added to the section,
1609
# converting recurse=False to the norecurse policy.
1610
self.get_branch_config('http://www.example.com/norecurse')
1611
self.callDeprecated(['The recurse option is deprecated as of 0.14. '
1612
'The section "http://www.example.com/norecurse" '
1613
'has been converted to use policies.'],
1614
self.my_config.set_user_option,
1615
'foo', 'bar', store=config.STORE_LOCATION)
1617
self.my_location_config._get_option_policy(
1618
'http://www.example.com/norecurse', 'foo'),
1620
# The previously existing option is still norecurse:
1622
self.my_location_config._get_option_policy(
1623
'http://www.example.com/norecurse', 'normal_option'),
1624
config.POLICY_NORECURSE)
1626
def test_post_commit_default(self):
1627
self.get_branch_config('/a/c')
1628
self.assertEqual('bzrlib.tests.test_config.post_commit',
1629
self.my_config.post_commit())
1631
def get_branch_config(self, location, global_config=None,
1632
location_config=None):
1633
my_branch = FakeBranch(location)
1634
if global_config is None:
1635
global_config = sample_config_text
1636
if location_config is None:
1637
location_config = sample_branches_text
1639
my_global_config = config.GlobalConfig.from_string(global_config,
1641
my_location_config = config.LocationConfig.from_string(
1642
location_config, my_branch.base, save=True)
1643
my_config = config.BranchConfig(my_branch)
1644
self.my_config = my_config
1645
self.my_location_config = my_config._get_location_config()
1647
def test_set_user_setting_sets_and_saves(self):
1648
self.get_branch_config('/a/c')
1649
record = InstrumentedConfigObj("foo")
1650
self.my_location_config._parser = record
1652
self.callDeprecated(['The recurse option is deprecated as of '
1653
'0.14. The section "/a/c" has been '
1654
'converted to use policies.'],
1655
self.my_config.set_user_option,
1656
'foo', 'bar', store=config.STORE_LOCATION)
1657
self.assertEqual([('reload',),
1658
('__contains__', '/a/c'),
1659
('__contains__', '/a/c/'),
1660
('__setitem__', '/a/c', {}),
1661
('__getitem__', '/a/c'),
1662
('__setitem__', 'foo', 'bar'),
1663
('__getitem__', '/a/c'),
1664
('as_bool', 'recurse'),
1665
('__getitem__', '/a/c'),
1666
('__delitem__', 'recurse'),
1667
('__getitem__', '/a/c'),
1669
('__getitem__', '/a/c'),
1670
('__contains__', 'foo:policy'),
1674
def test_set_user_setting_sets_and_saves2(self):
1675
self.get_branch_config('/a/c')
1676
self.assertIs(self.my_config.get_user_option('foo'), None)
1677
self.my_config.set_user_option('foo', 'bar')
1679
self.my_config.branch.control_files.files['branch.conf'].strip(),
1681
self.assertEqual(self.my_config.get_user_option('foo'), 'bar')
1682
self.my_config.set_user_option('foo', 'baz',
1683
store=config.STORE_LOCATION)
1684
self.assertEqual(self.my_config.get_user_option('foo'), 'baz')
1685
self.my_config.set_user_option('foo', 'qux')
1686
self.assertEqual(self.my_config.get_user_option('foo'), 'baz')
1688
def test_get_bzr_remote_path(self):
1689
my_config = config.LocationConfig('/a/c')
1690
self.assertEqual('bzr', my_config.get_bzr_remote_path())
1691
my_config.set_user_option('bzr_remote_path', '/path-bzr')
1692
self.assertEqual('/path-bzr', my_config.get_bzr_remote_path())
1693
self.overrideEnv('BZR_REMOTE_PATH', '/environ-bzr')
1694
self.assertEqual('/environ-bzr', my_config.get_bzr_remote_path())
1697
precedence_global = 'option = global'
1698
precedence_branch = 'option = branch'
1699
precedence_location = """
1703
[http://example.com/specific]
1707
class TestBranchConfigItems(tests.TestCaseInTempDir):
1709
def get_branch_config(self, global_config=None, location=None,
1710
location_config=None, branch_data_config=None):
1711
my_branch = FakeBranch(location)
1712
if global_config is not None:
1713
my_global_config = config.GlobalConfig.from_string(global_config,
1715
if location_config is not None:
1716
my_location_config = config.LocationConfig.from_string(
1717
location_config, my_branch.base, save=True)
1718
my_config = config.BranchConfig(my_branch)
1719
if branch_data_config is not None:
1720
my_config.branch.control_files.files['branch.conf'] = \
1724
def test_user_id(self):
1725
branch = FakeBranch(user_id='Robert Collins <robertc@example.net>')
1726
my_config = config.BranchConfig(branch)
1727
self.assertEqual("Robert Collins <robertc@example.net>",
1728
my_config.username())
1729
my_config.branch.control_files.files['email'] = "John"
1730
my_config.set_user_option('email',
1731
"Robert Collins <robertc@example.org>")
1732
self.assertEqual("John", my_config.username())
1733
del my_config.branch.control_files.files['email']
1734
self.assertEqual("Robert Collins <robertc@example.org>",
1735
my_config.username())
1737
def test_not_set_in_branch(self):
1738
my_config = self.get_branch_config(global_config=sample_config_text)
1739
self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
1740
my_config._get_user_id())
1741
my_config.branch.control_files.files['email'] = "John"
1742
self.assertEqual("John", my_config._get_user_id())
1744
def test_BZR_EMAIL_OVERRIDES(self):
1745
self.overrideEnv('BZR_EMAIL', "Robert Collins <robertc@example.org>")
1746
branch = FakeBranch()
1747
my_config = config.BranchConfig(branch)
1748
self.assertEqual("Robert Collins <robertc@example.org>",
1749
my_config.username())
1751
def test_signatures_forced(self):
1752
my_config = self.get_branch_config(
1753
global_config=sample_always_signatures)
1754
self.assertEqual(config.CHECK_NEVER, my_config.signature_checking())
1755
self.assertEqual(config.SIGN_ALWAYS, my_config.signing_policy())
1756
self.assertTrue(my_config.signature_needed())
1758
def test_signatures_forced_branch(self):
1759
my_config = self.get_branch_config(
1760
global_config=sample_ignore_signatures,
1761
branch_data_config=sample_always_signatures)
1762
self.assertEqual(config.CHECK_NEVER, my_config.signature_checking())
1763
self.assertEqual(config.SIGN_ALWAYS, my_config.signing_policy())
1764
self.assertTrue(my_config.signature_needed())
1766
def test_gpg_signing_command(self):
1767
my_config = self.get_branch_config(
1768
global_config=sample_config_text,
1769
# branch data cannot set gpg_signing_command
1770
branch_data_config="gpg_signing_command=pgp")
1771
self.assertEqual('gnome-gpg', my_config.gpg_signing_command())
1773
def test_get_user_option_global(self):
1774
my_config = self.get_branch_config(global_config=sample_config_text)
1775
self.assertEqual('something',
1776
my_config.get_user_option('user_global_option'))
1778
def test_post_commit_default(self):
1779
my_config = self.get_branch_config(global_config=sample_config_text,
1781
location_config=sample_branches_text)
1782
self.assertEqual(my_config.branch.base, '/a/c')
1783
self.assertEqual('bzrlib.tests.test_config.post_commit',
1784
my_config.post_commit())
1785
my_config.set_user_option('post_commit', 'rmtree_root')
1786
# post-commit is ignored when present in branch data
1787
self.assertEqual('bzrlib.tests.test_config.post_commit',
1788
my_config.post_commit())
1789
my_config.set_user_option('post_commit', 'rmtree_root',
1790
store=config.STORE_LOCATION)
1791
self.assertEqual('rmtree_root', my_config.post_commit())
1793
def test_config_precedence(self):
1794
# FIXME: eager test, luckily no persitent config file makes it fail
1796
my_config = self.get_branch_config(global_config=precedence_global)
1797
self.assertEqual(my_config.get_user_option('option'), 'global')
1798
my_config = self.get_branch_config(global_config=precedence_global,
1799
branch_data_config=precedence_branch)
1800
self.assertEqual(my_config.get_user_option('option'), 'branch')
1801
my_config = self.get_branch_config(
1802
global_config=precedence_global,
1803
branch_data_config=precedence_branch,
1804
location_config=precedence_location)
1805
self.assertEqual(my_config.get_user_option('option'), 'recurse')
1806
my_config = self.get_branch_config(
1807
global_config=precedence_global,
1808
branch_data_config=precedence_branch,
1809
location_config=precedence_location,
1810
location='http://example.com/specific')
1811
self.assertEqual(my_config.get_user_option('option'), 'exact')
1813
def test_get_mail_client(self):
1814
config = self.get_branch_config()
1815
client = config.get_mail_client()
1816
self.assertIsInstance(client, mail_client.DefaultMail)
1819
config.set_user_option('mail_client', 'evolution')
1820
client = config.get_mail_client()
1821
self.assertIsInstance(client, mail_client.Evolution)
1823
config.set_user_option('mail_client', 'kmail')
1824
client = config.get_mail_client()
1825
self.assertIsInstance(client, mail_client.KMail)
1827
config.set_user_option('mail_client', 'mutt')
1828
client = config.get_mail_client()
1829
self.assertIsInstance(client, mail_client.Mutt)
1831
config.set_user_option('mail_client', 'thunderbird')
1832
client = config.get_mail_client()
1833
self.assertIsInstance(client, mail_client.Thunderbird)
1836
config.set_user_option('mail_client', 'default')
1837
client = config.get_mail_client()
1838
self.assertIsInstance(client, mail_client.DefaultMail)
1840
config.set_user_option('mail_client', 'editor')
1841
client = config.get_mail_client()
1842
self.assertIsInstance(client, mail_client.Editor)
1844
config.set_user_option('mail_client', 'mapi')
1845
client = config.get_mail_client()
1846
self.assertIsInstance(client, mail_client.MAPIClient)
1848
config.set_user_option('mail_client', 'xdg-email')
1849
client = config.get_mail_client()
1850
self.assertIsInstance(client, mail_client.XDGEmail)
1852
config.set_user_option('mail_client', 'firebird')
1853
self.assertRaises(errors.UnknownMailClient, config.get_mail_client)
1856
class TestMailAddressExtraction(tests.TestCase):
1858
def test_extract_email_address(self):
1859
self.assertEqual('jane@test.com',
1860
config.extract_email_address('Jane <jane@test.com>'))
1861
self.assertRaises(errors.NoEmailInUsername,
1862
config.extract_email_address, 'Jane Tester')
1864
def test_parse_username(self):
1865
self.assertEqual(('', 'jdoe@example.com'),
1866
config.parse_username('jdoe@example.com'))
1867
self.assertEqual(('', 'jdoe@example.com'),
1868
config.parse_username('<jdoe@example.com>'))
1869
self.assertEqual(('John Doe', 'jdoe@example.com'),
1870
config.parse_username('John Doe <jdoe@example.com>'))
1871
self.assertEqual(('John Doe', ''),
1872
config.parse_username('John Doe'))
1873
self.assertEqual(('John Doe', 'jdoe@example.com'),
1874
config.parse_username('John Doe jdoe@example.com'))
1876
class TestTreeConfig(tests.TestCaseWithTransport):
1878
def test_get_value(self):
1879
"""Test that retreiving a value from a section is possible"""
1880
branch = self.make_branch('.')
1881
tree_config = config.TreeConfig(branch)
1882
tree_config.set_option('value', 'key', 'SECTION')
1883
tree_config.set_option('value2', 'key2')
1884
tree_config.set_option('value3-top', 'key3')
1885
tree_config.set_option('value3-section', 'key3', 'SECTION')
1886
value = tree_config.get_option('key', 'SECTION')
1887
self.assertEqual(value, 'value')
1888
value = tree_config.get_option('key2')
1889
self.assertEqual(value, 'value2')
1890
self.assertEqual(tree_config.get_option('non-existant'), None)
1891
value = tree_config.get_option('non-existant', 'SECTION')
1892
self.assertEqual(value, None)
1893
value = tree_config.get_option('non-existant', default='default')
1894
self.assertEqual(value, 'default')
1895
self.assertEqual(tree_config.get_option('key2', 'NOSECTION'), None)
1896
value = tree_config.get_option('key2', 'NOSECTION', default='default')
1897
self.assertEqual(value, 'default')
1898
value = tree_config.get_option('key3')
1899
self.assertEqual(value, 'value3-top')
1900
value = tree_config.get_option('key3', 'SECTION')
1901
self.assertEqual(value, 'value3-section')
1904
class TestTransportConfig(tests.TestCaseWithTransport):
1906
def test_load_utf8(self):
1907
"""Ensure we can load an utf8-encoded file."""
1908
t = self.get_transport()
1909
unicode_user = u'b\N{Euro Sign}ar'
1910
unicode_content = u'user=%s' % (unicode_user,)
1911
utf8_content = unicode_content.encode('utf8')
1912
# Store the raw content in the config file
1913
t.put_bytes('foo.conf', utf8_content)
1914
conf = config.TransportConfig(t, 'foo.conf')
1915
self.assertEquals(unicode_user, conf.get_option('user'))
1917
def test_load_non_ascii(self):
1918
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
1919
t = self.get_transport()
1920
t.put_bytes('foo.conf', 'user=foo\n#\xff\n')
1921
conf = config.TransportConfig(t, 'foo.conf')
1922
self.assertRaises(errors.ConfigContentError, conf._get_configobj)
1924
def test_load_erroneous_content(self):
1925
"""Ensure we display a proper error on content that can't be parsed."""
1926
t = self.get_transport()
1927
t.put_bytes('foo.conf', '[open_section\n')
1928
conf = config.TransportConfig(t, 'foo.conf')
1929
self.assertRaises(errors.ParseConfigError, conf._get_configobj)
1931
def test_get_value(self):
1932
"""Test that retreiving a value from a section is possible"""
1933
bzrdir_config = config.TransportConfig(self.get_transport('.'),
1935
bzrdir_config.set_option('value', 'key', 'SECTION')
1936
bzrdir_config.set_option('value2', 'key2')
1937
bzrdir_config.set_option('value3-top', 'key3')
1938
bzrdir_config.set_option('value3-section', 'key3', 'SECTION')
1939
value = bzrdir_config.get_option('key', 'SECTION')
1940
self.assertEqual(value, 'value')
1941
value = bzrdir_config.get_option('key2')
1942
self.assertEqual(value, 'value2')
1943
self.assertEqual(bzrdir_config.get_option('non-existant'), None)
1944
value = bzrdir_config.get_option('non-existant', 'SECTION')
1945
self.assertEqual(value, None)
1946
value = bzrdir_config.get_option('non-existant', default='default')
1947
self.assertEqual(value, 'default')
1948
self.assertEqual(bzrdir_config.get_option('key2', 'NOSECTION'), None)
1949
value = bzrdir_config.get_option('key2', 'NOSECTION',
1951
self.assertEqual(value, 'default')
1952
value = bzrdir_config.get_option('key3')
1953
self.assertEqual(value, 'value3-top')
1954
value = bzrdir_config.get_option('key3', 'SECTION')
1955
self.assertEqual(value, 'value3-section')
1957
def test_set_unset_default_stack_on(self):
1958
my_dir = self.make_bzrdir('.')
1959
bzrdir_config = config.BzrDirConfig(my_dir)
1960
self.assertIs(None, bzrdir_config.get_default_stack_on())
1961
bzrdir_config.set_default_stack_on('Foo')
1962
self.assertEqual('Foo', bzrdir_config._config.get_option(
1963
'default_stack_on'))
1964
self.assertEqual('Foo', bzrdir_config.get_default_stack_on())
1965
bzrdir_config.set_default_stack_on(None)
1966
self.assertIs(None, bzrdir_config.get_default_stack_on())
1969
class TestOldConfigHooks(tests.TestCaseWithTransport):
1972
super(TestOldConfigHooks, self).setUp()
1973
create_configs_with_file_option(self)
1975
def assertGetHook(self, conf, name, value):
1979
config.OldConfigHooks.install_named_hook('get', hook, None)
1981
config.OldConfigHooks.uninstall_named_hook, 'get', None)
1982
self.assertLength(0, calls)
1983
actual_value = conf.get_user_option(name)
1984
self.assertEquals(value, actual_value)
1985
self.assertLength(1, calls)
1986
self.assertEquals((conf, name, value), calls[0])
1988
def test_get_hook_bazaar(self):
1989
self.assertGetHook(self.bazaar_config, 'file', 'bazaar')
1991
def test_get_hook_locations(self):
1992
self.assertGetHook(self.locations_config, 'file', 'locations')
1994
def test_get_hook_branch(self):
1995
# Since locations masks branch, we define a different option
1996
self.branch_config.set_user_option('file2', 'branch')
1997
self.assertGetHook(self.branch_config, 'file2', 'branch')
1999
def assertSetHook(self, conf, name, value):
2003
config.OldConfigHooks.install_named_hook('set', hook, None)
2005
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2006
self.assertLength(0, calls)
2007
conf.set_user_option(name, value)
2008
self.assertLength(1, calls)
2009
# We can't assert the conf object below as different configs use
2010
# different means to implement set_user_option and we care only about
2012
self.assertEquals((name, value), calls[0][1:])
2014
def test_set_hook_bazaar(self):
2015
self.assertSetHook(self.bazaar_config, 'foo', 'bazaar')
2017
def test_set_hook_locations(self):
2018
self.assertSetHook(self.locations_config, 'foo', 'locations')
2020
def test_set_hook_branch(self):
2021
self.assertSetHook(self.branch_config, 'foo', 'branch')
2023
def assertRemoveHook(self, conf, name, section_name=None):
2027
config.OldConfigHooks.install_named_hook('remove', hook, None)
2029
config.OldConfigHooks.uninstall_named_hook, 'remove', None)
2030
self.assertLength(0, calls)
2031
conf.remove_user_option(name, section_name)
2032
self.assertLength(1, calls)
2033
# We can't assert the conf object below as different configs use
2034
# different means to implement remove_user_option and we care only about
2036
self.assertEquals((name,), calls[0][1:])
2038
def test_remove_hook_bazaar(self):
2039
self.assertRemoveHook(self.bazaar_config, 'file')
2041
def test_remove_hook_locations(self):
2042
self.assertRemoveHook(self.locations_config, 'file',
2043
self.locations_config.location)
2045
def test_remove_hook_branch(self):
2046
self.assertRemoveHook(self.branch_config, 'file')
2048
def assertLoadHook(self, name, conf_class, *conf_args):
2052
config.OldConfigHooks.install_named_hook('load', hook, None)
2054
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2055
self.assertLength(0, calls)
2057
conf = conf_class(*conf_args)
2058
# Access an option to trigger a load
2059
conf.get_user_option(name)
2060
self.assertLength(1, calls)
2061
# Since we can't assert about conf, we just use the number of calls ;-/
2063
def test_load_hook_bazaar(self):
2064
self.assertLoadHook('file', config.GlobalConfig)
2066
def test_load_hook_locations(self):
2067
self.assertLoadHook('file', config.LocationConfig, self.tree.basedir)
2069
def test_load_hook_branch(self):
2070
self.assertLoadHook('file', config.BranchConfig, self.tree.branch)
2072
def assertSaveHook(self, conf):
2076
config.OldConfigHooks.install_named_hook('save', hook, None)
2078
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2079
self.assertLength(0, calls)
2080
# Setting an option triggers a save
2081
conf.set_user_option('foo', 'bar')
2082
self.assertLength(1, calls)
2083
# Since we can't assert about conf, we just use the number of calls ;-/
2085
def test_save_hook_bazaar(self):
2086
self.assertSaveHook(self.bazaar_config)
2088
def test_save_hook_locations(self):
2089
self.assertSaveHook(self.locations_config)
2091
def test_save_hook_branch(self):
2092
self.assertSaveHook(self.branch_config)
2095
class TestOldConfigHooksForRemote(tests.TestCaseWithTransport):
2096
"""Tests config hooks for remote configs.
2098
No tests for the remove hook as this is not implemented there.
2102
super(TestOldConfigHooksForRemote, self).setUp()
2103
self.transport_server = test_server.SmartTCPServer_for_testing
2104
create_configs_with_file_option(self)
2106
def assertGetHook(self, conf, name, value):
2110
config.OldConfigHooks.install_named_hook('get', hook, None)
2112
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2113
self.assertLength(0, calls)
2114
actual_value = conf.get_option(name)
2115
self.assertEquals(value, actual_value)
2116
self.assertLength(1, calls)
2117
self.assertEquals((conf, name, value), calls[0])
2119
def test_get_hook_remote_branch(self):
2120
remote_branch = branch.Branch.open(self.get_url('tree'))
2121
self.assertGetHook(remote_branch._get_config(), 'file', 'branch')
2123
def test_get_hook_remote_bzrdir(self):
2124
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2125
conf = remote_bzrdir._get_config()
2126
conf.set_option('remotedir', 'file')
2127
self.assertGetHook(conf, 'file', 'remotedir')
2129
def assertSetHook(self, conf, name, value):
2133
config.OldConfigHooks.install_named_hook('set', hook, None)
2135
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2136
self.assertLength(0, calls)
2137
conf.set_option(value, name)
2138
self.assertLength(1, calls)
2139
# We can't assert the conf object below as different configs use
2140
# different means to implement set_user_option and we care only about
2142
self.assertEquals((name, value), calls[0][1:])
2144
def test_set_hook_remote_branch(self):
2145
remote_branch = branch.Branch.open(self.get_url('tree'))
2146
self.addCleanup(remote_branch.lock_write().unlock)
2147
self.assertSetHook(remote_branch._get_config(), 'file', 'remote')
2149
def test_set_hook_remote_bzrdir(self):
2150
remote_branch = branch.Branch.open(self.get_url('tree'))
2151
self.addCleanup(remote_branch.lock_write().unlock)
2152
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2153
self.assertSetHook(remote_bzrdir._get_config(), 'file', 'remotedir')
2155
def assertLoadHook(self, expected_nb_calls, name, conf_class, *conf_args):
2159
config.OldConfigHooks.install_named_hook('load', hook, None)
2161
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2162
self.assertLength(0, calls)
2164
conf = conf_class(*conf_args)
2165
# Access an option to trigger a load
2166
conf.get_option(name)
2167
self.assertLength(expected_nb_calls, calls)
2168
# Since we can't assert about conf, we just use the number of calls ;-/
2170
def test_load_hook_remote_branch(self):
2171
remote_branch = branch.Branch.open(self.get_url('tree'))
2172
self.assertLoadHook(1, 'file', remote.RemoteBranchConfig, remote_branch)
2174
def test_load_hook_remote_bzrdir(self):
2175
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2176
# The config file doesn't exist, set an option to force its creation
2177
conf = remote_bzrdir._get_config()
2178
conf.set_option('remotedir', 'file')
2179
# We get one call for the server and one call for the client, this is
2180
# caused by the differences in implementations betwen
2181
# SmartServerBzrDirRequestConfigFile (in smart/bzrdir.py) and
2182
# SmartServerBranchGetConfigFile (in smart/branch.py)
2183
self.assertLoadHook(2 ,'file', remote.RemoteBzrDirConfig, remote_bzrdir)
2185
def assertSaveHook(self, conf):
2189
config.OldConfigHooks.install_named_hook('save', hook, None)
2191
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2192
self.assertLength(0, calls)
2193
# Setting an option triggers a save
2194
conf.set_option('foo', 'bar')
2195
self.assertLength(1, calls)
2196
# Since we can't assert about conf, we just use the number of calls ;-/
2198
def test_save_hook_remote_branch(self):
2199
remote_branch = branch.Branch.open(self.get_url('tree'))
2200
self.addCleanup(remote_branch.lock_write().unlock)
2201
self.assertSaveHook(remote_branch._get_config())
2203
def test_save_hook_remote_bzrdir(self):
2204
remote_branch = branch.Branch.open(self.get_url('tree'))
2205
self.addCleanup(remote_branch.lock_write().unlock)
2206
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2207
self.assertSaveHook(remote_bzrdir._get_config())
2210
class TestOption(tests.TestCase):
2212
def test_default_value(self):
2213
opt = config.Option('foo', default='bar')
2214
self.assertEquals('bar', opt.get_default())
2217
class TestOptionRegistry(tests.TestCase):
2220
super(TestOptionRegistry, self).setUp()
2221
# Always start with an empty registry
2222
self.overrideAttr(config, 'option_registry', registry.Registry())
2223
self.registry = config.option_registry
2225
def test_register(self):
2226
opt = config.Option('foo')
2227
self.registry.register('foo', opt)
2228
self.assertIs(opt, self.registry.get('foo'))
2230
lazy_option = config.Option('lazy_foo')
2232
def test_register_lazy(self):
2233
self.registry.register_lazy('foo', self.__module__,
2234
'TestOptionRegistry.lazy_option')
2235
self.assertIs(self.lazy_option, self.registry.get('foo'))
2237
def test_registered_help(self):
2238
opt = config.Option('foo')
2239
self.registry.register('foo', opt, help='A simple option')
2240
self.assertEquals('A simple option', self.registry.get_help('foo'))
2243
class TestRegisteredOptions(tests.TestCase):
2244
"""All registered options should verify some constraints."""
2246
scenarios = [(key, {'option_name': key, 'option': option}) for key, option
2247
in config.option_registry.iteritems()]
2250
super(TestRegisteredOptions, self).setUp()
2251
self.registry = config.option_registry
2253
def test_proper_name(self):
2254
# An option should be registered under its own name, this can't be
2255
# checked at registration time for the lazy ones.
2256
self.assertEquals(self.option_name, self.option.name)
2258
def test_help_is_set(self):
2259
option_help = self.registry.get_help(self.option_name)
2260
self.assertNotEquals(None, option_help)
2261
# Come on, think about the user, he really wants to know whst the
2263
self.assertNotEquals('', option_help)
2266
class TestSection(tests.TestCase):
2268
# FIXME: Parametrize so that all sections produced by Stores run these
2269
# tests -- vila 2011-04-01
2271
def test_get_a_value(self):
2272
a_dict = dict(foo='bar')
2273
section = config.Section('myID', a_dict)
2274
self.assertEquals('bar', section.get('foo'))
2276
def test_get_unknown_option(self):
2278
section = config.Section(None, a_dict)
2279
self.assertEquals('out of thin air',
2280
section.get('foo', 'out of thin air'))
2282
def test_options_is_shared(self):
2284
section = config.Section(None, a_dict)
2285
self.assertIs(a_dict, section.options)
2288
class TestMutableSection(tests.TestCase):
2290
# FIXME: Parametrize so that all sections (including os.environ and the
2291
# ones produced by Stores) run these tests -- vila 2011-04-01
2294
a_dict = dict(foo='bar')
2295
section = config.MutableSection('myID', a_dict)
2296
section.set('foo', 'new_value')
2297
self.assertEquals('new_value', section.get('foo'))
2298
# The change appears in the shared section
2299
self.assertEquals('new_value', a_dict.get('foo'))
2300
# We keep track of the change
2301
self.assertTrue('foo' in section.orig)
2302
self.assertEquals('bar', section.orig.get('foo'))
2304
def test_set_preserve_original_once(self):
2305
a_dict = dict(foo='bar')
2306
section = config.MutableSection('myID', a_dict)
2307
section.set('foo', 'first_value')
2308
section.set('foo', 'second_value')
2309
# We keep track of the original value
2310
self.assertTrue('foo' in section.orig)
2311
self.assertEquals('bar', section.orig.get('foo'))
2313
def test_remove(self):
2314
a_dict = dict(foo='bar')
2315
section = config.MutableSection('myID', a_dict)
2316
section.remove('foo')
2317
# We get None for unknown options via the default value
2318
self.assertEquals(None, section.get('foo'))
2319
# Or we just get the default value
2320
self.assertEquals('unknown', section.get('foo', 'unknown'))
2321
self.assertFalse('foo' in section.options)
2322
# We keep track of the deletion
2323
self.assertTrue('foo' in section.orig)
2324
self.assertEquals('bar', section.orig.get('foo'))
2326
def test_remove_new_option(self):
2328
section = config.MutableSection('myID', a_dict)
2329
section.set('foo', 'bar')
2330
section.remove('foo')
2331
self.assertFalse('foo' in section.options)
2332
# The option didn't exist initially so it we need to keep track of it
2333
# with a special value
2334
self.assertTrue('foo' in section.orig)
2335
self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
2338
class TestStore(tests.TestCaseWithTransport):
2340
def assertSectionContent(self, expected, section):
2341
"""Assert that some options have the proper values in a section."""
2342
expected_name, expected_options = expected
2343
self.assertEquals(expected_name, section.id)
2346
dict([(k, section.get(k)) for k in expected_options.keys()]))
2349
class TestReadonlyStore(TestStore):
2351
scenarios = [(key, {'get_store': builder}) for key, builder
2352
in config.test_store_builder_registry.iteritems()]
2355
super(TestReadonlyStore, self).setUp()
2357
def test_building_delays_load(self):
2358
store = self.get_store(self)
2359
self.assertEquals(False, store.is_loaded())
2360
store._load_from_string('')
2361
self.assertEquals(True, store.is_loaded())
2363
def test_get_no_sections_for_empty(self):
2364
store = self.get_store(self)
2365
store._load_from_string('')
2366
self.assertEquals([], list(store.get_sections()))
2368
def test_get_default_section(self):
2369
store = self.get_store(self)
2370
store._load_from_string('foo=bar')
2371
sections = list(store.get_sections())
2372
self.assertLength(1, sections)
2373
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2375
def test_get_named_section(self):
2376
store = self.get_store(self)
2377
store._load_from_string('[baz]\nfoo=bar')
2378
sections = list(store.get_sections())
2379
self.assertLength(1, sections)
2380
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2382
def test_load_from_string_fails_for_non_empty_store(self):
2383
store = self.get_store(self)
2384
store._load_from_string('foo=bar')
2385
self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2388
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2389
"""Simulate loading a config store without content of various encodings.
2391
All files produced by bzr are in utf8 content.
2393
Users may modify them manually and end up with a file that can't be
2394
loaded. We need to issue proper error messages in this case.
2397
invalid_utf8_char = '\xff'
2399
def test_load_utf8(self):
2400
"""Ensure we can load an utf8-encoded file."""
2401
t = self.get_transport()
2402
# From http://pad.lv/799212
2403
unicode_user = u'b\N{Euro Sign}ar'
2404
unicode_content = u'user=%s' % (unicode_user,)
2405
utf8_content = unicode_content.encode('utf8')
2406
# Store the raw content in the config file
2407
t.put_bytes('foo.conf', utf8_content)
2408
store = config.IniFileStore(t, 'foo.conf')
2410
stack = config.Stack([store.get_sections], store)
2411
self.assertEquals(unicode_user, stack.get('user'))
2413
def test_load_non_ascii(self):
2414
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2415
t = self.get_transport()
2416
t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2417
store = config.IniFileStore(t, 'foo.conf')
2418
self.assertRaises(errors.ConfigContentError, store.load)
2420
def test_load_erroneous_content(self):
2421
"""Ensure we display a proper error on content that can't be parsed."""
2422
t = self.get_transport()
2423
t.put_bytes('foo.conf', '[open_section\n')
2424
store = config.IniFileStore(t, 'foo.conf')
2425
self.assertRaises(errors.ParseConfigError, store.load)
2428
class TestIniConfigContent(tests.TestCaseWithTransport):
2429
"""Simulate loading a IniBasedConfig without content of various encodings.
2431
All files produced by bzr are in utf8 content.
2433
Users may modify them manually and end up with a file that can't be
2434
loaded. We need to issue proper error messages in this case.
2437
invalid_utf8_char = '\xff'
2439
def test_load_utf8(self):
2440
"""Ensure we can load an utf8-encoded file."""
2441
# From http://pad.lv/799212
2442
unicode_user = u'b\N{Euro Sign}ar'
2443
unicode_content = u'user=%s' % (unicode_user,)
2444
utf8_content = unicode_content.encode('utf8')
2445
# Store the raw content in the config file
2446
with open('foo.conf', 'wb') as f:
2447
f.write(utf8_content)
2448
conf = config.IniBasedConfig(file_name='foo.conf')
2449
self.assertEquals(unicode_user, conf.get_user_option('user'))
2451
def test_load_badly_encoded_content(self):
2452
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2453
with open('foo.conf', 'wb') as f:
2454
f.write('user=foo\n#%s\n' % (self.invalid_utf8_char,))
2455
conf = config.IniBasedConfig(file_name='foo.conf')
2456
self.assertRaises(errors.ConfigContentError, conf._get_parser)
2458
def test_load_erroneous_content(self):
2459
"""Ensure we display a proper error on content that can't be parsed."""
2460
with open('foo.conf', 'wb') as f:
2461
f.write('[open_section\n')
2462
conf = config.IniBasedConfig(file_name='foo.conf')
2463
self.assertRaises(errors.ParseConfigError, conf._get_parser)
2466
class TestMutableStore(TestStore):
2468
scenarios = [(key, {'store_id': key, 'get_store': builder}) for key, builder
2469
in config.test_store_builder_registry.iteritems()]
2472
super(TestMutableStore, self).setUp()
2473
self.transport = self.get_transport()
2475
def has_store(self, store):
2476
store_basename = urlutils.relative_url(self.transport.external_url(),
2477
store.external_url())
2478
return self.transport.has(store_basename)
2480
def test_save_empty_creates_no_file(self):
2481
# FIXME: There should be a better way than relying on the test
2482
# parametrization to identify branch.conf -- vila 2011-0526
2483
if self.store_id in ('branch', 'remote_branch'):
2484
raise tests.TestNotApplicable(
2485
'branch.conf is *always* created when a branch is initialized')
2486
store = self.get_store(self)
2488
self.assertEquals(False, self.has_store(store))
2490
def test_save_emptied_succeeds(self):
2491
store = self.get_store(self)
2492
store._load_from_string('foo=bar\n')
2493
section = store.get_mutable_section(None)
2494
section.remove('foo')
2496
self.assertEquals(True, self.has_store(store))
2497
modified_store = self.get_store(self)
2498
sections = list(modified_store.get_sections())
2499
self.assertLength(0, sections)
2501
def test_save_with_content_succeeds(self):
2502
# FIXME: There should be a better way than relying on the test
2503
# parametrization to identify branch.conf -- vila 2011-0526
2504
if self.store_id in ('branch', 'remote_branch'):
2505
raise tests.TestNotApplicable(
2506
'branch.conf is *always* created when a branch is initialized')
2507
store = self.get_store(self)
2508
store._load_from_string('foo=bar\n')
2509
self.assertEquals(False, self.has_store(store))
2511
self.assertEquals(True, self.has_store(store))
2512
modified_store = self.get_store(self)
2513
sections = list(modified_store.get_sections())
2514
self.assertLength(1, sections)
2515
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2517
def test_set_option_in_empty_store(self):
2518
store = self.get_store(self)
2519
section = store.get_mutable_section(None)
2520
section.set('foo', 'bar')
2522
modified_store = self.get_store(self)
2523
sections = list(modified_store.get_sections())
2524
self.assertLength(1, sections)
2525
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2527
def test_set_option_in_default_section(self):
2528
store = self.get_store(self)
2529
store._load_from_string('')
2530
section = store.get_mutable_section(None)
2531
section.set('foo', 'bar')
2533
modified_store = self.get_store(self)
2534
sections = list(modified_store.get_sections())
2535
self.assertLength(1, sections)
2536
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2538
def test_set_option_in_named_section(self):
2539
store = self.get_store(self)
2540
store._load_from_string('')
2541
section = store.get_mutable_section('baz')
2542
section.set('foo', 'bar')
2544
modified_store = self.get_store(self)
2545
sections = list(modified_store.get_sections())
2546
self.assertLength(1, sections)
2547
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2549
def test_load_hook(self):
2550
# We first needs to ensure that the store exists
2551
store = self.get_store(self)
2552
section = store.get_mutable_section('baz')
2553
section.set('foo', 'bar')
2555
# Now we can try to load it
2556
store = self.get_store(self)
2560
config.ConfigHooks.install_named_hook('load', hook, None)
2561
self.assertLength(0, calls)
2563
self.assertLength(1, calls)
2564
self.assertEquals((store,), calls[0])
2566
def test_save_hook(self):
2570
config.ConfigHooks.install_named_hook('save', hook, None)
2571
self.assertLength(0, calls)
2572
store = self.get_store(self)
2573
section = store.get_mutable_section('baz')
2574
section.set('foo', 'bar')
2576
self.assertLength(1, calls)
2577
self.assertEquals((store,), calls[0])
2580
class TestIniFileStore(TestStore):
2582
def test_loading_unknown_file_fails(self):
2583
store = config.IniFileStore(self.get_transport(), 'I-do-not-exist')
2584
self.assertRaises(errors.NoSuchFile, store.load)
2586
def test_invalid_content(self):
2587
store = config.IniFileStore(self.get_transport(), 'foo.conf', )
2588
self.assertEquals(False, store.is_loaded())
2589
exc = self.assertRaises(
2590
errors.ParseConfigError, store._load_from_string,
2591
'this is invalid !')
2592
self.assertEndsWith(exc.filename, 'foo.conf')
2593
# And the load failed
2594
self.assertEquals(False, store.is_loaded())
2596
def test_get_embedded_sections(self):
2597
# A more complicated example (which also shows that section names and
2598
# option names share the same name space...)
2599
# FIXME: This should be fixed by forbidding dicts as values ?
2600
# -- vila 2011-04-05
2601
store = config.IniFileStore(self.get_transport(), 'foo.conf', )
2602
store._load_from_string('''
2606
foo_in_DEFAULT=foo_DEFAULT
2614
sections = list(store.get_sections())
2615
self.assertLength(4, sections)
2616
# The default section has no name.
2617
# List values are provided as lists
2618
self.assertSectionContent((None, {'foo': 'bar', 'l': ['1', '2']}),
2620
self.assertSectionContent(
2621
('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
2622
self.assertSectionContent(
2623
('bar', {'foo_in_bar': 'barbar'}), sections[2])
2624
# sub sections are provided as embedded dicts.
2625
self.assertSectionContent(
2626
('baz', {'foo_in_baz': 'barbaz', 'qux': {'foo_in_qux': 'quux'}}),
2630
class TestLockableIniFileStore(TestStore):
2632
def test_create_store_in_created_dir(self):
2633
self.assertPathDoesNotExist('dir')
2634
t = self.get_transport('dir/subdir')
2635
store = config.LockableIniFileStore(t, 'foo.conf')
2636
store.get_mutable_section(None).set('foo', 'bar')
2638
self.assertPathExists('dir/subdir')
2641
class TestConcurrentStoreUpdates(TestStore):
2642
"""Test that Stores properly handle conccurent updates.
2644
New Store implementation may fail some of these tests but until such
2645
implementations exist it's hard to properly filter them from the scenarios
2646
applied here. If you encounter such a case, contact the bzr devs.
2649
scenarios = [(key, {'get_stack': builder}) for key, builder
2650
in config.test_stack_builder_registry.iteritems()]
2653
super(TestConcurrentStoreUpdates, self).setUp()
2654
self._content = 'one=1\ntwo=2\n'
2655
self.stack = self.get_stack(self)
2656
if not isinstance(self.stack, config._CompatibleStack):
2657
raise tests.TestNotApplicable(
2658
'%s is not meant to be compatible with the old config design'
2660
self.stack.store._load_from_string(self._content)
2662
self.stack.store.save()
2664
def test_simple_read_access(self):
2665
self.assertEquals('1', self.stack.get('one'))
2667
def test_simple_write_access(self):
2668
self.stack.set('one', 'one')
2669
self.assertEquals('one', self.stack.get('one'))
2671
def test_listen_to_the_last_speaker(self):
2673
c2 = self.get_stack(self)
2674
c1.set('one', 'ONE')
2675
c2.set('two', 'TWO')
2676
self.assertEquals('ONE', c1.get('one'))
2677
self.assertEquals('TWO', c2.get('two'))
2678
# The second update respect the first one
2679
self.assertEquals('ONE', c2.get('one'))
2681
def test_last_speaker_wins(self):
2682
# If the same config is not shared, the same variable modified twice
2683
# can only see a single result.
2685
c2 = self.get_stack(self)
2688
self.assertEquals('c2', c2.get('one'))
2689
# The first modification is still available until another refresh
2691
self.assertEquals('c1', c1.get('one'))
2692
c1.set('two', 'done')
2693
self.assertEquals('c2', c1.get('one'))
2695
def test_writes_are_serialized(self):
2697
c2 = self.get_stack(self)
2699
# We spawn a thread that will pause *during* the config saving.
2700
before_writing = threading.Event()
2701
after_writing = threading.Event()
2702
writing_done = threading.Event()
2703
c1_save_without_locking_orig = c1.store.save_without_locking
2704
def c1_save_without_locking():
2705
before_writing.set()
2706
c1_save_without_locking_orig()
2707
# The lock is held. We wait for the main thread to decide when to
2709
after_writing.wait()
2710
c1.store.save_without_locking = c1_save_without_locking
2714
t1 = threading.Thread(target=c1_set)
2715
# Collect the thread after the test
2716
self.addCleanup(t1.join)
2717
# Be ready to unblock the thread if the test goes wrong
2718
self.addCleanup(after_writing.set)
2720
before_writing.wait()
2721
self.assertRaises(errors.LockContention,
2722
c2.set, 'one', 'c2')
2723
self.assertEquals('c1', c1.get('one'))
2724
# Let the lock be released
2728
self.assertEquals('c2', c2.get('one'))
2730
def test_read_while_writing(self):
2732
# We spawn a thread that will pause *during* the write
2733
ready_to_write = threading.Event()
2734
do_writing = threading.Event()
2735
writing_done = threading.Event()
2736
# We override the _save implementation so we know the store is locked
2737
c1_save_without_locking_orig = c1.store.save_without_locking
2738
def c1_save_without_locking():
2739
ready_to_write.set()
2740
# The lock is held. We wait for the main thread to decide when to
2743
c1_save_without_locking_orig()
2745
c1.store.save_without_locking = c1_save_without_locking
2748
t1 = threading.Thread(target=c1_set)
2749
# Collect the thread after the test
2750
self.addCleanup(t1.join)
2751
# Be ready to unblock the thread if the test goes wrong
2752
self.addCleanup(do_writing.set)
2754
# Ensure the thread is ready to write
2755
ready_to_write.wait()
2756
self.assertEquals('c1', c1.get('one'))
2757
# If we read during the write, we get the old value
2758
c2 = self.get_stack(self)
2759
self.assertEquals('1', c2.get('one'))
2760
# Let the writing occur and ensure it occurred
2763
# Now we get the updated value
2764
c3 = self.get_stack(self)
2765
self.assertEquals('c1', c3.get('one'))
2767
# FIXME: It may be worth looking into removing the lock dir when it's not
2768
# needed anymore and look at possible fallouts for concurrent lockers. This
2769
# will matter if/when we use config files outside of bazaar directories
2770
# (.bazaar or .bzr) -- vila 20110-04-11
2773
class TestSectionMatcher(TestStore):
2775
scenarios = [('location', {'matcher': config.LocationMatcher})]
2777
def get_store(self, file_name):
2778
return config.IniFileStore(self.get_readonly_transport(), file_name)
2780
def test_no_matches_for_empty_stores(self):
2781
store = self.get_store('foo.conf')
2782
store._load_from_string('')
2783
matcher = self.matcher(store, '/bar')
2784
self.assertEquals([], list(matcher.get_sections()))
2786
def test_build_doesnt_load_store(self):
2787
store = self.get_store('foo.conf')
2788
matcher = self.matcher(store, '/bar')
2789
self.assertFalse(store.is_loaded())
2792
class TestLocationSection(tests.TestCase):
2794
def get_section(self, options, extra_path):
2795
section = config.Section('foo', options)
2796
# We don't care about the length so we use '0'
2797
return config.LocationSection(section, 0, extra_path)
2799
def test_simple_option(self):
2800
section = self.get_section({'foo': 'bar'}, '')
2801
self.assertEquals('bar', section.get('foo'))
2803
def test_option_with_extra_path(self):
2804
section = self.get_section({'foo': 'bar', 'foo:policy': 'appendpath'},
2806
self.assertEquals('bar/baz', section.get('foo'))
2808
def test_invalid_policy(self):
2809
section = self.get_section({'foo': 'bar', 'foo:policy': 'die'},
2811
# invalid policies are ignored
2812
self.assertEquals('bar', section.get('foo'))
2815
class TestLocationMatcher(TestStore):
2817
def get_store(self, file_name):
2818
return config.IniFileStore(self.get_readonly_transport(), file_name)
2820
def test_more_specific_sections_first(self):
2821
store = self.get_store('foo.conf')
2822
store._load_from_string('''
2828
self.assertEquals(['/foo', '/foo/bar'],
2829
[section.id for section in store.get_sections()])
2830
matcher = config.LocationMatcher(store, '/foo/bar/baz')
2831
sections = list(matcher.get_sections())
2832
self.assertEquals([3, 2],
2833
[section.length for section in sections])
2834
self.assertEquals(['/foo/bar', '/foo'],
2835
[section.id for section in sections])
2836
self.assertEquals(['baz', 'bar/baz'],
2837
[section.extra_path for section in sections])
2839
def test_appendpath_in_no_name_section(self):
2840
# It's a bit weird to allow appendpath in a no-name section, but
2841
# someone may found a use for it
2842
store = self.get_store('foo.conf')
2843
store._load_from_string('''
2845
foo:policy = appendpath
2847
matcher = config.LocationMatcher(store, 'dir/subdir')
2848
sections = list(matcher.get_sections())
2849
self.assertLength(1, sections)
2850
self.assertEquals('bar/dir/subdir', sections[0].get('foo'))
2852
def test_file_urls_are_normalized(self):
2853
store = self.get_store('foo.conf')
2854
if sys.platform == 'win32':
2855
expected_url = 'file:///C:/dir/subdir'
2856
expected_location = 'C:/dir/subdir'
2858
expected_url = 'file:///dir/subdir'
2859
expected_location = '/dir/subdir'
2860
matcher = config.LocationMatcher(store, expected_url)
2861
self.assertEquals(expected_location, matcher.location)
2864
class TestStackGet(tests.TestCase):
2866
# FIXME: This should be parametrized for all known Stack or dedicated
2867
# paramerized tests created to avoid bloating -- vila 2011-03-31
2869
def test_single_config_get(self):
2870
conf = dict(foo='bar')
2871
conf_stack = config.Stack([conf])
2872
self.assertEquals('bar', conf_stack.get('foo'))
2874
def test_get_with_registered_default_value(self):
2875
conf_stack = config.Stack([dict()])
2876
opt = config.Option('foo', default='bar')
2877
self.overrideAttr(config, 'option_registry', registry.Registry())
2878
config.option_registry.register('foo', opt)
2879
self.assertEquals('bar', conf_stack.get('foo'))
2881
def test_get_without_registered_default_value(self):
2882
conf_stack = config.Stack([dict()])
2883
opt = config.Option('foo')
2884
self.overrideAttr(config, 'option_registry', registry.Registry())
2885
config.option_registry.register('foo', opt)
2886
self.assertEquals(None, conf_stack.get('foo'))
2888
def test_get_without_default_value_for_not_registered(self):
2889
conf_stack = config.Stack([dict()])
2890
opt = config.Option('foo')
2891
self.overrideAttr(config, 'option_registry', registry.Registry())
2892
self.assertEquals(None, conf_stack.get('foo'))
2894
def test_get_first_definition(self):
2895
conf1 = dict(foo='bar')
2896
conf2 = dict(foo='baz')
2897
conf_stack = config.Stack([conf1, conf2])
2898
self.assertEquals('bar', conf_stack.get('foo'))
2900
def test_get_embedded_definition(self):
2901
conf1 = dict(yy='12')
2902
conf2 = config.Stack([dict(xx='42'), dict(foo='baz')])
2903
conf_stack = config.Stack([conf1, conf2])
2904
self.assertEquals('baz', conf_stack.get('foo'))
2906
def test_get_for_empty_section_callable(self):
2907
conf_stack = config.Stack([lambda : []])
2908
self.assertEquals(None, conf_stack.get('foo'))
2910
def test_get_for_broken_callable(self):
2911
# Trying to use and invalid callable raises an exception on first use
2912
conf_stack = config.Stack([lambda : object()])
2913
self.assertRaises(TypeError, conf_stack.get, 'foo')
2916
class TestStackWithTransport(tests.TestCaseWithTransport):
2918
scenarios = [(key, {'get_stack': builder}) for key, builder
2919
in config.test_stack_builder_registry.iteritems()]
2922
class TestConcreteStacks(TestStackWithTransport):
2924
def test_build_stack(self):
2925
# Just a smoke test to help debug builders
2926
stack = self.get_stack(self)
2929
class TestStackGet(TestStackWithTransport):
2931
def test_get_for_empty_stack(self):
2932
conf = self.get_stack(self)
2933
self.assertEquals(None, conf.get('foo'))
2935
def test_get_hook(self):
2936
conf = self.get_stack(self)
2937
conf.store._load_from_string('foo=bar')
2941
config.ConfigHooks.install_named_hook('get', hook, None)
2942
self.assertLength(0, calls)
2943
value = conf.get('foo')
2944
self.assertEquals('bar', value)
2945
self.assertLength(1, calls)
2946
self.assertEquals((conf, 'foo', 'bar'), calls[0])
2949
class TestStackSet(TestStackWithTransport):
2951
def test_simple_set(self):
2952
conf = self.get_stack(self)
2953
conf.store._load_from_string('foo=bar')
2954
self.assertEquals('bar', conf.get('foo'))
2955
conf.set('foo', 'baz')
2956
# Did we get it back ?
2957
self.assertEquals('baz', conf.get('foo'))
2959
def test_set_creates_a_new_section(self):
2960
conf = self.get_stack(self)
2961
conf.set('foo', 'baz')
2962
self.assertEquals, 'baz', conf.get('foo')
2964
def test_set_hook(self):
2968
config.ConfigHooks.install_named_hook('set', hook, None)
2969
self.assertLength(0, calls)
2970
conf = self.get_stack(self)
2971
conf.set('foo', 'bar')
2972
self.assertLength(1, calls)
2973
self.assertEquals((conf, 'foo', 'bar'), calls[0])
2976
class TestStackRemove(TestStackWithTransport):
2978
def test_remove_existing(self):
2979
conf = self.get_stack(self)
2980
conf.store._load_from_string('foo=bar')
2981
self.assertEquals('bar', conf.get('foo'))
2983
# Did we get it back ?
2984
self.assertEquals(None, conf.get('foo'))
2986
def test_remove_unknown(self):
2987
conf = self.get_stack(self)
2988
self.assertRaises(KeyError, conf.remove, 'I_do_not_exist')
2990
def test_remove_hook(self):
2994
config.ConfigHooks.install_named_hook('remove', hook, None)
2995
self.assertLength(0, calls)
2996
conf = self.get_stack(self)
2997
conf.store._load_from_string('foo=bar')
2999
self.assertLength(1, calls)
3000
self.assertEquals((conf, 'foo'), calls[0])
3003
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
3006
super(TestConfigGetOptions, self).setUp()
3007
create_configs(self)
3009
def test_no_variable(self):
3010
# Using branch should query branch, locations and bazaar
3011
self.assertOptions([], self.branch_config)
3013
def test_option_in_bazaar(self):
3014
self.bazaar_config.set_user_option('file', 'bazaar')
3015
self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
3018
def test_option_in_locations(self):
3019
self.locations_config.set_user_option('file', 'locations')
3021
[('file', 'locations', self.tree.basedir, 'locations')],
3022
self.locations_config)
3024
def test_option_in_branch(self):
3025
self.branch_config.set_user_option('file', 'branch')
3026
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
3029
def test_option_in_bazaar_and_branch(self):
3030
self.bazaar_config.set_user_option('file', 'bazaar')
3031
self.branch_config.set_user_option('file', 'branch')
3032
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
3033
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3036
def test_option_in_branch_and_locations(self):
3037
# Hmm, locations override branch :-/
3038
self.locations_config.set_user_option('file', 'locations')
3039
self.branch_config.set_user_option('file', 'branch')
3041
[('file', 'locations', self.tree.basedir, 'locations'),
3042
('file', 'branch', 'DEFAULT', 'branch'),],
3045
def test_option_in_bazaar_locations_and_branch(self):
3046
self.bazaar_config.set_user_option('file', 'bazaar')
3047
self.locations_config.set_user_option('file', 'locations')
3048
self.branch_config.set_user_option('file', 'branch')
3050
[('file', 'locations', self.tree.basedir, 'locations'),
3051
('file', 'branch', 'DEFAULT', 'branch'),
3052
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3056
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
3059
super(TestConfigRemoveOption, self).setUp()
3060
create_configs_with_file_option(self)
3062
def test_remove_in_locations(self):
3063
self.locations_config.remove_user_option('file', self.tree.basedir)
3065
[('file', 'branch', 'DEFAULT', 'branch'),
3066
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3069
def test_remove_in_branch(self):
3070
self.branch_config.remove_user_option('file')
3072
[('file', 'locations', self.tree.basedir, 'locations'),
3073
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3076
def test_remove_in_bazaar(self):
3077
self.bazaar_config.remove_user_option('file')
3079
[('file', 'locations', self.tree.basedir, 'locations'),
3080
('file', 'branch', 'DEFAULT', 'branch'),],
3084
class TestConfigGetSections(tests.TestCaseWithTransport):
3087
super(TestConfigGetSections, self).setUp()
3088
create_configs(self)
3090
def assertSectionNames(self, expected, conf, name=None):
3091
"""Check which sections are returned for a given config.
3093
If fallback configurations exist their sections can be included.
3095
:param expected: A list of section names.
3097
:param conf: The configuration that will be queried.
3099
:param name: An optional section name that will be passed to
3102
sections = list(conf._get_sections(name))
3103
self.assertLength(len(expected), sections)
3104
self.assertEqual(expected, [name for name, _, _ in sections])
3106
def test_bazaar_default_section(self):
3107
self.assertSectionNames(['DEFAULT'], self.bazaar_config)
3109
def test_locations_default_section(self):
3110
# No sections are defined in an empty file
3111
self.assertSectionNames([], self.locations_config)
3113
def test_locations_named_section(self):
3114
self.locations_config.set_user_option('file', 'locations')
3115
self.assertSectionNames([self.tree.basedir], self.locations_config)
3117
def test_locations_matching_sections(self):
3118
loc_config = self.locations_config
3119
loc_config.set_user_option('file', 'locations')
3120
# We need to cheat a bit here to create an option in sections above and
3121
# below the 'location' one.
3122
parser = loc_config._get_parser()
3123
# locations.cong deals with '/' ignoring native os.sep
3124
location_names = self.tree.basedir.split('/')
3125
parent = '/'.join(location_names[:-1])
3126
child = '/'.join(location_names + ['child'])
3128
parser[parent]['file'] = 'parent'
3130
parser[child]['file'] = 'child'
3131
self.assertSectionNames([self.tree.basedir, parent], loc_config)
3133
def test_branch_data_default_section(self):
3134
self.assertSectionNames([None],
3135
self.branch_config._get_branch_data_config())
3137
def test_branch_default_sections(self):
3138
# No sections are defined in an empty locations file
3139
self.assertSectionNames([None, 'DEFAULT'],
3141
# Unless we define an option
3142
self.branch_config._get_location_config().set_user_option(
3143
'file', 'locations')
3144
self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
3147
def test_bazaar_named_section(self):
3148
# We need to cheat as the API doesn't give direct access to sections
3149
# other than DEFAULT.
3150
self.bazaar_config.set_alias('bazaar', 'bzr')
3151
self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
3154
class TestAuthenticationConfigFile(tests.TestCase):
3155
"""Test the authentication.conf file matching"""
3157
def _got_user_passwd(self, expected_user, expected_password,
3158
config, *args, **kwargs):
3159
credentials = config.get_credentials(*args, **kwargs)
3160
if credentials is None:
3164
user = credentials['user']
3165
password = credentials['password']
3166
self.assertEquals(expected_user, user)
3167
self.assertEquals(expected_password, password)
3169
def test_empty_config(self):
3170
conf = config.AuthenticationConfig(_file=StringIO())
3171
self.assertEquals({}, conf._get_config())
3172
self._got_user_passwd(None, None, conf, 'http', 'foo.net')
3174
def test_non_utf8_config(self):
3175
conf = config.AuthenticationConfig(_file=StringIO(
3177
self.assertRaises(errors.ConfigContentError, conf._get_config)
3179
def test_missing_auth_section_header(self):
3180
conf = config.AuthenticationConfig(_file=StringIO('foo = bar'))
3181
self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
3183
def test_auth_section_header_not_closed(self):
3184
conf = config.AuthenticationConfig(_file=StringIO('[DEF'))
3185
self.assertRaises(errors.ParseConfigError, conf._get_config)
3187
def test_auth_value_not_boolean(self):
3188
conf = config.AuthenticationConfig(_file=StringIO(
3192
verify_certificates=askme # Error: Not a boolean
3194
self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
3196
def test_auth_value_not_int(self):
3197
conf = config.AuthenticationConfig(_file=StringIO(
3201
port=port # Error: Not an int
3203
self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
3205
def test_unknown_password_encoding(self):
3206
conf = config.AuthenticationConfig(_file=StringIO(
3210
password_encoding=unknown
3212
self.assertRaises(ValueError, conf.get_password,
3213
'ftp', 'foo.net', 'joe')
3215
def test_credentials_for_scheme_host(self):
3216
conf = config.AuthenticationConfig(_file=StringIO(
3217
"""# Identity on foo.net
3222
password=secret-pass
3225
self._got_user_passwd('joe', 'secret-pass', conf, 'ftp', 'foo.net')
3227
self._got_user_passwd(None, None, conf, 'http', 'foo.net')
3229
self._got_user_passwd(None, None, conf, 'ftp', 'bar.net')
3231
def test_credentials_for_host_port(self):
3232
conf = config.AuthenticationConfig(_file=StringIO(
3233
"""# Identity on foo.net
3239
password=secret-pass
3242
self._got_user_passwd('joe', 'secret-pass',
3243
conf, 'ftp', 'foo.net', port=10021)
3245
self._got_user_passwd(None, None, conf, 'ftp', 'foo.net')
3247
def test_for_matching_host(self):
3248
conf = config.AuthenticationConfig(_file=StringIO(
3249
"""# Identity on foo.net
3255
[sourceforge domain]
3262
self._got_user_passwd('georges', 'bendover',
3263
conf, 'bzr', 'foo.bzr.sf.net')
3265
self._got_user_passwd(None, None,
3266
conf, 'bzr', 'bbzr.sf.net')
3268
def test_for_matching_host_None(self):
3269
conf = config.AuthenticationConfig(_file=StringIO(
3270
"""# Identity on foo.net
3280
self._got_user_passwd('joe', 'joepass',
3281
conf, 'bzr', 'quux.net')
3282
# no host but different scheme
3283
self._got_user_passwd('georges', 'bendover',
3284
conf, 'ftp', 'quux.net')
3286
def test_credentials_for_path(self):
3287
conf = config.AuthenticationConfig(_file=StringIO(
3303
self._got_user_passwd(None, None,
3304
conf, 'http', host='bar.org', path='/dir3')
3306
self._got_user_passwd('georges', 'bendover',
3307
conf, 'http', host='bar.org', path='/dir2')
3309
self._got_user_passwd('jim', 'jimpass',
3310
conf, 'http', host='bar.org',path='/dir1/subdir')
3312
def test_credentials_for_user(self):
3313
conf = config.AuthenticationConfig(_file=StringIO(
3322
self._got_user_passwd('jim', 'jimpass',
3323
conf, 'http', 'bar.org')
3325
self._got_user_passwd('jim', 'jimpass',
3326
conf, 'http', 'bar.org', user='jim')
3327
# Don't get a different user if one is specified
3328
self._got_user_passwd(None, None,
3329
conf, 'http', 'bar.org', user='georges')
3331
def test_credentials_for_user_without_password(self):
3332
conf = config.AuthenticationConfig(_file=StringIO(
3339
# Get user but no password
3340
self._got_user_passwd('jim', None,
3341
conf, 'http', 'bar.org')
3343
def test_verify_certificates(self):
3344
conf = config.AuthenticationConfig(_file=StringIO(
3351
verify_certificates=False
3358
credentials = conf.get_credentials('https', 'bar.org')
3359
self.assertEquals(False, credentials.get('verify_certificates'))
3360
credentials = conf.get_credentials('https', 'foo.net')
3361
self.assertEquals(True, credentials.get('verify_certificates'))
3364
class TestAuthenticationStorage(tests.TestCaseInTempDir):
3366
def test_set_credentials(self):
3367
conf = config.AuthenticationConfig()
3368
conf.set_credentials('name', 'host', 'user', 'scheme', 'password',
3369
99, path='/foo', verify_certificates=False, realm='realm')
3370
credentials = conf.get_credentials(host='host', scheme='scheme',
3371
port=99, path='/foo',
3373
CREDENTIALS = {'name': 'name', 'user': 'user', 'password': 'password',
3374
'verify_certificates': False, 'scheme': 'scheme',
3375
'host': 'host', 'port': 99, 'path': '/foo',
3377
self.assertEqual(CREDENTIALS, credentials)
3378
credentials_from_disk = config.AuthenticationConfig().get_credentials(
3379
host='host', scheme='scheme', port=99, path='/foo', realm='realm')
3380
self.assertEqual(CREDENTIALS, credentials_from_disk)
3382
def test_reset_credentials_different_name(self):
3383
conf = config.AuthenticationConfig()
3384
conf.set_credentials('name', 'host', 'user', 'scheme', 'password'),
3385
conf.set_credentials('name2', 'host', 'user2', 'scheme', 'password'),
3386
self.assertIs(None, conf._get_config().get('name'))
3387
credentials = conf.get_credentials(host='host', scheme='scheme')
3388
CREDENTIALS = {'name': 'name2', 'user': 'user2', 'password':
3389
'password', 'verify_certificates': True,
3390
'scheme': 'scheme', 'host': 'host', 'port': None,
3391
'path': None, 'realm': None}
3392
self.assertEqual(CREDENTIALS, credentials)
3395
class TestAuthenticationConfig(tests.TestCase):
3396
"""Test AuthenticationConfig behaviour"""
3398
def _check_default_password_prompt(self, expected_prompt_format, scheme,
3399
host=None, port=None, realm=None,
3403
user, password = 'jim', 'precious'
3404
expected_prompt = expected_prompt_format % {
3405
'scheme': scheme, 'host': host, 'port': port,
3406
'user': user, 'realm': realm}
3408
stdout = tests.StringIOWrapper()
3409
stderr = tests.StringIOWrapper()
3410
ui.ui_factory = tests.TestUIFactory(stdin=password + '\n',
3411
stdout=stdout, stderr=stderr)
3412
# We use an empty conf so that the user is always prompted
3413
conf = config.AuthenticationConfig()
3414
self.assertEquals(password,
3415
conf.get_password(scheme, host, user, port=port,
3416
realm=realm, path=path))
3417
self.assertEquals(expected_prompt, stderr.getvalue())
3418
self.assertEquals('', stdout.getvalue())
3420
def _check_default_username_prompt(self, expected_prompt_format, scheme,
3421
host=None, port=None, realm=None,
3426
expected_prompt = expected_prompt_format % {
3427
'scheme': scheme, 'host': host, 'port': port,
3429
stdout = tests.StringIOWrapper()
3430
stderr = tests.StringIOWrapper()
3431
ui.ui_factory = tests.TestUIFactory(stdin=username+ '\n',
3432
stdout=stdout, stderr=stderr)
3433
# We use an empty conf so that the user is always prompted
3434
conf = config.AuthenticationConfig()
3435
self.assertEquals(username, conf.get_user(scheme, host, port=port,
3436
realm=realm, path=path, ask=True))
3437
self.assertEquals(expected_prompt, stderr.getvalue())
3438
self.assertEquals('', stdout.getvalue())
3440
def test_username_defaults_prompts(self):
3441
# HTTP prompts can't be tested here, see test_http.py
3442
self._check_default_username_prompt(u'FTP %(host)s username: ', 'ftp')
3443
self._check_default_username_prompt(
3444
u'FTP %(host)s:%(port)d username: ', 'ftp', port=10020)
3445
self._check_default_username_prompt(
3446
u'SSH %(host)s:%(port)d username: ', 'ssh', port=12345)
3448
def test_username_default_no_prompt(self):
3449
conf = config.AuthenticationConfig()
3450
self.assertEquals(None,
3451
conf.get_user('ftp', 'example.com'))
3452
self.assertEquals("explicitdefault",
3453
conf.get_user('ftp', 'example.com', default="explicitdefault"))
3455
def test_password_default_prompts(self):
3456
# HTTP prompts can't be tested here, see test_http.py
3457
self._check_default_password_prompt(
3458
u'FTP %(user)s@%(host)s password: ', 'ftp')
3459
self._check_default_password_prompt(
3460
u'FTP %(user)s@%(host)s:%(port)d password: ', 'ftp', port=10020)
3461
self._check_default_password_prompt(
3462
u'SSH %(user)s@%(host)s:%(port)d password: ', 'ssh', port=12345)
3463
# SMTP port handling is a bit special (it's handled if embedded in the
3465
# FIXME: should we: forbid that, extend it to other schemes, leave
3466
# things as they are that's fine thank you ?
3467
self._check_default_password_prompt(
3468
u'SMTP %(user)s@%(host)s password: ', 'smtp')
3469
self._check_default_password_prompt(
3470
u'SMTP %(user)s@%(host)s password: ', 'smtp', host='bar.org:10025')
3471
self._check_default_password_prompt(
3472
u'SMTP %(user)s@%(host)s:%(port)d password: ', 'smtp', port=10025)
3474
def test_ssh_password_emits_warning(self):
3475
conf = config.AuthenticationConfig(_file=StringIO(
3483
entered_password = 'typed-by-hand'
3484
stdout = tests.StringIOWrapper()
3485
stderr = tests.StringIOWrapper()
3486
ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
3487
stdout=stdout, stderr=stderr)
3489
# Since the password defined in the authentication config is ignored,
3490
# the user is prompted
3491
self.assertEquals(entered_password,
3492
conf.get_password('ssh', 'bar.org', user='jim'))
3493
self.assertContainsRe(
3495
'password ignored in section \[ssh with password\]')
3497
def test_ssh_without_password_doesnt_emit_warning(self):
3498
conf = config.AuthenticationConfig(_file=StringIO(
3505
entered_password = 'typed-by-hand'
3506
stdout = tests.StringIOWrapper()
3507
stderr = tests.StringIOWrapper()
3508
ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
3512
# Since the password defined in the authentication config is ignored,
3513
# the user is prompted
3514
self.assertEquals(entered_password,
3515
conf.get_password('ssh', 'bar.org', user='jim'))
3516
# No warning shoud be emitted since there is no password. We are only
3518
self.assertNotContainsRe(
3520
'password ignored in section \[ssh with password\]')
3522
def test_uses_fallback_stores(self):
3523
self.overrideAttr(config, 'credential_store_registry',
3524
config.CredentialStoreRegistry())
3525
store = StubCredentialStore()
3526
store.add_credentials("http", "example.com", "joe", "secret")
3527
config.credential_store_registry.register("stub", store, fallback=True)
3528
conf = config.AuthenticationConfig(_file=StringIO())
3529
creds = conf.get_credentials("http", "example.com")
3530
self.assertEquals("joe", creds["user"])
3531
self.assertEquals("secret", creds["password"])
3534
class StubCredentialStore(config.CredentialStore):
3540
def add_credentials(self, scheme, host, user, password=None):
3541
self._username[(scheme, host)] = user
3542
self._password[(scheme, host)] = password
3544
def get_credentials(self, scheme, host, port=None, user=None,
3545
path=None, realm=None):
3546
key = (scheme, host)
3547
if not key in self._username:
3549
return { "scheme": scheme, "host": host, "port": port,
3550
"user": self._username[key], "password": self._password[key]}
3553
class CountingCredentialStore(config.CredentialStore):
3558
def get_credentials(self, scheme, host, port=None, user=None,
3559
path=None, realm=None):
3564
class TestCredentialStoreRegistry(tests.TestCase):
3566
def _get_cs_registry(self):
3567
return config.credential_store_registry
3569
def test_default_credential_store(self):
3570
r = self._get_cs_registry()
3571
default = r.get_credential_store(None)
3572
self.assertIsInstance(default, config.PlainTextCredentialStore)
3574
def test_unknown_credential_store(self):
3575
r = self._get_cs_registry()
3576
# It's hard to imagine someone creating a credential store named
3577
# 'unknown' so we use that as an never registered key.
3578
self.assertRaises(KeyError, r.get_credential_store, 'unknown')
3580
def test_fallback_none_registered(self):
3581
r = config.CredentialStoreRegistry()
3582
self.assertEquals(None,
3583
r.get_fallback_credentials("http", "example.com"))
3585
def test_register(self):
3586
r = config.CredentialStoreRegistry()
3587
r.register("stub", StubCredentialStore(), fallback=False)
3588
r.register("another", StubCredentialStore(), fallback=True)
3589
self.assertEquals(["another", "stub"], r.keys())
3591
def test_register_lazy(self):
3592
r = config.CredentialStoreRegistry()
3593
r.register_lazy("stub", "bzrlib.tests.test_config",
3594
"StubCredentialStore", fallback=False)
3595
self.assertEquals(["stub"], r.keys())
3596
self.assertIsInstance(r.get_credential_store("stub"),
3597
StubCredentialStore)
3599
def test_is_fallback(self):
3600
r = config.CredentialStoreRegistry()
3601
r.register("stub1", None, fallback=False)
3602
r.register("stub2", None, fallback=True)
3603
self.assertEquals(False, r.is_fallback("stub1"))
3604
self.assertEquals(True, r.is_fallback("stub2"))
3606
def test_no_fallback(self):
3607
r = config.CredentialStoreRegistry()
3608
store = CountingCredentialStore()
3609
r.register("count", store, fallback=False)
3610
self.assertEquals(None,
3611
r.get_fallback_credentials("http", "example.com"))
3612
self.assertEquals(0, store._calls)
3614
def test_fallback_credentials(self):
3615
r = config.CredentialStoreRegistry()
3616
store = StubCredentialStore()
3617
store.add_credentials("http", "example.com",
3618
"somebody", "geheim")
3619
r.register("stub", store, fallback=True)
3620
creds = r.get_fallback_credentials("http", "example.com")
3621
self.assertEquals("somebody", creds["user"])
3622
self.assertEquals("geheim", creds["password"])
3624
def test_fallback_first_wins(self):
3625
r = config.CredentialStoreRegistry()
3626
stub1 = StubCredentialStore()
3627
stub1.add_credentials("http", "example.com",
3628
"somebody", "stub1")
3629
r.register("stub1", stub1, fallback=True)
3630
stub2 = StubCredentialStore()
3631
stub2.add_credentials("http", "example.com",
3632
"somebody", "stub2")
3633
r.register("stub2", stub1, fallback=True)
3634
creds = r.get_fallback_credentials("http", "example.com")
3635
self.assertEquals("somebody", creds["user"])
3636
self.assertEquals("stub1", creds["password"])
3639
class TestPlainTextCredentialStore(tests.TestCase):
3641
def test_decode_password(self):
3642
r = config.credential_store_registry
3643
plain_text = r.get_credential_store()
3644
decoded = plain_text.decode_password(dict(password='secret'))
3645
self.assertEquals('secret', decoded)
3648
# FIXME: Once we have a way to declare authentication to all test servers, we
3649
# can implement generic tests.
3650
# test_user_password_in_url
3651
# test_user_in_url_password_from_config
3652
# test_user_in_url_password_prompted
3653
# test_user_in_config
3654
# test_user_getpass.getuser
3655
# test_user_prompted ?
3656
class TestAuthenticationRing(tests.TestCaseWithTransport):
3660
class TestAutoUserId(tests.TestCase):
3661
"""Test inferring an automatic user name."""
3663
def test_auto_user_id(self):
3664
"""Automatic inference of user name.
3666
This is a bit hard to test in an isolated way, because it depends on
3667
system functions that go direct to /etc or perhaps somewhere else.
3668
But it's reasonable to say that on Unix, with an /etc/mailname, we ought
3669
to be able to choose a user name with no configuration.
3671
if sys.platform == 'win32':
3672
raise tests.TestSkipped(
3673
"User name inference not implemented on win32")
3674
realname, address = config._auto_user_id()
3675
if os.path.exists('/etc/mailname'):
3676
self.assertIsNot(None, realname)
3677
self.assertIsNot(None, address)
3679
self.assertEquals((None, None), (realname, address))