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
42
from bzrlib.symbol_versioning import (
45
from bzrlib.transport import remote as transport_remote
46
from bzrlib.tests import (
51
from bzrlib.util.configobj import configobj
54
def lockable_config_scenarios():
57
{'config_class': config.GlobalConfig,
59
'config_section': 'DEFAULT'}),
61
{'config_class': config.LocationConfig,
63
'config_section': '.'}),]
66
load_tests = scenarios.load_tests_apply_scenarios
68
# Register helpers to build stores
69
config.test_store_builder_registry.register(
70
'configobj', lambda test: config.TransportIniFileStore(
71
test.get_transport(), 'configobj.conf'))
72
config.test_store_builder_registry.register(
73
'bazaar', lambda test: config.GlobalStore())
74
config.test_store_builder_registry.register(
75
'location', lambda test: config.LocationStore())
78
def build_backing_branch(test, relpath,
79
transport_class=None, server_class=None):
80
"""Test helper to create a backing branch only once.
82
Some tests needs multiple stores/stacks to check concurrent update
83
behaviours. As such, they need to build different branch *objects* even if
84
they share the branch on disk.
86
:param relpath: The relative path to the branch. (Note that the helper
87
should always specify the same relpath).
89
:param transport_class: The Transport class the test needs to use.
91
:param server_class: The server associated with the ``transport_class``
94
Either both or neither of ``transport_class`` and ``server_class`` should
97
if transport_class is not None and server_class is not None:
98
test.transport_class = transport_class
99
test.transport_server = server_class
100
elif not (transport_class is None and server_class is None):
101
raise AssertionError('Specify both ``transport_class`` and '
102
'``server_class`` or neither of them')
103
if getattr(test, 'backing_branch', None) is None:
104
# First call, let's build the branch on disk
105
test.backing_branch = test.make_branch(relpath)
108
def build_branch_store(test):
109
build_backing_branch(test, 'branch')
110
b = branch.Branch.open('branch')
111
return config.BranchStore(b)
112
config.test_store_builder_registry.register('branch', build_branch_store)
115
def build_control_store(test):
116
build_backing_branch(test, 'branch')
117
b = bzrdir.BzrDir.open('branch')
118
return config.ControlStore(b)
119
config.test_store_builder_registry.register('control', build_control_store)
122
def build_remote_branch_store(test):
123
# There is only one permutation (but we won't be able to handle more with
124
# this design anyway)
126
server_class) = transport_remote.get_test_permutations()[0]
127
build_backing_branch(test, 'branch', transport_class, server_class)
128
b = branch.Branch.open(test.get_url('branch'))
129
return config.BranchStore(b)
130
config.test_store_builder_registry.register('remote_branch',
131
build_remote_branch_store)
134
config.test_stack_builder_registry.register(
135
'bazaar', lambda test: config.GlobalStack())
136
config.test_stack_builder_registry.register(
137
'location', lambda test: config.LocationStack('.'))
140
def build_branch_stack(test):
141
build_backing_branch(test, 'branch')
142
b = branch.Branch.open('branch')
143
return config.BranchStack(b)
144
config.test_stack_builder_registry.register('branch', build_branch_stack)
147
def build_remote_branch_stack(test):
148
# There is only one permutation (but we won't be able to handle more with
149
# this design anyway)
151
server_class) = transport_remote.get_test_permutations()[0]
152
build_backing_branch(test, 'branch', transport_class, server_class)
153
b = branch.Branch.open(test.get_url('branch'))
154
return config.RemoteBranchStack(b)
155
config.test_stack_builder_registry.register('remote_branch',
156
build_remote_branch_stack)
158
def build_remote_control_stack(test):
159
# There is only one permutation (but we won't be able to handle more with
160
# this design anyway)
162
server_class) = transport_remote.get_test_permutations()[0]
163
# We need only a bzrdir for this, not a full branch, but it's not worth
164
# creating a dedicated helper to create only the bzrdir
165
build_backing_branch(test, 'branch', transport_class, server_class)
166
b = branch.Branch.open(test.get_url('branch'))
167
return config.RemoteControlStack(b.bzrdir)
168
config.test_stack_builder_registry.register('remote_control',
169
build_remote_control_stack)
172
sample_long_alias="log -r-15..-1 --line"
173
sample_config_text = u"""
175
email=Erik B\u00e5gfors <erik@bagfors.nu>
177
change_editor=vimdiff -of @new_path @old_path
178
gpg_signing_command=gnome-gpg
179
gpg_signing_key=DD4D5088
181
validate_signatures_in_log=true
183
user_global_option=something
184
bzr.mergetool.sometool=sometool {base} {this} {other} -o {result}
185
bzr.mergetool.funkytool=funkytool "arg with spaces" {this_temp}
186
bzr.mergetool.newtool='"newtool with spaces" {this_temp}'
187
bzr.default_mergetool=sometool
190
ll=""" + sample_long_alias + "\n"
193
sample_always_signatures = """
195
check_signatures=ignore
196
create_signatures=always
199
sample_ignore_signatures = """
201
check_signatures=require
202
create_signatures=never
205
sample_maybe_signatures = """
207
check_signatures=ignore
208
create_signatures=when-required
211
sample_branches_text = """
212
[http://www.example.com]
214
email=Robert Collins <robertc@example.org>
215
normal_option = normal
216
appendpath_option = append
217
appendpath_option:policy = appendpath
218
norecurse_option = norecurse
219
norecurse_option:policy = norecurse
220
[http://www.example.com/ignoreparent]
221
# different project: ignore parent dir config
223
[http://www.example.com/norecurse]
224
# configuration items that only apply to this dir
226
normal_option = norecurse
227
[http://www.example.com/dir]
228
appendpath_option = normal
230
check_signatures=require
231
# test trailing / matching with no children
233
check_signatures=check-available
234
gpg_signing_command=false
235
gpg_signing_key=default
236
user_local_option=local
237
# test trailing / matching
239
#subdirs will match but not the parent
241
check_signatures=ignore
242
post_commit=bzrlib.tests.test_config.post_commit
243
#testing explicit beats globs
247
def create_configs(test):
248
"""Create configuration files for a given test.
250
This requires creating a tree (and populate the ``test.tree`` attribute)
251
and its associated branch and will populate the following attributes:
253
- branch_config: A BranchConfig for the associated branch.
255
- locations_config : A LocationConfig for the associated branch
257
- bazaar_config: A GlobalConfig.
259
The tree and branch are created in a 'tree' subdirectory so the tests can
260
still use the test directory to stay outside of the branch.
262
tree = test.make_branch_and_tree('tree')
264
test.branch_config = config.BranchConfig(tree.branch)
265
test.locations_config = config.LocationConfig(tree.basedir)
266
test.bazaar_config = config.GlobalConfig()
269
def create_configs_with_file_option(test):
270
"""Create configuration files with a ``file`` option set in each.
272
This builds on ``create_configs`` and add one ``file`` option in each
273
configuration with a value which allows identifying the configuration file.
276
test.bazaar_config.set_user_option('file', 'bazaar')
277
test.locations_config.set_user_option('file', 'locations')
278
test.branch_config.set_user_option('file', 'branch')
281
class TestOptionsMixin:
283
def assertOptions(self, expected, conf):
284
# We don't care about the parser (as it will make tests hard to write
285
# and error-prone anyway)
286
self.assertThat([opt[:4] for opt in conf._get_options()],
287
matchers.Equals(expected))
290
class InstrumentedConfigObj(object):
291
"""A config obj look-enough-alike to record calls made to it."""
293
def __contains__(self, thing):
294
self._calls.append(('__contains__', thing))
297
def __getitem__(self, key):
298
self._calls.append(('__getitem__', key))
301
def __init__(self, input, encoding=None):
302
self._calls = [('__init__', input, encoding)]
304
def __setitem__(self, key, value):
305
self._calls.append(('__setitem__', key, value))
307
def __delitem__(self, key):
308
self._calls.append(('__delitem__', key))
311
self._calls.append(('keys',))
315
self._calls.append(('reload',))
317
def write(self, arg):
318
self._calls.append(('write',))
320
def as_bool(self, value):
321
self._calls.append(('as_bool', value))
324
def get_value(self, section, name):
325
self._calls.append(('get_value', section, name))
329
class FakeBranch(object):
331
def __init__(self, base=None, user_id=None):
333
self.base = "http://example.com/branches/demo"
336
self._transport = self.control_files = \
337
FakeControlFilesAndTransport(user_id=user_id)
339
def _get_config(self):
340
return config.TransportConfig(self._transport, 'branch.conf')
342
def lock_write(self):
349
class FakeControlFilesAndTransport(object):
351
def __init__(self, user_id=None):
354
self.files['email'] = user_id
355
self._transport = self
357
def get_utf8(self, filename):
359
raise AssertionError("get_utf8 should no longer be used")
361
def get(self, filename):
364
return StringIO(self.files[filename])
366
raise errors.NoSuchFile(filename)
368
def get_bytes(self, filename):
371
return self.files[filename]
373
raise errors.NoSuchFile(filename)
375
def put(self, filename, fileobj):
376
self.files[filename] = fileobj.read()
378
def put_file(self, filename, fileobj):
379
return self.put(filename, fileobj)
382
class InstrumentedConfig(config.Config):
383
"""An instrumented config that supplies stubs for template methods."""
386
super(InstrumentedConfig, self).__init__()
388
self._signatures = config.CHECK_NEVER
390
def _get_user_id(self):
391
self._calls.append('_get_user_id')
392
return "Robert Collins <robert.collins@example.org>"
394
def _get_signature_checking(self):
395
self._calls.append('_get_signature_checking')
396
return self._signatures
398
def _get_change_editor(self):
399
self._calls.append('_get_change_editor')
400
return 'vimdiff -fo @new_path @old_path'
403
bool_config = """[DEFAULT]
412
class TestConfigObj(tests.TestCase):
414
def test_get_bool(self):
415
co = config.ConfigObj(StringIO(bool_config))
416
self.assertIs(co.get_bool('DEFAULT', 'active'), True)
417
self.assertIs(co.get_bool('DEFAULT', 'inactive'), False)
418
self.assertIs(co.get_bool('UPPERCASE', 'active'), True)
419
self.assertIs(co.get_bool('UPPERCASE', 'nonactive'), False)
421
def test_hash_sign_in_value(self):
423
Before 4.5.0, ConfigObj did not quote # signs in values, so they'd be
424
treated as comments when read in again. (#86838)
426
co = config.ConfigObj()
427
co['test'] = 'foo#bar'
429
co.write(outfile=outfile)
430
lines = outfile.getvalue().splitlines()
431
self.assertEqual(lines, ['test = "foo#bar"'])
432
co2 = config.ConfigObj(lines)
433
self.assertEqual(co2['test'], 'foo#bar')
435
def test_triple_quotes(self):
436
# Bug #710410: if the value string has triple quotes
437
# then ConfigObj versions up to 4.7.2 will quote them wrong
438
# and won't able to read them back
439
triple_quotes_value = '''spam
440
""" that's my spam """
442
co = config.ConfigObj()
443
co['test'] = triple_quotes_value
444
# While writing this test another bug in ConfigObj has been found:
445
# method co.write() without arguments produces list of lines
446
# one option per line, and multiline values are not split
447
# across multiple lines,
448
# and that breaks the parsing these lines back by ConfigObj.
449
# This issue only affects test, but it's better to avoid
450
# `co.write()` construct at all.
451
# [bialix 20110222] bug report sent to ConfigObj's author
453
co.write(outfile=outfile)
454
output = outfile.getvalue()
455
# now we're trying to read it back
456
co2 = config.ConfigObj(StringIO(output))
457
self.assertEquals(triple_quotes_value, co2['test'])
460
erroneous_config = """[section] # line 1
463
whocares=notme # line 4
467
class TestConfigObjErrors(tests.TestCase):
469
def test_duplicate_section_name_error_line(self):
471
co = configobj.ConfigObj(StringIO(erroneous_config),
473
except config.configobj.DuplicateError, e:
474
self.assertEqual(3, e.line_number)
476
self.fail('Error in config file not detected')
479
class TestConfig(tests.TestCase):
481
def test_constructs(self):
484
def test_no_default_editor(self):
487
self.applyDeprecated, deprecated_in((2, 4, 0)),
488
config.Config().get_editor)
490
def test_user_email(self):
491
my_config = InstrumentedConfig()
492
self.assertEqual('robert.collins@example.org', my_config.user_email())
493
self.assertEqual(['_get_user_id'], my_config._calls)
495
def test_username(self):
496
my_config = InstrumentedConfig()
497
self.assertEqual('Robert Collins <robert.collins@example.org>',
498
my_config.username())
499
self.assertEqual(['_get_user_id'], my_config._calls)
501
def test_signatures_default(self):
502
my_config = config.Config()
503
self.assertFalse(my_config.signature_needed())
504
self.assertEqual(config.CHECK_IF_POSSIBLE,
505
my_config.signature_checking())
506
self.assertEqual(config.SIGN_WHEN_REQUIRED,
507
my_config.signing_policy())
509
def test_signatures_template_method(self):
510
my_config = InstrumentedConfig()
511
self.assertEqual(config.CHECK_NEVER, my_config.signature_checking())
512
self.assertEqual(['_get_signature_checking'], my_config._calls)
514
def test_signatures_template_method_none(self):
515
my_config = InstrumentedConfig()
516
my_config._signatures = None
517
self.assertEqual(config.CHECK_IF_POSSIBLE,
518
my_config.signature_checking())
519
self.assertEqual(['_get_signature_checking'], my_config._calls)
521
def test_gpg_signing_command_default(self):
522
my_config = config.Config()
523
self.assertEqual('gpg', my_config.gpg_signing_command())
525
def test_get_user_option_default(self):
526
my_config = config.Config()
527
self.assertEqual(None, my_config.get_user_option('no_option'))
529
def test_post_commit_default(self):
530
my_config = config.Config()
531
self.assertEqual(None, my_config.post_commit())
533
def test_log_format_default(self):
534
my_config = config.Config()
535
self.assertEqual('long', my_config.log_format())
537
def test_acceptable_keys_default(self):
538
my_config = config.Config()
539
self.assertEqual(None, my_config.acceptable_keys())
541
def test_validate_signatures_in_log_default(self):
542
my_config = config.Config()
543
self.assertEqual(False, my_config.validate_signatures_in_log())
545
def test_get_change_editor(self):
546
my_config = InstrumentedConfig()
547
change_editor = my_config.get_change_editor('old_tree', 'new_tree')
548
self.assertEqual(['_get_change_editor'], my_config._calls)
549
self.assertIs(diff.DiffFromTool, change_editor.__class__)
550
self.assertEqual(['vimdiff', '-fo', '@new_path', '@old_path'],
551
change_editor.command_template)
554
class TestConfigPath(tests.TestCase):
557
super(TestConfigPath, self).setUp()
558
self.overrideEnv('HOME', '/home/bogus')
559
self.overrideEnv('XDG_CACHE_DIR', '')
560
if sys.platform == 'win32':
562
'BZR_HOME', r'C:\Documents and Settings\bogus\Application Data')
564
'C:/Documents and Settings/bogus/Application Data/bazaar/2.0'
566
self.bzr_home = '/home/bogus/.bazaar'
568
def test_config_dir(self):
569
self.assertEqual(config.config_dir(), self.bzr_home)
571
def test_config_filename(self):
572
self.assertEqual(config.config_filename(),
573
self.bzr_home + '/bazaar.conf')
575
def test_locations_config_filename(self):
576
self.assertEqual(config.locations_config_filename(),
577
self.bzr_home + '/locations.conf')
579
def test_authentication_config_filename(self):
580
self.assertEqual(config.authentication_config_filename(),
581
self.bzr_home + '/authentication.conf')
583
def test_xdg_cache_dir(self):
584
self.assertEqual(config.xdg_cache_dir(),
585
'/home/bogus/.cache')
588
class TestXDGConfigDir(tests.TestCaseInTempDir):
589
# must be in temp dir because config tests for the existence of the bazaar
590
# subdirectory of $XDG_CONFIG_HOME
593
if sys.platform in ('darwin', 'win32'):
594
raise tests.TestNotApplicable(
595
'XDG config dir not used on this platform')
596
super(TestXDGConfigDir, self).setUp()
597
self.overrideEnv('HOME', self.test_home_dir)
598
# BZR_HOME overrides everything we want to test so unset it.
599
self.overrideEnv('BZR_HOME', None)
601
def test_xdg_config_dir_exists(self):
602
"""When ~/.config/bazaar exists, use it as the config dir."""
603
newdir = osutils.pathjoin(self.test_home_dir, '.config', 'bazaar')
605
self.assertEqual(config.config_dir(), newdir)
607
def test_xdg_config_home(self):
608
"""When XDG_CONFIG_HOME is set, use it."""
609
xdgconfigdir = osutils.pathjoin(self.test_home_dir, 'xdgconfig')
610
self.overrideEnv('XDG_CONFIG_HOME', xdgconfigdir)
611
newdir = osutils.pathjoin(xdgconfigdir, 'bazaar')
613
self.assertEqual(config.config_dir(), newdir)
616
class TestIniConfig(tests.TestCaseInTempDir):
618
def make_config_parser(self, s):
619
conf = config.IniBasedConfig.from_string(s)
620
return conf, conf._get_parser()
623
class TestIniConfigBuilding(TestIniConfig):
625
def test_contructs(self):
626
my_config = config.IniBasedConfig()
628
def test_from_fp(self):
629
my_config = config.IniBasedConfig.from_string(sample_config_text)
630
self.assertIsInstance(my_config._get_parser(), configobj.ConfigObj)
632
def test_cached(self):
633
my_config = config.IniBasedConfig.from_string(sample_config_text)
634
parser = my_config._get_parser()
635
self.assertTrue(my_config._get_parser() is parser)
637
def _dummy_chown(self, path, uid, gid):
638
self.path, self.uid, self.gid = path, uid, gid
640
def test_ini_config_ownership(self):
641
"""Ensure that chown is happening during _write_config_file"""
642
self.requireFeature(features.chown_feature)
643
self.overrideAttr(os, 'chown', self._dummy_chown)
644
self.path = self.uid = self.gid = None
645
conf = config.IniBasedConfig(file_name='./foo.conf')
646
conf._write_config_file()
647
self.assertEquals(self.path, './foo.conf')
648
self.assertTrue(isinstance(self.uid, int))
649
self.assertTrue(isinstance(self.gid, int))
651
def test_get_filename_parameter_is_deprecated_(self):
652
conf = self.callDeprecated([
653
'IniBasedConfig.__init__(get_filename) was deprecated in 2.3.'
654
' Use file_name instead.'],
655
config.IniBasedConfig, lambda: 'ini.conf')
656
self.assertEqual('ini.conf', conf.file_name)
658
def test_get_parser_file_parameter_is_deprecated_(self):
659
config_file = StringIO(sample_config_text.encode('utf-8'))
660
conf = config.IniBasedConfig.from_string(sample_config_text)
661
conf = self.callDeprecated([
662
'IniBasedConfig._get_parser(file=xxx) was deprecated in 2.3.'
663
' Use IniBasedConfig(_content=xxx) instead.'],
664
conf._get_parser, file=config_file)
667
class TestIniConfigSaving(tests.TestCaseInTempDir):
669
def test_cant_save_without_a_file_name(self):
670
conf = config.IniBasedConfig()
671
self.assertRaises(AssertionError, conf._write_config_file)
673
def test_saved_with_content(self):
674
content = 'foo = bar\n'
675
conf = config.IniBasedConfig.from_string(
676
content, file_name='./test.conf', save=True)
677
self.assertFileEqual(content, 'test.conf')
680
class TestIniConfigOptionExpansionDefaultValue(tests.TestCaseInTempDir):
681
"""What is the default value of expand for config options.
683
This is an opt-in beta feature used to evaluate whether or not option
684
references can appear in dangerous place raising exceptions, disapearing
685
(and as such corrupting data) or if it's safe to activate the option by
688
Note that these tests relies on config._expand_default_value being already
689
overwritten in the parent class setUp.
693
super(TestIniConfigOptionExpansionDefaultValue, self).setUp()
697
self.warnings.append(args[0] % args[1:])
698
self.overrideAttr(trace, 'warning', warning)
700
def get_config(self, expand):
701
c = config.GlobalConfig.from_string('bzr.config.expand=%s' % (expand,),
705
def assertExpandIs(self, expected):
706
actual = config._get_expand_default_value()
707
#self.config.get_user_option_as_bool('bzr.config.expand')
708
self.assertEquals(expected, actual)
710
def test_default_is_None(self):
711
self.assertEquals(None, config._expand_default_value)
713
def test_default_is_False_even_if_None(self):
714
self.config = self.get_config(None)
715
self.assertExpandIs(False)
717
def test_default_is_False_even_if_invalid(self):
718
self.config = self.get_config('<your choice>')
719
self.assertExpandIs(False)
721
# Huh ? My choice is False ? Thanks, always happy to hear that :D
722
# Wait, you've been warned !
723
self.assertLength(1, self.warnings)
725
'Value "<your choice>" is not a boolean for "bzr.config.expand"',
728
def test_default_is_True(self):
729
self.config = self.get_config(True)
730
self.assertExpandIs(True)
732
def test_default_is_False(self):
733
self.config = self.get_config(False)
734
self.assertExpandIs(False)
737
class TestIniConfigOptionExpansion(tests.TestCase):
738
"""Test option expansion from the IniConfig level.
740
What we really want here is to test the Config level, but the class being
741
abstract as far as storing values is concerned, this can't be done
744
# FIXME: This should be rewritten when all configs share a storage
745
# implementation -- vila 2011-02-18
747
def get_config(self, string=None):
750
c = config.IniBasedConfig.from_string(string)
753
def assertExpansion(self, expected, conf, string, env=None):
754
self.assertEquals(expected, conf.expand_options(string, env))
756
def test_no_expansion(self):
757
c = self.get_config('')
758
self.assertExpansion('foo', c, 'foo')
760
def test_env_adding_options(self):
761
c = self.get_config('')
762
self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
764
def test_env_overriding_options(self):
765
c = self.get_config('foo=baz')
766
self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
768
def test_simple_ref(self):
769
c = self.get_config('foo=xxx')
770
self.assertExpansion('xxx', c, '{foo}')
772
def test_unknown_ref(self):
773
c = self.get_config('')
774
self.assertRaises(errors.ExpandingUnknownOption,
775
c.expand_options, '{foo}')
777
def test_indirect_ref(self):
778
c = self.get_config('''
782
self.assertExpansion('xxx', c, '{bar}')
784
def test_embedded_ref(self):
785
c = self.get_config('''
789
self.assertExpansion('xxx', c, '{{bar}}')
791
def test_simple_loop(self):
792
c = self.get_config('foo={foo}')
793
self.assertRaises(errors.OptionExpansionLoop, c.expand_options, '{foo}')
795
def test_indirect_loop(self):
796
c = self.get_config('''
800
e = self.assertRaises(errors.OptionExpansionLoop,
801
c.expand_options, '{foo}')
802
self.assertEquals('foo->bar->baz', e.refs)
803
self.assertEquals('{foo}', e.string)
806
conf = self.get_config('''
810
list={foo},{bar},{baz}
812
self.assertEquals(['start', 'middle', 'end'],
813
conf.get_user_option('list', expand=True))
815
def test_cascading_list(self):
816
conf = self.get_config('''
822
self.assertEquals(['start', 'middle', 'end'],
823
conf.get_user_option('list', expand=True))
825
def test_pathological_hidden_list(self):
826
conf = self.get_config('''
832
hidden={start}{middle}{end}
834
# Nope, it's either a string or a list, and the list wins as soon as a
835
# ',' appears, so the string concatenation never occur.
836
self.assertEquals(['{foo', '}', '{', 'bar}'],
837
conf.get_user_option('hidden', expand=True))
840
class TestLocationConfigOptionExpansion(tests.TestCaseInTempDir):
842
def get_config(self, location, string=None):
845
# Since we don't save the config we won't strictly require to inherit
846
# from TestCaseInTempDir, but an error occurs so quickly...
847
c = config.LocationConfig.from_string(string, location)
850
def test_dont_cross_unrelated_section(self):
851
c = self.get_config('/another/branch/path','''
856
[/another/branch/path]
859
self.assertRaises(errors.ExpandingUnknownOption,
860
c.get_user_option, 'bar', expand=True)
862
def test_cross_related_sections(self):
863
c = self.get_config('/project/branch/path','''
867
[/project/branch/path]
870
self.assertEquals('quux', c.get_user_option('bar', expand=True))
873
class TestIniBaseConfigOnDisk(tests.TestCaseInTempDir):
875
def test_cannot_reload_without_name(self):
876
conf = config.IniBasedConfig.from_string(sample_config_text)
877
self.assertRaises(AssertionError, conf.reload)
879
def test_reload_see_new_value(self):
880
c1 = config.IniBasedConfig.from_string('editor=vim\n',
881
file_name='./test/conf')
882
c1._write_config_file()
883
c2 = config.IniBasedConfig.from_string('editor=emacs\n',
884
file_name='./test/conf')
885
c2._write_config_file()
886
self.assertEqual('vim', c1.get_user_option('editor'))
887
self.assertEqual('emacs', c2.get_user_option('editor'))
888
# Make sure we get the Right value
890
self.assertEqual('emacs', c1.get_user_option('editor'))
893
class TestLockableConfig(tests.TestCaseInTempDir):
895
scenarios = lockable_config_scenarios()
900
config_section = None
903
super(TestLockableConfig, self).setUp()
904
self._content = '[%s]\none=1\ntwo=2\n' % (self.config_section,)
905
self.config = self.create_config(self._content)
907
def get_existing_config(self):
908
return self.config_class(*self.config_args)
910
def create_config(self, content):
911
kwargs = dict(save=True)
912
c = self.config_class.from_string(content, *self.config_args, **kwargs)
915
def test_simple_read_access(self):
916
self.assertEquals('1', self.config.get_user_option('one'))
918
def test_simple_write_access(self):
919
self.config.set_user_option('one', 'one')
920
self.assertEquals('one', self.config.get_user_option('one'))
922
def test_listen_to_the_last_speaker(self):
924
c2 = self.get_existing_config()
925
c1.set_user_option('one', 'ONE')
926
c2.set_user_option('two', 'TWO')
927
self.assertEquals('ONE', c1.get_user_option('one'))
928
self.assertEquals('TWO', c2.get_user_option('two'))
929
# The second update respect the first one
930
self.assertEquals('ONE', c2.get_user_option('one'))
932
def test_last_speaker_wins(self):
933
# If the same config is not shared, the same variable modified twice
934
# can only see a single result.
936
c2 = self.get_existing_config()
937
c1.set_user_option('one', 'c1')
938
c2.set_user_option('one', 'c2')
939
self.assertEquals('c2', c2._get_user_option('one'))
940
# The first modification is still available until another refresh
942
self.assertEquals('c1', c1._get_user_option('one'))
943
c1.set_user_option('two', 'done')
944
self.assertEquals('c2', c1._get_user_option('one'))
946
def test_writes_are_serialized(self):
948
c2 = self.get_existing_config()
950
# We spawn a thread that will pause *during* the write
951
before_writing = threading.Event()
952
after_writing = threading.Event()
953
writing_done = threading.Event()
954
c1_orig = c1._write_config_file
955
def c1_write_config_file():
958
# The lock is held. We wait for the main thread to decide when to
961
c1._write_config_file = c1_write_config_file
963
c1.set_user_option('one', 'c1')
965
t1 = threading.Thread(target=c1_set_option)
966
# Collect the thread after the test
967
self.addCleanup(t1.join)
968
# Be ready to unblock the thread if the test goes wrong
969
self.addCleanup(after_writing.set)
971
before_writing.wait()
972
self.assertTrue(c1._lock.is_held)
973
self.assertRaises(errors.LockContention,
974
c2.set_user_option, 'one', 'c2')
975
self.assertEquals('c1', c1.get_user_option('one'))
976
# Let the lock be released
979
c2.set_user_option('one', 'c2')
980
self.assertEquals('c2', c2.get_user_option('one'))
982
def test_read_while_writing(self):
984
# We spawn a thread that will pause *during* the write
985
ready_to_write = threading.Event()
986
do_writing = threading.Event()
987
writing_done = threading.Event()
988
c1_orig = c1._write_config_file
989
def c1_write_config_file():
991
# The lock is held. We wait for the main thread to decide when to
996
c1._write_config_file = c1_write_config_file
998
c1.set_user_option('one', 'c1')
999
t1 = threading.Thread(target=c1_set_option)
1000
# Collect the thread after the test
1001
self.addCleanup(t1.join)
1002
# Be ready to unblock the thread if the test goes wrong
1003
self.addCleanup(do_writing.set)
1005
# Ensure the thread is ready to write
1006
ready_to_write.wait()
1007
self.assertTrue(c1._lock.is_held)
1008
self.assertEquals('c1', c1.get_user_option('one'))
1009
# If we read during the write, we get the old value
1010
c2 = self.get_existing_config()
1011
self.assertEquals('1', c2.get_user_option('one'))
1012
# Let the writing occur and ensure it occurred
1015
# Now we get the updated value
1016
c3 = self.get_existing_config()
1017
self.assertEquals('c1', c3.get_user_option('one'))
1020
class TestGetUserOptionAs(TestIniConfig):
1022
def test_get_user_option_as_bool(self):
1023
conf, parser = self.make_config_parser("""
1026
an_invalid_bool = maybe
1027
a_list = hmm, who knows ? # This is interpreted as a list !
1029
get_bool = conf.get_user_option_as_bool
1030
self.assertEqual(True, get_bool('a_true_bool'))
1031
self.assertEqual(False, get_bool('a_false_bool'))
1034
warnings.append(args[0] % args[1:])
1035
self.overrideAttr(trace, 'warning', warning)
1036
msg = 'Value "%s" is not a boolean for "%s"'
1037
self.assertIs(None, get_bool('an_invalid_bool'))
1038
self.assertEquals(msg % ('maybe', 'an_invalid_bool'), warnings[0])
1040
self.assertIs(None, get_bool('not_defined_in_this_config'))
1041
self.assertEquals([], warnings)
1043
def test_get_user_option_as_list(self):
1044
conf, parser = self.make_config_parser("""
1049
get_list = conf.get_user_option_as_list
1050
self.assertEqual(['a', 'b', 'c'], get_list('a_list'))
1051
self.assertEqual(['1'], get_list('length_1'))
1052
self.assertEqual('x', conf.get_user_option('one_item'))
1053
# automatically cast to list
1054
self.assertEqual(['x'], get_list('one_item'))
1056
def test_get_user_option_as_int_from_SI(self):
1057
conf, parser = self.make_config_parser("""
1066
get_si = conf.get_user_option_as_int_from_SI
1067
self.assertEqual(100, get_si('plain'))
1068
self.assertEqual(5000, get_si('si_k'))
1069
self.assertEqual(5000, get_si('si_kb'))
1070
self.assertEqual(5000000, get_si('si_m'))
1071
self.assertEqual(5000000, get_si('si_mb'))
1072
self.assertEqual(5000000000, get_si('si_g'))
1073
self.assertEqual(5000000000, get_si('si_gb'))
1074
self.assertEqual(None, get_si('non-exist'))
1075
self.assertEqual(42, get_si('non-exist-with-default', 42))
1077
class TestSupressWarning(TestIniConfig):
1079
def make_warnings_config(self, s):
1080
conf, parser = self.make_config_parser(s)
1081
return conf.suppress_warning
1083
def test_suppress_warning_unknown(self):
1084
suppress_warning = self.make_warnings_config('')
1085
self.assertEqual(False, suppress_warning('unknown_warning'))
1087
def test_suppress_warning_known(self):
1088
suppress_warning = self.make_warnings_config('suppress_warnings=a,b')
1089
self.assertEqual(False, suppress_warning('c'))
1090
self.assertEqual(True, suppress_warning('a'))
1091
self.assertEqual(True, suppress_warning('b'))
1094
class TestGetConfig(tests.TestCase):
1096
def test_constructs(self):
1097
my_config = config.GlobalConfig()
1099
def test_calls_read_filenames(self):
1100
# replace the class that is constructed, to check its parameters
1101
oldparserclass = config.ConfigObj
1102
config.ConfigObj = InstrumentedConfigObj
1103
my_config = config.GlobalConfig()
1105
parser = my_config._get_parser()
1107
config.ConfigObj = oldparserclass
1108
self.assertIsInstance(parser, InstrumentedConfigObj)
1109
self.assertEqual(parser._calls, [('__init__', config.config_filename(),
1113
class TestBranchConfig(tests.TestCaseWithTransport):
1115
def test_constructs(self):
1116
branch = FakeBranch()
1117
my_config = config.BranchConfig(branch)
1118
self.assertRaises(TypeError, config.BranchConfig)
1120
def test_get_location_config(self):
1121
branch = FakeBranch()
1122
my_config = config.BranchConfig(branch)
1123
location_config = my_config._get_location_config()
1124
self.assertEqual(branch.base, location_config.location)
1125
self.assertIs(location_config, my_config._get_location_config())
1127
def test_get_config(self):
1128
"""The Branch.get_config method works properly"""
1129
b = bzrdir.BzrDir.create_standalone_workingtree('.').branch
1130
my_config = b.get_config()
1131
self.assertIs(my_config.get_user_option('wacky'), None)
1132
my_config.set_user_option('wacky', 'unlikely')
1133
self.assertEqual(my_config.get_user_option('wacky'), 'unlikely')
1135
# Ensure we get the same thing if we start again
1136
b2 = branch.Branch.open('.')
1137
my_config2 = b2.get_config()
1138
self.assertEqual(my_config2.get_user_option('wacky'), 'unlikely')
1140
def test_has_explicit_nickname(self):
1141
b = self.make_branch('.')
1142
self.assertFalse(b.get_config().has_explicit_nickname())
1144
self.assertTrue(b.get_config().has_explicit_nickname())
1146
def test_config_url(self):
1147
"""The Branch.get_config will use section that uses a local url"""
1148
branch = self.make_branch('branch')
1149
self.assertEqual('branch', branch.nick)
1151
local_url = urlutils.local_path_to_url('branch')
1152
conf = config.LocationConfig.from_string(
1153
'[%s]\nnickname = foobar' % (local_url,),
1154
local_url, save=True)
1155
self.assertEqual('foobar', branch.nick)
1157
def test_config_local_path(self):
1158
"""The Branch.get_config will use a local system path"""
1159
branch = self.make_branch('branch')
1160
self.assertEqual('branch', branch.nick)
1162
local_path = osutils.getcwd().encode('utf8')
1163
conf = config.LocationConfig.from_string(
1164
'[%s/branch]\nnickname = barry' % (local_path,),
1165
'branch', save=True)
1166
self.assertEqual('barry', branch.nick)
1168
def test_config_creates_local(self):
1169
"""Creating a new entry in config uses a local path."""
1170
branch = self.make_branch('branch', format='knit')
1171
branch.set_push_location('http://foobar')
1172
local_path = osutils.getcwd().encode('utf8')
1173
# Surprisingly ConfigObj doesn't create a trailing newline
1174
self.check_file_contents(config.locations_config_filename(),
1176
'push_location = http://foobar\n'
1177
'push_location:policy = norecurse\n'
1180
def test_autonick_urlencoded(self):
1181
b = self.make_branch('!repo')
1182
self.assertEqual('!repo', b.get_config().get_nickname())
1184
def test_warn_if_masked(self):
1187
warnings.append(args[0] % args[1:])
1188
self.overrideAttr(trace, 'warning', warning)
1190
def set_option(store, warn_masked=True):
1192
conf.set_user_option('example_option', repr(store), store=store,
1193
warn_masked=warn_masked)
1194
def assertWarning(warning):
1196
self.assertEqual(0, len(warnings))
1198
self.assertEqual(1, len(warnings))
1199
self.assertEqual(warning, warnings[0])
1200
branch = self.make_branch('.')
1201
conf = branch.get_config()
1202
set_option(config.STORE_GLOBAL)
1204
set_option(config.STORE_BRANCH)
1206
set_option(config.STORE_GLOBAL)
1207
assertWarning('Value "4" is masked by "3" from branch.conf')
1208
set_option(config.STORE_GLOBAL, warn_masked=False)
1210
set_option(config.STORE_LOCATION)
1212
set_option(config.STORE_BRANCH)
1213
assertWarning('Value "3" is masked by "0" from locations.conf')
1214
set_option(config.STORE_BRANCH, warn_masked=False)
1218
class TestGlobalConfigItems(tests.TestCaseInTempDir):
1220
def test_user_id(self):
1221
my_config = config.GlobalConfig.from_string(sample_config_text)
1222
self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
1223
my_config._get_user_id())
1225
def test_absent_user_id(self):
1226
my_config = config.GlobalConfig()
1227
self.assertEqual(None, my_config._get_user_id())
1229
def test_configured_editor(self):
1230
my_config = config.GlobalConfig.from_string(sample_config_text)
1231
editor = self.applyDeprecated(
1232
deprecated_in((2, 4, 0)), my_config.get_editor)
1233
self.assertEqual('vim', editor)
1235
def test_signatures_always(self):
1236
my_config = config.GlobalConfig.from_string(sample_always_signatures)
1237
self.assertEqual(config.CHECK_NEVER,
1238
my_config.signature_checking())
1239
self.assertEqual(config.SIGN_ALWAYS,
1240
my_config.signing_policy())
1241
self.assertEqual(True, my_config.signature_needed())
1243
def test_signatures_if_possible(self):
1244
my_config = config.GlobalConfig.from_string(sample_maybe_signatures)
1245
self.assertEqual(config.CHECK_NEVER,
1246
my_config.signature_checking())
1247
self.assertEqual(config.SIGN_WHEN_REQUIRED,
1248
my_config.signing_policy())
1249
self.assertEqual(False, my_config.signature_needed())
1251
def test_signatures_ignore(self):
1252
my_config = config.GlobalConfig.from_string(sample_ignore_signatures)
1253
self.assertEqual(config.CHECK_ALWAYS,
1254
my_config.signature_checking())
1255
self.assertEqual(config.SIGN_NEVER,
1256
my_config.signing_policy())
1257
self.assertEqual(False, my_config.signature_needed())
1259
def _get_sample_config(self):
1260
my_config = config.GlobalConfig.from_string(sample_config_text)
1263
def test_gpg_signing_command(self):
1264
my_config = self._get_sample_config()
1265
self.assertEqual("gnome-gpg", my_config.gpg_signing_command())
1266
self.assertEqual(False, my_config.signature_needed())
1268
def test_gpg_signing_key(self):
1269
my_config = self._get_sample_config()
1270
self.assertEqual("DD4D5088", my_config.gpg_signing_key())
1272
def _get_empty_config(self):
1273
my_config = config.GlobalConfig()
1276
def test_gpg_signing_command_unset(self):
1277
my_config = self._get_empty_config()
1278
self.assertEqual("gpg", my_config.gpg_signing_command())
1280
def test_get_user_option_default(self):
1281
my_config = self._get_empty_config()
1282
self.assertEqual(None, my_config.get_user_option('no_option'))
1284
def test_get_user_option_global(self):
1285
my_config = self._get_sample_config()
1286
self.assertEqual("something",
1287
my_config.get_user_option('user_global_option'))
1289
def test_post_commit_default(self):
1290
my_config = self._get_sample_config()
1291
self.assertEqual(None, my_config.post_commit())
1293
def test_configured_logformat(self):
1294
my_config = self._get_sample_config()
1295
self.assertEqual("short", my_config.log_format())
1297
def test_configured_acceptable_keys(self):
1298
my_config = self._get_sample_config()
1299
self.assertEqual("amy", my_config.acceptable_keys())
1301
def test_configured_validate_signatures_in_log(self):
1302
my_config = self._get_sample_config()
1303
self.assertEqual(True, my_config.validate_signatures_in_log())
1305
def test_get_alias(self):
1306
my_config = self._get_sample_config()
1307
self.assertEqual('help', my_config.get_alias('h'))
1309
def test_get_aliases(self):
1310
my_config = self._get_sample_config()
1311
aliases = my_config.get_aliases()
1312
self.assertEqual(2, len(aliases))
1313
sorted_keys = sorted(aliases)
1314
self.assertEqual('help', aliases[sorted_keys[0]])
1315
self.assertEqual(sample_long_alias, aliases[sorted_keys[1]])
1317
def test_get_no_alias(self):
1318
my_config = self._get_sample_config()
1319
self.assertEqual(None, my_config.get_alias('foo'))
1321
def test_get_long_alias(self):
1322
my_config = self._get_sample_config()
1323
self.assertEqual(sample_long_alias, my_config.get_alias('ll'))
1325
def test_get_change_editor(self):
1326
my_config = self._get_sample_config()
1327
change_editor = my_config.get_change_editor('old', 'new')
1328
self.assertIs(diff.DiffFromTool, change_editor.__class__)
1329
self.assertEqual('vimdiff -of @new_path @old_path',
1330
' '.join(change_editor.command_template))
1332
def test_get_no_change_editor(self):
1333
my_config = self._get_empty_config()
1334
change_editor = my_config.get_change_editor('old', 'new')
1335
self.assertIs(None, change_editor)
1337
def test_get_merge_tools(self):
1338
conf = self._get_sample_config()
1339
tools = conf.get_merge_tools()
1340
self.log(repr(tools))
1342
{u'funkytool' : u'funkytool "arg with spaces" {this_temp}',
1343
u'sometool' : u'sometool {base} {this} {other} -o {result}',
1344
u'newtool' : u'"newtool with spaces" {this_temp}'},
1347
def test_get_merge_tools_empty(self):
1348
conf = self._get_empty_config()
1349
tools = conf.get_merge_tools()
1350
self.assertEqual({}, tools)
1352
def test_find_merge_tool(self):
1353
conf = self._get_sample_config()
1354
cmdline = conf.find_merge_tool('sometool')
1355
self.assertEqual('sometool {base} {this} {other} -o {result}', cmdline)
1357
def test_find_merge_tool_not_found(self):
1358
conf = self._get_sample_config()
1359
cmdline = conf.find_merge_tool('DOES NOT EXIST')
1360
self.assertIs(cmdline, None)
1362
def test_find_merge_tool_known(self):
1363
conf = self._get_empty_config()
1364
cmdline = conf.find_merge_tool('kdiff3')
1365
self.assertEquals('kdiff3 {base} {this} {other} -o {result}', cmdline)
1367
def test_find_merge_tool_override_known(self):
1368
conf = self._get_empty_config()
1369
conf.set_user_option('bzr.mergetool.kdiff3', 'kdiff3 blah')
1370
cmdline = conf.find_merge_tool('kdiff3')
1371
self.assertEqual('kdiff3 blah', cmdline)
1374
class TestGlobalConfigSavingOptions(tests.TestCaseInTempDir):
1376
def test_empty(self):
1377
my_config = config.GlobalConfig()
1378
self.assertEqual(0, len(my_config.get_aliases()))
1380
def test_set_alias(self):
1381
my_config = config.GlobalConfig()
1382
alias_value = 'commit --strict'
1383
my_config.set_alias('commit', alias_value)
1384
new_config = config.GlobalConfig()
1385
self.assertEqual(alias_value, new_config.get_alias('commit'))
1387
def test_remove_alias(self):
1388
my_config = config.GlobalConfig()
1389
my_config.set_alias('commit', 'commit --strict')
1390
# Now remove the alias again.
1391
my_config.unset_alias('commit')
1392
new_config = config.GlobalConfig()
1393
self.assertIs(None, new_config.get_alias('commit'))
1396
class TestLocationConfig(tests.TestCaseInTempDir, TestOptionsMixin):
1398
def test_constructs(self):
1399
my_config = config.LocationConfig('http://example.com')
1400
self.assertRaises(TypeError, config.LocationConfig)
1402
def test_branch_calls_read_filenames(self):
1403
# This is testing the correct file names are provided.
1404
# TODO: consolidate with the test for GlobalConfigs filename checks.
1406
# replace the class that is constructed, to check its parameters
1407
oldparserclass = config.ConfigObj
1408
config.ConfigObj = InstrumentedConfigObj
1410
my_config = config.LocationConfig('http://www.example.com')
1411
parser = my_config._get_parser()
1413
config.ConfigObj = oldparserclass
1414
self.assertIsInstance(parser, InstrumentedConfigObj)
1415
self.assertEqual(parser._calls,
1416
[('__init__', config.locations_config_filename(),
1419
def test_get_global_config(self):
1420
my_config = config.BranchConfig(FakeBranch('http://example.com'))
1421
global_config = my_config._get_global_config()
1422
self.assertIsInstance(global_config, config.GlobalConfig)
1423
self.assertIs(global_config, my_config._get_global_config())
1425
def assertLocationMatching(self, expected):
1426
self.assertEqual(expected,
1427
list(self.my_location_config._get_matching_sections()))
1429
def test__get_matching_sections_no_match(self):
1430
self.get_branch_config('/')
1431
self.assertLocationMatching([])
1433
def test__get_matching_sections_exact(self):
1434
self.get_branch_config('http://www.example.com')
1435
self.assertLocationMatching([('http://www.example.com', '')])
1437
def test__get_matching_sections_suffix_does_not(self):
1438
self.get_branch_config('http://www.example.com-com')
1439
self.assertLocationMatching([])
1441
def test__get_matching_sections_subdir_recursive(self):
1442
self.get_branch_config('http://www.example.com/com')
1443
self.assertLocationMatching([('http://www.example.com', 'com')])
1445
def test__get_matching_sections_ignoreparent(self):
1446
self.get_branch_config('http://www.example.com/ignoreparent')
1447
self.assertLocationMatching([('http://www.example.com/ignoreparent',
1450
def test__get_matching_sections_ignoreparent_subdir(self):
1451
self.get_branch_config(
1452
'http://www.example.com/ignoreparent/childbranch')
1453
self.assertLocationMatching([('http://www.example.com/ignoreparent',
1456
def test__get_matching_sections_subdir_trailing_slash(self):
1457
self.get_branch_config('/b')
1458
self.assertLocationMatching([('/b/', '')])
1460
def test__get_matching_sections_subdir_child(self):
1461
self.get_branch_config('/a/foo')
1462
self.assertLocationMatching([('/a/*', ''), ('/a/', 'foo')])
1464
def test__get_matching_sections_subdir_child_child(self):
1465
self.get_branch_config('/a/foo/bar')
1466
self.assertLocationMatching([('/a/*', 'bar'), ('/a/', 'foo/bar')])
1468
def test__get_matching_sections_trailing_slash_with_children(self):
1469
self.get_branch_config('/a/')
1470
self.assertLocationMatching([('/a/', '')])
1472
def test__get_matching_sections_explicit_over_glob(self):
1473
# XXX: 2006-09-08 jamesh
1474
# This test only passes because ord('c') > ord('*'). If there
1475
# was a config section for '/a/?', it would get precedence
1477
self.get_branch_config('/a/c')
1478
self.assertLocationMatching([('/a/c', ''), ('/a/*', ''), ('/a/', 'c')])
1480
def test__get_option_policy_normal(self):
1481
self.get_branch_config('http://www.example.com')
1483
self.my_location_config._get_config_policy(
1484
'http://www.example.com', 'normal_option'),
1487
def test__get_option_policy_norecurse(self):
1488
self.get_branch_config('http://www.example.com')
1490
self.my_location_config._get_option_policy(
1491
'http://www.example.com', 'norecurse_option'),
1492
config.POLICY_NORECURSE)
1493
# Test old recurse=False setting:
1495
self.my_location_config._get_option_policy(
1496
'http://www.example.com/norecurse', 'normal_option'),
1497
config.POLICY_NORECURSE)
1499
def test__get_option_policy_normal(self):
1500
self.get_branch_config('http://www.example.com')
1502
self.my_location_config._get_option_policy(
1503
'http://www.example.com', 'appendpath_option'),
1504
config.POLICY_APPENDPATH)
1506
def test__get_options_with_policy(self):
1507
self.get_branch_config('/dir/subdir',
1508
location_config="""\
1510
other_url = /other-dir
1511
other_url:policy = appendpath
1513
other_url = /other-subdir
1516
[(u'other_url', u'/other-subdir', u'/dir/subdir', 'locations'),
1517
(u'other_url', u'/other-dir', u'/dir', 'locations'),
1518
(u'other_url:policy', u'appendpath', u'/dir', 'locations')],
1519
self.my_location_config)
1521
def test_location_without_username(self):
1522
self.get_branch_config('http://www.example.com/ignoreparent')
1523
self.assertEqual(u'Erik B\u00e5gfors <erik@bagfors.nu>',
1524
self.my_config.username())
1526
def test_location_not_listed(self):
1527
"""Test that the global username is used when no location matches"""
1528
self.get_branch_config('/home/robertc/sources')
1529
self.assertEqual(u'Erik B\u00e5gfors <erik@bagfors.nu>',
1530
self.my_config.username())
1532
def test_overriding_location(self):
1533
self.get_branch_config('http://www.example.com/foo')
1534
self.assertEqual('Robert Collins <robertc@example.org>',
1535
self.my_config.username())
1537
def test_signatures_not_set(self):
1538
self.get_branch_config('http://www.example.com',
1539
global_config=sample_ignore_signatures)
1540
self.assertEqual(config.CHECK_ALWAYS,
1541
self.my_config.signature_checking())
1542
self.assertEqual(config.SIGN_NEVER,
1543
self.my_config.signing_policy())
1545
def test_signatures_never(self):
1546
self.get_branch_config('/a/c')
1547
self.assertEqual(config.CHECK_NEVER,
1548
self.my_config.signature_checking())
1550
def test_signatures_when_available(self):
1551
self.get_branch_config('/a/', global_config=sample_ignore_signatures)
1552
self.assertEqual(config.CHECK_IF_POSSIBLE,
1553
self.my_config.signature_checking())
1555
def test_signatures_always(self):
1556
self.get_branch_config('/b')
1557
self.assertEqual(config.CHECK_ALWAYS,
1558
self.my_config.signature_checking())
1560
def test_gpg_signing_command(self):
1561
self.get_branch_config('/b')
1562
self.assertEqual("gnome-gpg", self.my_config.gpg_signing_command())
1564
def test_gpg_signing_command_missing(self):
1565
self.get_branch_config('/a')
1566
self.assertEqual("false", self.my_config.gpg_signing_command())
1568
def test_gpg_signing_key(self):
1569
self.get_branch_config('/b')
1570
self.assertEqual("DD4D5088", self.my_config.gpg_signing_key())
1572
def test_gpg_signing_key_default(self):
1573
self.get_branch_config('/a')
1574
self.assertEqual("erik@bagfors.nu", self.my_config.gpg_signing_key())
1576
def test_get_user_option_global(self):
1577
self.get_branch_config('/a')
1578
self.assertEqual('something',
1579
self.my_config.get_user_option('user_global_option'))
1581
def test_get_user_option_local(self):
1582
self.get_branch_config('/a')
1583
self.assertEqual('local',
1584
self.my_config.get_user_option('user_local_option'))
1586
def test_get_user_option_appendpath(self):
1587
# returned as is for the base path:
1588
self.get_branch_config('http://www.example.com')
1589
self.assertEqual('append',
1590
self.my_config.get_user_option('appendpath_option'))
1591
# Extra path components get appended:
1592
self.get_branch_config('http://www.example.com/a/b/c')
1593
self.assertEqual('append/a/b/c',
1594
self.my_config.get_user_option('appendpath_option'))
1595
# Overriden for http://www.example.com/dir, where it is a
1597
self.get_branch_config('http://www.example.com/dir/a/b/c')
1598
self.assertEqual('normal',
1599
self.my_config.get_user_option('appendpath_option'))
1601
def test_get_user_option_norecurse(self):
1602
self.get_branch_config('http://www.example.com')
1603
self.assertEqual('norecurse',
1604
self.my_config.get_user_option('norecurse_option'))
1605
self.get_branch_config('http://www.example.com/dir')
1606
self.assertEqual(None,
1607
self.my_config.get_user_option('norecurse_option'))
1608
# http://www.example.com/norecurse is a recurse=False section
1609
# that redefines normal_option. Subdirectories do not pick up
1610
# this redefinition.
1611
self.get_branch_config('http://www.example.com/norecurse')
1612
self.assertEqual('norecurse',
1613
self.my_config.get_user_option('normal_option'))
1614
self.get_branch_config('http://www.example.com/norecurse/subdir')
1615
self.assertEqual('normal',
1616
self.my_config.get_user_option('normal_option'))
1618
def test_set_user_option_norecurse(self):
1619
self.get_branch_config('http://www.example.com')
1620
self.my_config.set_user_option('foo', 'bar',
1621
store=config.STORE_LOCATION_NORECURSE)
1623
self.my_location_config._get_option_policy(
1624
'http://www.example.com', 'foo'),
1625
config.POLICY_NORECURSE)
1627
def test_set_user_option_appendpath(self):
1628
self.get_branch_config('http://www.example.com')
1629
self.my_config.set_user_option('foo', 'bar',
1630
store=config.STORE_LOCATION_APPENDPATH)
1632
self.my_location_config._get_option_policy(
1633
'http://www.example.com', 'foo'),
1634
config.POLICY_APPENDPATH)
1636
def test_set_user_option_change_policy(self):
1637
self.get_branch_config('http://www.example.com')
1638
self.my_config.set_user_option('norecurse_option', 'normal',
1639
store=config.STORE_LOCATION)
1641
self.my_location_config._get_option_policy(
1642
'http://www.example.com', 'norecurse_option'),
1645
def test_set_user_option_recurse_false_section(self):
1646
# The following section has recurse=False set. The test is to
1647
# make sure that a normal option can be added to the section,
1648
# converting recurse=False to the norecurse policy.
1649
self.get_branch_config('http://www.example.com/norecurse')
1650
self.callDeprecated(['The recurse option is deprecated as of 0.14. '
1651
'The section "http://www.example.com/norecurse" '
1652
'has been converted to use policies.'],
1653
self.my_config.set_user_option,
1654
'foo', 'bar', store=config.STORE_LOCATION)
1656
self.my_location_config._get_option_policy(
1657
'http://www.example.com/norecurse', 'foo'),
1659
# The previously existing option is still norecurse:
1661
self.my_location_config._get_option_policy(
1662
'http://www.example.com/norecurse', 'normal_option'),
1663
config.POLICY_NORECURSE)
1665
def test_post_commit_default(self):
1666
self.get_branch_config('/a/c')
1667
self.assertEqual('bzrlib.tests.test_config.post_commit',
1668
self.my_config.post_commit())
1670
def get_branch_config(self, location, global_config=None,
1671
location_config=None):
1672
my_branch = FakeBranch(location)
1673
if global_config is None:
1674
global_config = sample_config_text
1675
if location_config is None:
1676
location_config = sample_branches_text
1678
my_global_config = config.GlobalConfig.from_string(global_config,
1680
my_location_config = config.LocationConfig.from_string(
1681
location_config, my_branch.base, save=True)
1682
my_config = config.BranchConfig(my_branch)
1683
self.my_config = my_config
1684
self.my_location_config = my_config._get_location_config()
1686
def test_set_user_setting_sets_and_saves(self):
1687
self.get_branch_config('/a/c')
1688
record = InstrumentedConfigObj("foo")
1689
self.my_location_config._parser = record
1691
self.callDeprecated(['The recurse option is deprecated as of '
1692
'0.14. The section "/a/c" has been '
1693
'converted to use policies.'],
1694
self.my_config.set_user_option,
1695
'foo', 'bar', store=config.STORE_LOCATION)
1696
self.assertEqual([('reload',),
1697
('__contains__', '/a/c'),
1698
('__contains__', '/a/c/'),
1699
('__setitem__', '/a/c', {}),
1700
('__getitem__', '/a/c'),
1701
('__setitem__', 'foo', 'bar'),
1702
('__getitem__', '/a/c'),
1703
('as_bool', 'recurse'),
1704
('__getitem__', '/a/c'),
1705
('__delitem__', 'recurse'),
1706
('__getitem__', '/a/c'),
1708
('__getitem__', '/a/c'),
1709
('__contains__', 'foo:policy'),
1713
def test_set_user_setting_sets_and_saves2(self):
1714
self.get_branch_config('/a/c')
1715
self.assertIs(self.my_config.get_user_option('foo'), None)
1716
self.my_config.set_user_option('foo', 'bar')
1718
self.my_config.branch.control_files.files['branch.conf'].strip(),
1720
self.assertEqual(self.my_config.get_user_option('foo'), 'bar')
1721
self.my_config.set_user_option('foo', 'baz',
1722
store=config.STORE_LOCATION)
1723
self.assertEqual(self.my_config.get_user_option('foo'), 'baz')
1724
self.my_config.set_user_option('foo', 'qux')
1725
self.assertEqual(self.my_config.get_user_option('foo'), 'baz')
1727
def test_get_bzr_remote_path(self):
1728
my_config = config.LocationConfig('/a/c')
1729
self.assertEqual('bzr', my_config.get_bzr_remote_path())
1730
my_config.set_user_option('bzr_remote_path', '/path-bzr')
1731
self.assertEqual('/path-bzr', my_config.get_bzr_remote_path())
1732
self.overrideEnv('BZR_REMOTE_PATH', '/environ-bzr')
1733
self.assertEqual('/environ-bzr', my_config.get_bzr_remote_path())
1736
precedence_global = 'option = global'
1737
precedence_branch = 'option = branch'
1738
precedence_location = """
1742
[http://example.com/specific]
1746
class TestBranchConfigItems(tests.TestCaseInTempDir):
1748
def get_branch_config(self, global_config=None, location=None,
1749
location_config=None, branch_data_config=None):
1750
my_branch = FakeBranch(location)
1751
if global_config is not None:
1752
my_global_config = config.GlobalConfig.from_string(global_config,
1754
if location_config is not None:
1755
my_location_config = config.LocationConfig.from_string(
1756
location_config, my_branch.base, save=True)
1757
my_config = config.BranchConfig(my_branch)
1758
if branch_data_config is not None:
1759
my_config.branch.control_files.files['branch.conf'] = \
1763
def test_user_id(self):
1764
branch = FakeBranch(user_id='Robert Collins <robertc@example.net>')
1765
my_config = config.BranchConfig(branch)
1766
self.assertEqual("Robert Collins <robertc@example.net>",
1767
my_config.username())
1768
my_config.branch.control_files.files['email'] = "John"
1769
my_config.set_user_option('email',
1770
"Robert Collins <robertc@example.org>")
1771
self.assertEqual("John", my_config.username())
1772
del my_config.branch.control_files.files['email']
1773
self.assertEqual("Robert Collins <robertc@example.org>",
1774
my_config.username())
1776
def test_not_set_in_branch(self):
1777
my_config = self.get_branch_config(global_config=sample_config_text)
1778
self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
1779
my_config._get_user_id())
1780
my_config.branch.control_files.files['email'] = "John"
1781
self.assertEqual("John", my_config._get_user_id())
1783
def test_BZR_EMAIL_OVERRIDES(self):
1784
self.overrideEnv('BZR_EMAIL', "Robert Collins <robertc@example.org>")
1785
branch = FakeBranch()
1786
my_config = config.BranchConfig(branch)
1787
self.assertEqual("Robert Collins <robertc@example.org>",
1788
my_config.username())
1790
def test_signatures_forced(self):
1791
my_config = self.get_branch_config(
1792
global_config=sample_always_signatures)
1793
self.assertEqual(config.CHECK_NEVER, my_config.signature_checking())
1794
self.assertEqual(config.SIGN_ALWAYS, my_config.signing_policy())
1795
self.assertTrue(my_config.signature_needed())
1797
def test_signatures_forced_branch(self):
1798
my_config = self.get_branch_config(
1799
global_config=sample_ignore_signatures,
1800
branch_data_config=sample_always_signatures)
1801
self.assertEqual(config.CHECK_NEVER, my_config.signature_checking())
1802
self.assertEqual(config.SIGN_ALWAYS, my_config.signing_policy())
1803
self.assertTrue(my_config.signature_needed())
1805
def test_gpg_signing_command(self):
1806
my_config = self.get_branch_config(
1807
global_config=sample_config_text,
1808
# branch data cannot set gpg_signing_command
1809
branch_data_config="gpg_signing_command=pgp")
1810
self.assertEqual('gnome-gpg', my_config.gpg_signing_command())
1812
def test_get_user_option_global(self):
1813
my_config = self.get_branch_config(global_config=sample_config_text)
1814
self.assertEqual('something',
1815
my_config.get_user_option('user_global_option'))
1817
def test_post_commit_default(self):
1818
my_config = self.get_branch_config(global_config=sample_config_text,
1820
location_config=sample_branches_text)
1821
self.assertEqual(my_config.branch.base, '/a/c')
1822
self.assertEqual('bzrlib.tests.test_config.post_commit',
1823
my_config.post_commit())
1824
my_config.set_user_option('post_commit', 'rmtree_root')
1825
# post-commit is ignored when present in branch data
1826
self.assertEqual('bzrlib.tests.test_config.post_commit',
1827
my_config.post_commit())
1828
my_config.set_user_option('post_commit', 'rmtree_root',
1829
store=config.STORE_LOCATION)
1830
self.assertEqual('rmtree_root', my_config.post_commit())
1832
def test_config_precedence(self):
1833
# FIXME: eager test, luckily no persitent config file makes it fail
1835
my_config = self.get_branch_config(global_config=precedence_global)
1836
self.assertEqual(my_config.get_user_option('option'), 'global')
1837
my_config = self.get_branch_config(global_config=precedence_global,
1838
branch_data_config=precedence_branch)
1839
self.assertEqual(my_config.get_user_option('option'), 'branch')
1840
my_config = self.get_branch_config(
1841
global_config=precedence_global,
1842
branch_data_config=precedence_branch,
1843
location_config=precedence_location)
1844
self.assertEqual(my_config.get_user_option('option'), 'recurse')
1845
my_config = self.get_branch_config(
1846
global_config=precedence_global,
1847
branch_data_config=precedence_branch,
1848
location_config=precedence_location,
1849
location='http://example.com/specific')
1850
self.assertEqual(my_config.get_user_option('option'), 'exact')
1852
def test_get_mail_client(self):
1853
config = self.get_branch_config()
1854
client = config.get_mail_client()
1855
self.assertIsInstance(client, mail_client.DefaultMail)
1858
config.set_user_option('mail_client', 'evolution')
1859
client = config.get_mail_client()
1860
self.assertIsInstance(client, mail_client.Evolution)
1862
config.set_user_option('mail_client', 'kmail')
1863
client = config.get_mail_client()
1864
self.assertIsInstance(client, mail_client.KMail)
1866
config.set_user_option('mail_client', 'mutt')
1867
client = config.get_mail_client()
1868
self.assertIsInstance(client, mail_client.Mutt)
1870
config.set_user_option('mail_client', 'thunderbird')
1871
client = config.get_mail_client()
1872
self.assertIsInstance(client, mail_client.Thunderbird)
1875
config.set_user_option('mail_client', 'default')
1876
client = config.get_mail_client()
1877
self.assertIsInstance(client, mail_client.DefaultMail)
1879
config.set_user_option('mail_client', 'editor')
1880
client = config.get_mail_client()
1881
self.assertIsInstance(client, mail_client.Editor)
1883
config.set_user_option('mail_client', 'mapi')
1884
client = config.get_mail_client()
1885
self.assertIsInstance(client, mail_client.MAPIClient)
1887
config.set_user_option('mail_client', 'xdg-email')
1888
client = config.get_mail_client()
1889
self.assertIsInstance(client, mail_client.XDGEmail)
1891
config.set_user_option('mail_client', 'firebird')
1892
self.assertRaises(errors.UnknownMailClient, config.get_mail_client)
1895
class TestMailAddressExtraction(tests.TestCase):
1897
def test_extract_email_address(self):
1898
self.assertEqual('jane@test.com',
1899
config.extract_email_address('Jane <jane@test.com>'))
1900
self.assertRaises(errors.NoEmailInUsername,
1901
config.extract_email_address, 'Jane Tester')
1903
def test_parse_username(self):
1904
self.assertEqual(('', 'jdoe@example.com'),
1905
config.parse_username('jdoe@example.com'))
1906
self.assertEqual(('', 'jdoe@example.com'),
1907
config.parse_username('<jdoe@example.com>'))
1908
self.assertEqual(('John Doe', 'jdoe@example.com'),
1909
config.parse_username('John Doe <jdoe@example.com>'))
1910
self.assertEqual(('John Doe', ''),
1911
config.parse_username('John Doe'))
1912
self.assertEqual(('John Doe', 'jdoe@example.com'),
1913
config.parse_username('John Doe jdoe@example.com'))
1915
class TestTreeConfig(tests.TestCaseWithTransport):
1917
def test_get_value(self):
1918
"""Test that retreiving a value from a section is possible"""
1919
branch = self.make_branch('.')
1920
tree_config = config.TreeConfig(branch)
1921
tree_config.set_option('value', 'key', 'SECTION')
1922
tree_config.set_option('value2', 'key2')
1923
tree_config.set_option('value3-top', 'key3')
1924
tree_config.set_option('value3-section', 'key3', 'SECTION')
1925
value = tree_config.get_option('key', 'SECTION')
1926
self.assertEqual(value, 'value')
1927
value = tree_config.get_option('key2')
1928
self.assertEqual(value, 'value2')
1929
self.assertEqual(tree_config.get_option('non-existant'), None)
1930
value = tree_config.get_option('non-existant', 'SECTION')
1931
self.assertEqual(value, None)
1932
value = tree_config.get_option('non-existant', default='default')
1933
self.assertEqual(value, 'default')
1934
self.assertEqual(tree_config.get_option('key2', 'NOSECTION'), None)
1935
value = tree_config.get_option('key2', 'NOSECTION', default='default')
1936
self.assertEqual(value, 'default')
1937
value = tree_config.get_option('key3')
1938
self.assertEqual(value, 'value3-top')
1939
value = tree_config.get_option('key3', 'SECTION')
1940
self.assertEqual(value, 'value3-section')
1943
class TestTransportConfig(tests.TestCaseWithTransport):
1945
def test_load_utf8(self):
1946
"""Ensure we can load an utf8-encoded file."""
1947
t = self.get_transport()
1948
unicode_user = u'b\N{Euro Sign}ar'
1949
unicode_content = u'user=%s' % (unicode_user,)
1950
utf8_content = unicode_content.encode('utf8')
1951
# Store the raw content in the config file
1952
t.put_bytes('foo.conf', utf8_content)
1953
conf = config.TransportConfig(t, 'foo.conf')
1954
self.assertEquals(unicode_user, conf.get_option('user'))
1956
def test_load_non_ascii(self):
1957
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
1958
t = self.get_transport()
1959
t.put_bytes('foo.conf', 'user=foo\n#\xff\n')
1960
conf = config.TransportConfig(t, 'foo.conf')
1961
self.assertRaises(errors.ConfigContentError, conf._get_configobj)
1963
def test_load_erroneous_content(self):
1964
"""Ensure we display a proper error on content that can't be parsed."""
1965
t = self.get_transport()
1966
t.put_bytes('foo.conf', '[open_section\n')
1967
conf = config.TransportConfig(t, 'foo.conf')
1968
self.assertRaises(errors.ParseConfigError, conf._get_configobj)
1970
def test_load_permission_denied(self):
1971
"""Ensure we get an empty config file if the file is inaccessible."""
1974
warnings.append(args[0] % args[1:])
1975
self.overrideAttr(trace, 'warning', warning)
1977
class DenyingTransport(object):
1979
def __init__(self, base):
1982
def get_bytes(self, relpath):
1983
raise errors.PermissionDenied(relpath, "")
1985
cfg = config.TransportConfig(
1986
DenyingTransport("nonexisting://"), 'control.conf')
1987
self.assertIs(None, cfg.get_option('non-existant', 'SECTION'))
1990
[u'Permission denied while trying to open configuration file '
1991
u'nonexisting:///control.conf.'])
1993
def test_get_value(self):
1994
"""Test that retreiving a value from a section is possible"""
1995
bzrdir_config = config.TransportConfig(self.get_transport('.'),
1997
bzrdir_config.set_option('value', 'key', 'SECTION')
1998
bzrdir_config.set_option('value2', 'key2')
1999
bzrdir_config.set_option('value3-top', 'key3')
2000
bzrdir_config.set_option('value3-section', 'key3', 'SECTION')
2001
value = bzrdir_config.get_option('key', 'SECTION')
2002
self.assertEqual(value, 'value')
2003
value = bzrdir_config.get_option('key2')
2004
self.assertEqual(value, 'value2')
2005
self.assertEqual(bzrdir_config.get_option('non-existant'), None)
2006
value = bzrdir_config.get_option('non-existant', 'SECTION')
2007
self.assertEqual(value, None)
2008
value = bzrdir_config.get_option('non-existant', default='default')
2009
self.assertEqual(value, 'default')
2010
self.assertEqual(bzrdir_config.get_option('key2', 'NOSECTION'), None)
2011
value = bzrdir_config.get_option('key2', 'NOSECTION',
2013
self.assertEqual(value, 'default')
2014
value = bzrdir_config.get_option('key3')
2015
self.assertEqual(value, 'value3-top')
2016
value = bzrdir_config.get_option('key3', 'SECTION')
2017
self.assertEqual(value, 'value3-section')
2019
def test_set_unset_default_stack_on(self):
2020
my_dir = self.make_bzrdir('.')
2021
bzrdir_config = config.BzrDirConfig(my_dir)
2022
self.assertIs(None, bzrdir_config.get_default_stack_on())
2023
bzrdir_config.set_default_stack_on('Foo')
2024
self.assertEqual('Foo', bzrdir_config._config.get_option(
2025
'default_stack_on'))
2026
self.assertEqual('Foo', bzrdir_config.get_default_stack_on())
2027
bzrdir_config.set_default_stack_on(None)
2028
self.assertIs(None, bzrdir_config.get_default_stack_on())
2031
class TestOldConfigHooks(tests.TestCaseWithTransport):
2034
super(TestOldConfigHooks, self).setUp()
2035
create_configs_with_file_option(self)
2037
def assertGetHook(self, conf, name, value):
2041
config.OldConfigHooks.install_named_hook('get', hook, None)
2043
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2044
self.assertLength(0, calls)
2045
actual_value = conf.get_user_option(name)
2046
self.assertEquals(value, actual_value)
2047
self.assertLength(1, calls)
2048
self.assertEquals((conf, name, value), calls[0])
2050
def test_get_hook_bazaar(self):
2051
self.assertGetHook(self.bazaar_config, 'file', 'bazaar')
2053
def test_get_hook_locations(self):
2054
self.assertGetHook(self.locations_config, 'file', 'locations')
2056
def test_get_hook_branch(self):
2057
# Since locations masks branch, we define a different option
2058
self.branch_config.set_user_option('file2', 'branch')
2059
self.assertGetHook(self.branch_config, 'file2', 'branch')
2061
def assertSetHook(self, conf, name, value):
2065
config.OldConfigHooks.install_named_hook('set', hook, None)
2067
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2068
self.assertLength(0, calls)
2069
conf.set_user_option(name, value)
2070
self.assertLength(1, calls)
2071
# We can't assert the conf object below as different configs use
2072
# different means to implement set_user_option and we care only about
2074
self.assertEquals((name, value), calls[0][1:])
2076
def test_set_hook_bazaar(self):
2077
self.assertSetHook(self.bazaar_config, 'foo', 'bazaar')
2079
def test_set_hook_locations(self):
2080
self.assertSetHook(self.locations_config, 'foo', 'locations')
2082
def test_set_hook_branch(self):
2083
self.assertSetHook(self.branch_config, 'foo', 'branch')
2085
def assertRemoveHook(self, conf, name, section_name=None):
2089
config.OldConfigHooks.install_named_hook('remove', hook, None)
2091
config.OldConfigHooks.uninstall_named_hook, 'remove', None)
2092
self.assertLength(0, calls)
2093
conf.remove_user_option(name, section_name)
2094
self.assertLength(1, calls)
2095
# We can't assert the conf object below as different configs use
2096
# different means to implement remove_user_option and we care only about
2098
self.assertEquals((name,), calls[0][1:])
2100
def test_remove_hook_bazaar(self):
2101
self.assertRemoveHook(self.bazaar_config, 'file')
2103
def test_remove_hook_locations(self):
2104
self.assertRemoveHook(self.locations_config, 'file',
2105
self.locations_config.location)
2107
def test_remove_hook_branch(self):
2108
self.assertRemoveHook(self.branch_config, 'file')
2110
def assertLoadHook(self, name, conf_class, *conf_args):
2114
config.OldConfigHooks.install_named_hook('load', hook, None)
2116
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2117
self.assertLength(0, calls)
2119
conf = conf_class(*conf_args)
2120
# Access an option to trigger a load
2121
conf.get_user_option(name)
2122
self.assertLength(1, calls)
2123
# Since we can't assert about conf, we just use the number of calls ;-/
2125
def test_load_hook_bazaar(self):
2126
self.assertLoadHook('file', config.GlobalConfig)
2128
def test_load_hook_locations(self):
2129
self.assertLoadHook('file', config.LocationConfig, self.tree.basedir)
2131
def test_load_hook_branch(self):
2132
self.assertLoadHook('file', config.BranchConfig, self.tree.branch)
2134
def assertSaveHook(self, conf):
2138
config.OldConfigHooks.install_named_hook('save', hook, None)
2140
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2141
self.assertLength(0, calls)
2142
# Setting an option triggers a save
2143
conf.set_user_option('foo', 'bar')
2144
self.assertLength(1, calls)
2145
# Since we can't assert about conf, we just use the number of calls ;-/
2147
def test_save_hook_bazaar(self):
2148
self.assertSaveHook(self.bazaar_config)
2150
def test_save_hook_locations(self):
2151
self.assertSaveHook(self.locations_config)
2153
def test_save_hook_branch(self):
2154
self.assertSaveHook(self.branch_config)
2157
class TestOldConfigHooksForRemote(tests.TestCaseWithTransport):
2158
"""Tests config hooks for remote configs.
2160
No tests for the remove hook as this is not implemented there.
2164
super(TestOldConfigHooksForRemote, self).setUp()
2165
self.transport_server = test_server.SmartTCPServer_for_testing
2166
create_configs_with_file_option(self)
2168
def assertGetHook(self, conf, name, value):
2172
config.OldConfigHooks.install_named_hook('get', hook, None)
2174
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2175
self.assertLength(0, calls)
2176
actual_value = conf.get_option(name)
2177
self.assertEquals(value, actual_value)
2178
self.assertLength(1, calls)
2179
self.assertEquals((conf, name, value), calls[0])
2181
def test_get_hook_remote_branch(self):
2182
remote_branch = branch.Branch.open(self.get_url('tree'))
2183
self.assertGetHook(remote_branch._get_config(), 'file', 'branch')
2185
def test_get_hook_remote_bzrdir(self):
2186
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2187
conf = remote_bzrdir._get_config()
2188
conf.set_option('remotedir', 'file')
2189
self.assertGetHook(conf, 'file', 'remotedir')
2191
def assertSetHook(self, conf, name, value):
2195
config.OldConfigHooks.install_named_hook('set', hook, None)
2197
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2198
self.assertLength(0, calls)
2199
conf.set_option(value, name)
2200
self.assertLength(1, calls)
2201
# We can't assert the conf object below as different configs use
2202
# different means to implement set_user_option and we care only about
2204
self.assertEquals((name, value), calls[0][1:])
2206
def test_set_hook_remote_branch(self):
2207
remote_branch = branch.Branch.open(self.get_url('tree'))
2208
self.addCleanup(remote_branch.lock_write().unlock)
2209
self.assertSetHook(remote_branch._get_config(), 'file', 'remote')
2211
def test_set_hook_remote_bzrdir(self):
2212
remote_branch = branch.Branch.open(self.get_url('tree'))
2213
self.addCleanup(remote_branch.lock_write().unlock)
2214
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2215
self.assertSetHook(remote_bzrdir._get_config(), 'file', 'remotedir')
2217
def assertLoadHook(self, expected_nb_calls, name, conf_class, *conf_args):
2221
config.OldConfigHooks.install_named_hook('load', hook, None)
2223
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2224
self.assertLength(0, calls)
2226
conf = conf_class(*conf_args)
2227
# Access an option to trigger a load
2228
conf.get_option(name)
2229
self.assertLength(expected_nb_calls, calls)
2230
# Since we can't assert about conf, we just use the number of calls ;-/
2232
def test_load_hook_remote_branch(self):
2233
remote_branch = branch.Branch.open(self.get_url('tree'))
2234
self.assertLoadHook(1, 'file', remote.RemoteBranchConfig, remote_branch)
2236
def test_load_hook_remote_bzrdir(self):
2237
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2238
# The config file doesn't exist, set an option to force its creation
2239
conf = remote_bzrdir._get_config()
2240
conf.set_option('remotedir', 'file')
2241
# We get one call for the server and one call for the client, this is
2242
# caused by the differences in implementations betwen
2243
# SmartServerBzrDirRequestConfigFile (in smart/bzrdir.py) and
2244
# SmartServerBranchGetConfigFile (in smart/branch.py)
2245
self.assertLoadHook(2 ,'file', remote.RemoteBzrDirConfig, remote_bzrdir)
2247
def assertSaveHook(self, conf):
2251
config.OldConfigHooks.install_named_hook('save', hook, None)
2253
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2254
self.assertLength(0, calls)
2255
# Setting an option triggers a save
2256
conf.set_option('foo', 'bar')
2257
self.assertLength(1, calls)
2258
# Since we can't assert about conf, we just use the number of calls ;-/
2260
def test_save_hook_remote_branch(self):
2261
remote_branch = branch.Branch.open(self.get_url('tree'))
2262
self.addCleanup(remote_branch.lock_write().unlock)
2263
self.assertSaveHook(remote_branch._get_config())
2265
def test_save_hook_remote_bzrdir(self):
2266
remote_branch = branch.Branch.open(self.get_url('tree'))
2267
self.addCleanup(remote_branch.lock_write().unlock)
2268
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2269
self.assertSaveHook(remote_bzrdir._get_config())
2272
class TestOption(tests.TestCase):
2274
def test_default_value(self):
2275
opt = config.Option('foo', default='bar')
2276
self.assertEquals('bar', opt.get_default())
2278
def test_default_value_from_env(self):
2279
opt = config.Option('foo', default='bar', default_from_env=['FOO'])
2280
self.overrideEnv('FOO', 'quux')
2281
# Env variable provides a default taking over the option one
2282
self.assertEquals('quux', opt.get_default())
2284
def test_first_default_value_from_env_wins(self):
2285
opt = config.Option('foo', default='bar',
2286
default_from_env=['NO_VALUE', 'FOO', 'BAZ'])
2287
self.overrideEnv('FOO', 'foo')
2288
self.overrideEnv('BAZ', 'baz')
2289
# The first env var set wins
2290
self.assertEquals('foo', opt.get_default())
2292
def test_not_supported_list_default_value(self):
2293
self.assertRaises(AssertionError, config.Option, 'foo', default=[1])
2295
def test_not_supported_object_default_value(self):
2296
self.assertRaises(AssertionError, config.Option, 'foo',
2300
class TestOptionConverterMixin(object):
2302
def assertConverted(self, expected, opt, value):
2303
self.assertEquals(expected, opt.convert_from_unicode(value))
2305
def assertWarns(self, opt, value):
2308
warnings.append(args[0] % args[1:])
2309
self.overrideAttr(trace, 'warning', warning)
2310
self.assertEquals(None, opt.convert_from_unicode(value))
2311
self.assertLength(1, warnings)
2313
'Value "%s" is not valid for "%s"' % (value, opt.name),
2316
def assertErrors(self, opt, value):
2317
self.assertRaises(errors.ConfigOptionValueError,
2318
opt.convert_from_unicode, value)
2320
def assertConvertInvalid(self, opt, invalid_value):
2322
self.assertEquals(None, opt.convert_from_unicode(invalid_value))
2323
opt.invalid = 'warning'
2324
self.assertWarns(opt, invalid_value)
2325
opt.invalid = 'error'
2326
self.assertErrors(opt, invalid_value)
2329
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2331
def get_option(self):
2332
return config.Option('foo', help='A boolean.',
2333
from_unicode=config.bool_from_store)
2335
def test_convert_invalid(self):
2336
opt = self.get_option()
2337
# A string that is not recognized as a boolean
2338
self.assertConvertInvalid(opt, u'invalid-boolean')
2339
# A list of strings is never recognized as a boolean
2340
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2342
def test_convert_valid(self):
2343
opt = self.get_option()
2344
self.assertConverted(True, opt, u'True')
2345
self.assertConverted(True, opt, u'1')
2346
self.assertConverted(False, opt, u'False')
2349
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2351
def get_option(self):
2352
return config.Option('foo', help='An integer.',
2353
from_unicode=config.int_from_store)
2355
def test_convert_invalid(self):
2356
opt = self.get_option()
2357
# A string that is not recognized as an integer
2358
self.assertConvertInvalid(opt, u'forty-two')
2359
# A list of strings is never recognized as an integer
2360
self.assertConvertInvalid(opt, [u'a', u'list'])
2362
def test_convert_valid(self):
2363
opt = self.get_option()
2364
self.assertConverted(16, opt, u'16')
2366
class TestOptionWithListConverter(tests.TestCase, TestOptionConverterMixin):
2368
def get_option(self):
2369
return config.Option('foo', help='A list.',
2370
from_unicode=config.list_from_store)
2372
def test_convert_invalid(self):
2373
# No string is invalid as all forms can be converted to a list
2376
def test_convert_valid(self):
2377
opt = self.get_option()
2378
# An empty string is an empty list
2379
self.assertConverted([], opt, '') # Using a bare str() just in case
2380
self.assertConverted([], opt, u'')
2382
self.assertConverted([u'True'], opt, u'True')
2384
self.assertConverted([u'42'], opt, u'42')
2386
self.assertConverted([u'bar'], opt, u'bar')
2387
# A list remains a list (configObj will turn a string containing commas
2388
# into a list, but that's not what we're testing here)
2389
self.assertConverted([u'foo', u'1', u'True'],
2390
opt, [u'foo', u'1', u'True'])
2393
class TestOptionConverterMixin(object):
2395
def assertConverted(self, expected, opt, value):
2396
self.assertEquals(expected, opt.convert_from_unicode(value))
2398
def assertWarns(self, opt, value):
2401
warnings.append(args[0] % args[1:])
2402
self.overrideAttr(trace, 'warning', warning)
2403
self.assertEquals(None, opt.convert_from_unicode(value))
2404
self.assertLength(1, warnings)
2406
'Value "%s" is not valid for "%s"' % (value, opt.name),
2409
def assertErrors(self, opt, value):
2410
self.assertRaises(errors.ConfigOptionValueError,
2411
opt.convert_from_unicode, value)
2413
def assertConvertInvalid(self, opt, invalid_value):
2415
self.assertEquals(None, opt.convert_from_unicode(invalid_value))
2416
opt.invalid = 'warning'
2417
self.assertWarns(opt, invalid_value)
2418
opt.invalid = 'error'
2419
self.assertErrors(opt, invalid_value)
2422
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2424
def get_option(self):
2425
return config.Option('foo', help='A boolean.',
2426
from_unicode=config.bool_from_store)
2428
def test_convert_invalid(self):
2429
opt = self.get_option()
2430
# A string that is not recognized as a boolean
2431
self.assertConvertInvalid(opt, u'invalid-boolean')
2432
# A list of strings is never recognized as a boolean
2433
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2435
def test_convert_valid(self):
2436
opt = self.get_option()
2437
self.assertConverted(True, opt, u'True')
2438
self.assertConverted(True, opt, u'1')
2439
self.assertConverted(False, opt, u'False')
2442
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2444
def get_option(self):
2445
return config.Option('foo', help='An integer.',
2446
from_unicode=config.int_from_store)
2448
def test_convert_invalid(self):
2449
opt = self.get_option()
2450
# A string that is not recognized as an integer
2451
self.assertConvertInvalid(opt, u'forty-two')
2452
# A list of strings is never recognized as an integer
2453
self.assertConvertInvalid(opt, [u'a', u'list'])
2455
def test_convert_valid(self):
2456
opt = self.get_option()
2457
self.assertConverted(16, opt, u'16')
2460
class TestOptionWithListConverter(tests.TestCase, TestOptionConverterMixin):
2462
def get_option(self):
2463
return config.Option('foo', help='A list.',
2464
from_unicode=config.list_from_store)
2466
def test_convert_invalid(self):
2467
opt = self.get_option()
2468
# We don't even try to convert a list into a list, we only expect
2470
self.assertConvertInvalid(opt, [1])
2471
# No string is invalid as all forms can be converted to a list
2473
def test_convert_valid(self):
2474
opt = self.get_option()
2475
# An empty string is an empty list
2476
self.assertConverted([], opt, '') # Using a bare str() just in case
2477
self.assertConverted([], opt, u'')
2479
self.assertConverted([u'True'], opt, u'True')
2481
self.assertConverted([u'42'], opt, u'42')
2483
self.assertConverted([u'bar'], opt, u'bar')
2486
class TestOptionRegistry(tests.TestCase):
2489
super(TestOptionRegistry, self).setUp()
2490
# Always start with an empty registry
2491
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2492
self.registry = config.option_registry
2494
def test_register(self):
2495
opt = config.Option('foo')
2496
self.registry.register(opt)
2497
self.assertIs(opt, self.registry.get('foo'))
2499
def test_registered_help(self):
2500
opt = config.Option('foo', help='A simple option')
2501
self.registry.register(opt)
2502
self.assertEquals('A simple option', self.registry.get_help('foo'))
2504
lazy_option = config.Option('lazy_foo', help='Lazy help')
2506
def test_register_lazy(self):
2507
self.registry.register_lazy('lazy_foo', self.__module__,
2508
'TestOptionRegistry.lazy_option')
2509
self.assertIs(self.lazy_option, self.registry.get('lazy_foo'))
2511
def test_registered_lazy_help(self):
2512
self.registry.register_lazy('lazy_foo', self.__module__,
2513
'TestOptionRegistry.lazy_option')
2514
self.assertEquals('Lazy help', self.registry.get_help('lazy_foo'))
2517
class TestRegisteredOptions(tests.TestCase):
2518
"""All registered options should verify some constraints."""
2520
scenarios = [(key, {'option_name': key, 'option': option}) for key, option
2521
in config.option_registry.iteritems()]
2524
super(TestRegisteredOptions, self).setUp()
2525
self.registry = config.option_registry
2527
def test_proper_name(self):
2528
# An option should be registered under its own name, this can't be
2529
# checked at registration time for the lazy ones.
2530
self.assertEquals(self.option_name, self.option.name)
2532
def test_help_is_set(self):
2533
option_help = self.registry.get_help(self.option_name)
2534
self.assertNotEquals(None, option_help)
2535
# Come on, think about the user, he really wants to know what the
2537
self.assertIsNot(None, option_help)
2538
self.assertNotEquals('', option_help)
2541
class TestSection(tests.TestCase):
2543
# FIXME: Parametrize so that all sections produced by Stores run these
2544
# tests -- vila 2011-04-01
2546
def test_get_a_value(self):
2547
a_dict = dict(foo='bar')
2548
section = config.Section('myID', a_dict)
2549
self.assertEquals('bar', section.get('foo'))
2551
def test_get_unknown_option(self):
2553
section = config.Section(None, a_dict)
2554
self.assertEquals('out of thin air',
2555
section.get('foo', 'out of thin air'))
2557
def test_options_is_shared(self):
2559
section = config.Section(None, a_dict)
2560
self.assertIs(a_dict, section.options)
2563
class TestMutableSection(tests.TestCase):
2565
scenarios = [('mutable',
2567
lambda opts: config.MutableSection('myID', opts)},),
2571
a_dict = dict(foo='bar')
2572
section = self.get_section(a_dict)
2573
section.set('foo', 'new_value')
2574
self.assertEquals('new_value', section.get('foo'))
2575
# The change appears in the shared section
2576
self.assertEquals('new_value', a_dict.get('foo'))
2577
# We keep track of the change
2578
self.assertTrue('foo' in section.orig)
2579
self.assertEquals('bar', section.orig.get('foo'))
2581
def test_set_preserve_original_once(self):
2582
a_dict = dict(foo='bar')
2583
section = self.get_section(a_dict)
2584
section.set('foo', 'first_value')
2585
section.set('foo', 'second_value')
2586
# We keep track of the original value
2587
self.assertTrue('foo' in section.orig)
2588
self.assertEquals('bar', section.orig.get('foo'))
2590
def test_remove(self):
2591
a_dict = dict(foo='bar')
2592
section = self.get_section(a_dict)
2593
section.remove('foo')
2594
# We get None for unknown options via the default value
2595
self.assertEquals(None, section.get('foo'))
2596
# Or we just get the default value
2597
self.assertEquals('unknown', section.get('foo', 'unknown'))
2598
self.assertFalse('foo' in section.options)
2599
# We keep track of the deletion
2600
self.assertTrue('foo' in section.orig)
2601
self.assertEquals('bar', section.orig.get('foo'))
2603
def test_remove_new_option(self):
2605
section = self.get_section(a_dict)
2606
section.set('foo', 'bar')
2607
section.remove('foo')
2608
self.assertFalse('foo' in section.options)
2609
# The option didn't exist initially so it we need to keep track of it
2610
# with a special value
2611
self.assertTrue('foo' in section.orig)
2612
self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
2615
class TestCommandLineStore(tests.TestCase):
2618
super(TestCommandLineStore, self).setUp()
2619
self.store = config.CommandLineStore()
2621
def get_section(self):
2622
"""Get the unique section for the command line overrides."""
2623
sections = list(self.store.get_sections())
2624
self.assertLength(1, sections)
2625
store, section = sections[0]
2626
self.assertEquals(self.store, store)
2629
def test_no_override(self):
2630
self.store._from_cmdline([])
2631
section = self.get_section()
2632
self.assertLength(0, list(section.iter_option_names()))
2634
def test_simple_override(self):
2635
self.store._from_cmdline(['a=b'])
2636
section = self.get_section()
2637
self.assertEqual('b', section.get('a'))
2639
def test_list_override(self):
2640
self.store._from_cmdline(['l=1,2,3'])
2641
val = self.get_section().get('l')
2642
self.assertEqual('1,2,3', val)
2643
# Reminder: lists should be registered as such explicitely, otherwise
2644
# the conversion needs to be done afterwards.
2645
self.assertEqual(['1', '2', '3'], config.list_from_store(val))
2647
def test_multiple_overrides(self):
2648
self.store._from_cmdline(['a=b', 'x=y'])
2649
section = self.get_section()
2650
self.assertEquals('b', section.get('a'))
2651
self.assertEquals('y', section.get('x'))
2653
def test_wrong_syntax(self):
2654
self.assertRaises(errors.BzrCommandError,
2655
self.store._from_cmdline, ['a=b', 'c'])
2658
class TestStore(tests.TestCaseWithTransport):
2660
def assertSectionContent(self, expected, (store, section)):
2661
"""Assert that some options have the proper values in a section."""
2662
expected_name, expected_options = expected
2663
self.assertEquals(expected_name, section.id)
2666
dict([(k, section.get(k)) for k in expected_options.keys()]))
2669
class TestReadonlyStore(TestStore):
2671
scenarios = [(key, {'get_store': builder}) for key, builder
2672
in config.test_store_builder_registry.iteritems()]
2674
def test_building_delays_load(self):
2675
store = self.get_store(self)
2676
self.assertEquals(False, store.is_loaded())
2677
store._load_from_string('')
2678
self.assertEquals(True, store.is_loaded())
2680
def test_get_no_sections_for_empty(self):
2681
store = self.get_store(self)
2682
store._load_from_string('')
2683
self.assertEquals([], list(store.get_sections()))
2685
def test_get_default_section(self):
2686
store = self.get_store(self)
2687
store._load_from_string('foo=bar')
2688
sections = list(store.get_sections())
2689
self.assertLength(1, sections)
2690
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2692
def test_get_named_section(self):
2693
store = self.get_store(self)
2694
store._load_from_string('[baz]\nfoo=bar')
2695
sections = list(store.get_sections())
2696
self.assertLength(1, sections)
2697
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2699
def test_load_from_string_fails_for_non_empty_store(self):
2700
store = self.get_store(self)
2701
store._load_from_string('foo=bar')
2702
self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2705
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2706
"""Simulate loading a config store with content of various encodings.
2708
All files produced by bzr are in utf8 content.
2710
Users may modify them manually and end up with a file that can't be
2711
loaded. We need to issue proper error messages in this case.
2714
invalid_utf8_char = '\xff'
2716
def test_load_utf8(self):
2717
"""Ensure we can load an utf8-encoded file."""
2718
t = self.get_transport()
2719
# From http://pad.lv/799212
2720
unicode_user = u'b\N{Euro Sign}ar'
2721
unicode_content = u'user=%s' % (unicode_user,)
2722
utf8_content = unicode_content.encode('utf8')
2723
# Store the raw content in the config file
2724
t.put_bytes('foo.conf', utf8_content)
2725
store = config.TransportIniFileStore(t, 'foo.conf')
2727
stack = config.Stack([store.get_sections], store)
2728
self.assertEquals(unicode_user, stack.get('user'))
2730
def test_load_non_ascii(self):
2731
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2732
t = self.get_transport()
2733
t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2734
store = config.TransportIniFileStore(t, 'foo.conf')
2735
self.assertRaises(errors.ConfigContentError, store.load)
2737
def test_load_erroneous_content(self):
2738
"""Ensure we display a proper error on content that can't be parsed."""
2739
t = self.get_transport()
2740
t.put_bytes('foo.conf', '[open_section\n')
2741
store = config.TransportIniFileStore(t, 'foo.conf')
2742
self.assertRaises(errors.ParseConfigError, store.load)
2744
def test_load_permission_denied(self):
2745
"""Ensure we get warned when trying to load an inaccessible file."""
2748
warnings.append(args[0] % args[1:])
2749
self.overrideAttr(trace, 'warning', warning)
2751
t = self.get_transport()
2753
def get_bytes(relpath):
2754
raise errors.PermissionDenied(relpath, "")
2755
t.get_bytes = get_bytes
2756
store = config.TransportIniFileStore(t, 'foo.conf')
2757
self.assertRaises(errors.PermissionDenied, store.load)
2760
[u'Permission denied while trying to load configuration store %s.'
2761
% store.external_url()])
2764
class TestIniConfigContent(tests.TestCaseWithTransport):
2765
"""Simulate loading a IniBasedConfig with content of various encodings.
2767
All files produced by bzr are in utf8 content.
2769
Users may modify them manually and end up with a file that can't be
2770
loaded. We need to issue proper error messages in this case.
2773
invalid_utf8_char = '\xff'
2775
def test_load_utf8(self):
2776
"""Ensure we can load an utf8-encoded file."""
2777
# From http://pad.lv/799212
2778
unicode_user = u'b\N{Euro Sign}ar'
2779
unicode_content = u'user=%s' % (unicode_user,)
2780
utf8_content = unicode_content.encode('utf8')
2781
# Store the raw content in the config file
2782
with open('foo.conf', 'wb') as f:
2783
f.write(utf8_content)
2784
conf = config.IniBasedConfig(file_name='foo.conf')
2785
self.assertEquals(unicode_user, conf.get_user_option('user'))
2787
def test_load_badly_encoded_content(self):
2788
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2789
with open('foo.conf', 'wb') as f:
2790
f.write('user=foo\n#%s\n' % (self.invalid_utf8_char,))
2791
conf = config.IniBasedConfig(file_name='foo.conf')
2792
self.assertRaises(errors.ConfigContentError, conf._get_parser)
2794
def test_load_erroneous_content(self):
2795
"""Ensure we display a proper error on content that can't be parsed."""
2796
with open('foo.conf', 'wb') as f:
2797
f.write('[open_section\n')
2798
conf = config.IniBasedConfig(file_name='foo.conf')
2799
self.assertRaises(errors.ParseConfigError, conf._get_parser)
2802
class TestMutableStore(TestStore):
2804
scenarios = [(key, {'store_id': key, 'get_store': builder}) for key, builder
2805
in config.test_store_builder_registry.iteritems()]
2808
super(TestMutableStore, self).setUp()
2809
self.transport = self.get_transport()
2811
def has_store(self, store):
2812
store_basename = urlutils.relative_url(self.transport.external_url(),
2813
store.external_url())
2814
return self.transport.has(store_basename)
2816
def test_save_empty_creates_no_file(self):
2817
# FIXME: There should be a better way than relying on the test
2818
# parametrization to identify branch.conf -- vila 2011-0526
2819
if self.store_id in ('branch', 'remote_branch'):
2820
raise tests.TestNotApplicable(
2821
'branch.conf is *always* created when a branch is initialized')
2822
store = self.get_store(self)
2824
self.assertEquals(False, self.has_store(store))
2826
def test_save_emptied_succeeds(self):
2827
store = self.get_store(self)
2828
store._load_from_string('foo=bar\n')
2829
section = store.get_mutable_section(None)
2830
section.remove('foo')
2832
self.assertEquals(True, self.has_store(store))
2833
modified_store = self.get_store(self)
2834
sections = list(modified_store.get_sections())
2835
self.assertLength(0, sections)
2837
def test_save_with_content_succeeds(self):
2838
# FIXME: There should be a better way than relying on the test
2839
# parametrization to identify branch.conf -- vila 2011-0526
2840
if self.store_id in ('branch', 'remote_branch'):
2841
raise tests.TestNotApplicable(
2842
'branch.conf is *always* created when a branch is initialized')
2843
store = self.get_store(self)
2844
store._load_from_string('foo=bar\n')
2845
self.assertEquals(False, self.has_store(store))
2847
self.assertEquals(True, self.has_store(store))
2848
modified_store = self.get_store(self)
2849
sections = list(modified_store.get_sections())
2850
self.assertLength(1, sections)
2851
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2853
def test_set_option_in_empty_store(self):
2854
store = self.get_store(self)
2855
section = store.get_mutable_section(None)
2856
section.set('foo', 'bar')
2858
modified_store = self.get_store(self)
2859
sections = list(modified_store.get_sections())
2860
self.assertLength(1, sections)
2861
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2863
def test_set_option_in_default_section(self):
2864
store = self.get_store(self)
2865
store._load_from_string('')
2866
section = store.get_mutable_section(None)
2867
section.set('foo', 'bar')
2869
modified_store = self.get_store(self)
2870
sections = list(modified_store.get_sections())
2871
self.assertLength(1, sections)
2872
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2874
def test_set_option_in_named_section(self):
2875
store = self.get_store(self)
2876
store._load_from_string('')
2877
section = store.get_mutable_section('baz')
2878
section.set('foo', 'bar')
2880
modified_store = self.get_store(self)
2881
sections = list(modified_store.get_sections())
2882
self.assertLength(1, sections)
2883
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2885
def test_load_hook(self):
2886
# We first needs to ensure that the store exists
2887
store = self.get_store(self)
2888
section = store.get_mutable_section('baz')
2889
section.set('foo', 'bar')
2891
# Now we can try to load it
2892
store = self.get_store(self)
2896
config.ConfigHooks.install_named_hook('load', hook, None)
2897
self.assertLength(0, calls)
2899
self.assertLength(1, calls)
2900
self.assertEquals((store,), calls[0])
2902
def test_save_hook(self):
2906
config.ConfigHooks.install_named_hook('save', hook, None)
2907
self.assertLength(0, calls)
2908
store = self.get_store(self)
2909
section = store.get_mutable_section('baz')
2910
section.set('foo', 'bar')
2912
self.assertLength(1, calls)
2913
self.assertEquals((store,), calls[0])
2916
class TestTransportIniFileStore(TestStore):
2918
def test_loading_unknown_file_fails(self):
2919
store = config.TransportIniFileStore(self.get_transport(),
2921
self.assertRaises(errors.NoSuchFile, store.load)
2923
def test_invalid_content(self):
2924
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
2925
self.assertEquals(False, store.is_loaded())
2926
exc = self.assertRaises(
2927
errors.ParseConfigError, store._load_from_string,
2928
'this is invalid !')
2929
self.assertEndsWith(exc.filename, 'foo.conf')
2930
# And the load failed
2931
self.assertEquals(False, store.is_loaded())
2933
def test_get_embedded_sections(self):
2934
# A more complicated example (which also shows that section names and
2935
# option names share the same name space...)
2936
# FIXME: This should be fixed by forbidding dicts as values ?
2937
# -- vila 2011-04-05
2938
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
2939
store._load_from_string('''
2943
foo_in_DEFAULT=foo_DEFAULT
2951
sections = list(store.get_sections())
2952
self.assertLength(4, sections)
2953
# The default section has no name.
2954
# List values are provided as strings and need to be explicitly
2955
# converted by specifying from_unicode=list_from_store at option
2957
self.assertSectionContent((None, {'foo': 'bar', 'l': u'1,2'}),
2959
self.assertSectionContent(
2960
('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
2961
self.assertSectionContent(
2962
('bar', {'foo_in_bar': 'barbar'}), sections[2])
2963
# sub sections are provided as embedded dicts.
2964
self.assertSectionContent(
2965
('baz', {'foo_in_baz': 'barbaz', 'qux': {'foo_in_qux': 'quux'}}),
2969
class TestLockableIniFileStore(TestStore):
2971
def test_create_store_in_created_dir(self):
2972
self.assertPathDoesNotExist('dir')
2973
t = self.get_transport('dir/subdir')
2974
store = config.LockableIniFileStore(t, 'foo.conf')
2975
store.get_mutable_section(None).set('foo', 'bar')
2977
self.assertPathExists('dir/subdir')
2980
class TestConcurrentStoreUpdates(TestStore):
2981
"""Test that Stores properly handle conccurent updates.
2983
New Store implementation may fail some of these tests but until such
2984
implementations exist it's hard to properly filter them from the scenarios
2985
applied here. If you encounter such a case, contact the bzr devs.
2988
scenarios = [(key, {'get_stack': builder}) for key, builder
2989
in config.test_stack_builder_registry.iteritems()]
2992
super(TestConcurrentStoreUpdates, self).setUp()
2993
self.stack = self.get_stack(self)
2994
if not isinstance(self.stack, config._CompatibleStack):
2995
raise tests.TestNotApplicable(
2996
'%s is not meant to be compatible with the old config design'
2998
self.stack.set('one', '1')
2999
self.stack.set('two', '2')
3001
self.stack.store.save()
3003
def test_simple_read_access(self):
3004
self.assertEquals('1', self.stack.get('one'))
3006
def test_simple_write_access(self):
3007
self.stack.set('one', 'one')
3008
self.assertEquals('one', self.stack.get('one'))
3010
def test_listen_to_the_last_speaker(self):
3012
c2 = self.get_stack(self)
3013
c1.set('one', 'ONE')
3014
c2.set('two', 'TWO')
3015
self.assertEquals('ONE', c1.get('one'))
3016
self.assertEquals('TWO', c2.get('two'))
3017
# The second update respect the first one
3018
self.assertEquals('ONE', c2.get('one'))
3020
def test_last_speaker_wins(self):
3021
# If the same config is not shared, the same variable modified twice
3022
# can only see a single result.
3024
c2 = self.get_stack(self)
3027
self.assertEquals('c2', c2.get('one'))
3028
# The first modification is still available until another refresh
3030
self.assertEquals('c1', c1.get('one'))
3031
c1.set('two', 'done')
3032
self.assertEquals('c2', c1.get('one'))
3034
def test_writes_are_serialized(self):
3036
c2 = self.get_stack(self)
3038
# We spawn a thread that will pause *during* the config saving.
3039
before_writing = threading.Event()
3040
after_writing = threading.Event()
3041
writing_done = threading.Event()
3042
c1_save_without_locking_orig = c1.store.save_without_locking
3043
def c1_save_without_locking():
3044
before_writing.set()
3045
c1_save_without_locking_orig()
3046
# The lock is held. We wait for the main thread to decide when to
3048
after_writing.wait()
3049
c1.store.save_without_locking = c1_save_without_locking
3053
t1 = threading.Thread(target=c1_set)
3054
# Collect the thread after the test
3055
self.addCleanup(t1.join)
3056
# Be ready to unblock the thread if the test goes wrong
3057
self.addCleanup(after_writing.set)
3059
before_writing.wait()
3060
self.assertRaises(errors.LockContention,
3061
c2.set, 'one', 'c2')
3062
self.assertEquals('c1', c1.get('one'))
3063
# Let the lock be released
3067
self.assertEquals('c2', c2.get('one'))
3069
def test_read_while_writing(self):
3071
# We spawn a thread that will pause *during* the write
3072
ready_to_write = threading.Event()
3073
do_writing = threading.Event()
3074
writing_done = threading.Event()
3075
# We override the _save implementation so we know the store is locked
3076
c1_save_without_locking_orig = c1.store.save_without_locking
3077
def c1_save_without_locking():
3078
ready_to_write.set()
3079
# The lock is held. We wait for the main thread to decide when to
3082
c1_save_without_locking_orig()
3084
c1.store.save_without_locking = c1_save_without_locking
3087
t1 = threading.Thread(target=c1_set)
3088
# Collect the thread after the test
3089
self.addCleanup(t1.join)
3090
# Be ready to unblock the thread if the test goes wrong
3091
self.addCleanup(do_writing.set)
3093
# Ensure the thread is ready to write
3094
ready_to_write.wait()
3095
self.assertEquals('c1', c1.get('one'))
3096
# If we read during the write, we get the old value
3097
c2 = self.get_stack(self)
3098
self.assertEquals('1', c2.get('one'))
3099
# Let the writing occur and ensure it occurred
3102
# Now we get the updated value
3103
c3 = self.get_stack(self)
3104
self.assertEquals('c1', c3.get('one'))
3106
# FIXME: It may be worth looking into removing the lock dir when it's not
3107
# needed anymore and look at possible fallouts for concurrent lockers. This
3108
# will matter if/when we use config files outside of bazaar directories
3109
# (.bazaar or .bzr) -- vila 20110-04-111
3112
class TestSectionMatcher(TestStore):
3114
scenarios = [('location', {'matcher': config.LocationMatcher}),
3115
('id', {'matcher': config.NameMatcher}),]
3118
super(TestSectionMatcher, self).setUp()
3119
# Any simple store is good enough
3120
self.get_store = config.test_store_builder_registry.get('configobj')
3122
def test_no_matches_for_empty_stores(self):
3123
store = self.get_store(self)
3124
store._load_from_string('')
3125
matcher = self.matcher(store, '/bar')
3126
self.assertEquals([], list(matcher.get_sections()))
3128
def test_build_doesnt_load_store(self):
3129
store = self.get_store(self)
3130
matcher = self.matcher(store, '/bar')
3131
self.assertFalse(store.is_loaded())
3134
class TestLocationSection(tests.TestCase):
3136
def get_section(self, options, extra_path):
3137
section = config.Section('foo', options)
3138
# We don't care about the length so we use '0'
3139
return config.LocationSection(section, 0, extra_path)
3141
def test_simple_option(self):
3142
section = self.get_section({'foo': 'bar'}, '')
3143
self.assertEquals('bar', section.get('foo'))
3145
def test_option_with_extra_path(self):
3146
section = self.get_section({'foo': 'bar', 'foo:policy': 'appendpath'},
3148
self.assertEquals('bar/baz', section.get('foo'))
3150
def test_invalid_policy(self):
3151
section = self.get_section({'foo': 'bar', 'foo:policy': 'die'},
3153
# invalid policies are ignored
3154
self.assertEquals('bar', section.get('foo'))
3157
class TestLocationMatcher(TestStore):
3160
super(TestLocationMatcher, self).setUp()
3161
# Any simple store is good enough
3162
self.get_store = config.test_store_builder_registry.get('configobj')
3164
def test_unrelated_section_excluded(self):
3165
store = self.get_store(self)
3166
store._load_from_string('''
3174
section=/foo/bar/baz
3178
self.assertEquals(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
3180
[section.id for _, section in store.get_sections()])
3181
matcher = config.LocationMatcher(store, '/foo/bar/quux')
3182
sections = [section for s, section in matcher.get_sections()]
3183
self.assertEquals([3, 2],
3184
[section.length for section in sections])
3185
self.assertEquals(['/foo/bar', '/foo'],
3186
[section.id for section in sections])
3187
self.assertEquals(['quux', 'bar/quux'],
3188
[section.extra_path for section in sections])
3190
def test_more_specific_sections_first(self):
3191
store = self.get_store(self)
3192
store._load_from_string('''
3198
self.assertEquals(['/foo', '/foo/bar'],
3199
[section.id for _, section in store.get_sections()])
3200
matcher = config.LocationMatcher(store, '/foo/bar/baz')
3201
sections = [section for s, section in matcher.get_sections()]
3202
self.assertEquals([3, 2],
3203
[section.length for section in sections])
3204
self.assertEquals(['/foo/bar', '/foo'],
3205
[section.id for section in sections])
3206
self.assertEquals(['baz', 'bar/baz'],
3207
[section.extra_path for section in sections])
3209
def test_appendpath_in_no_name_section(self):
3210
# It's a bit weird to allow appendpath in a no-name section, but
3211
# someone may found a use for it
3212
store = self.get_store(self)
3213
store._load_from_string('''
3215
foo:policy = appendpath
3217
matcher = config.LocationMatcher(store, 'dir/subdir')
3218
sections = list(matcher.get_sections())
3219
self.assertLength(1, sections)
3220
self.assertEquals('bar/dir/subdir', sections[0][1].get('foo'))
3222
def test_file_urls_are_normalized(self):
3223
store = self.get_store(self)
3224
if sys.platform == 'win32':
3225
expected_url = 'file:///C:/dir/subdir'
3226
expected_location = 'C:/dir/subdir'
3228
expected_url = 'file:///dir/subdir'
3229
expected_location = '/dir/subdir'
3230
matcher = config.LocationMatcher(store, expected_url)
3231
self.assertEquals(expected_location, matcher.location)
3234
class TestNameMatcher(TestStore):
3237
super(TestNameMatcher, self).setUp()
3238
self.matcher = config.NameMatcher
3239
# Any simple store is good enough
3240
self.get_store = config.test_store_builder_registry.get('configobj')
3242
def get_matching_sections(self, name):
3243
store = self.get_store(self)
3244
store._load_from_string('''
3252
matcher = self.matcher(store, name)
3253
return list(matcher.get_sections())
3255
def test_matching(self):
3256
sections = self.get_matching_sections('foo')
3257
self.assertLength(1, sections)
3258
self.assertSectionContent(('foo', {'option': 'foo'}), sections[0])
3260
def test_not_matching(self):
3261
sections = self.get_matching_sections('baz')
3262
self.assertLength(0, sections)
3265
class TestStackGet(tests.TestCase):
3267
# FIXME: This should be parametrized for all known Stack or dedicated
3268
# paramerized tests created to avoid bloating -- vila 2011-03-31
3270
def overrideOptionRegistry(self):
3271
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3273
def test_single_config_get(self):
3274
conf = dict(foo='bar')
3275
conf_stack = config.Stack([conf])
3276
self.assertEquals('bar', conf_stack.get('foo'))
3278
def test_get_with_registered_default_value(self):
3279
conf_stack = config.Stack([dict()])
3280
opt = config.Option('foo', default='bar')
3281
self.overrideOptionRegistry()
3282
config.option_registry.register('foo', opt)
3283
self.assertEquals('bar', conf_stack.get('foo'))
3285
def test_get_without_registered_default_value(self):
3286
conf_stack = config.Stack([dict()])
3287
opt = config.Option('foo')
3288
self.overrideOptionRegistry()
3289
config.option_registry.register('foo', opt)
3290
self.assertEquals(None, conf_stack.get('foo'))
3292
def test_get_without_default_value_for_not_registered(self):
3293
conf_stack = config.Stack([dict()])
3294
opt = config.Option('foo')
3295
self.overrideOptionRegistry()
3296
self.assertEquals(None, conf_stack.get('foo'))
3298
def test_get_first_definition(self):
3299
conf1 = dict(foo='bar')
3300
conf2 = dict(foo='baz')
3301
conf_stack = config.Stack([conf1, conf2])
3302
self.assertEquals('bar', conf_stack.get('foo'))
3304
def test_get_embedded_definition(self):
3305
conf1 = dict(yy='12')
3306
conf2 = config.Stack([dict(xx='42'), dict(foo='baz')])
3307
conf_stack = config.Stack([conf1, conf2])
3308
self.assertEquals('baz', conf_stack.get('foo'))
3310
def test_get_for_empty_section_callable(self):
3311
conf_stack = config.Stack([lambda : []])
3312
self.assertEquals(None, conf_stack.get('foo'))
3314
def test_get_for_broken_callable(self):
3315
# Trying to use and invalid callable raises an exception on first use
3316
conf_stack = config.Stack([lambda : object()])
3317
self.assertRaises(TypeError, conf_stack.get, 'foo')
3320
class TestStackWithTransport(tests.TestCaseWithTransport):
3322
scenarios = [(key, {'get_stack': builder}) for key, builder
3323
in config.test_stack_builder_registry.iteritems()]
3326
class TestConcreteStacks(TestStackWithTransport):
3328
def test_build_stack(self):
3329
# Just a smoke test to help debug builders
3330
stack = self.get_stack(self)
3333
class TestStackGet(TestStackWithTransport):
3336
super(TestStackGet, self).setUp()
3337
self.conf = self.get_stack(self)
3339
def test_get_for_empty_stack(self):
3340
self.assertEquals(None, self.conf.get('foo'))
3342
def test_get_hook(self):
3343
self.conf.set('foo', 'bar')
3347
config.ConfigHooks.install_named_hook('get', hook, None)
3348
self.assertLength(0, calls)
3349
value = self.conf.get('foo')
3350
self.assertEquals('bar', value)
3351
self.assertLength(1, calls)
3352
self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
3355
class TestStackGetWithConverter(tests.TestCaseWithTransport):
3358
super(TestStackGetWithConverter, self).setUp()
3359
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3360
self.registry = config.option_registry
3361
# We just want a simple stack with a simple store so we can inject
3362
# whatever content the tests need without caring about what section
3363
# names are valid for a given store/stack.
3364
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3365
self.conf = config.Stack([store.get_sections], store)
3367
def register_bool_option(self, name, default=None, default_from_env=None):
3368
b = config.Option(name, help='A boolean.',
3369
default=default, default_from_env=default_from_env,
3370
from_unicode=config.bool_from_store)
3371
self.registry.register(b)
3373
def test_get_default_bool_None(self):
3374
self.register_bool_option('foo')
3375
self.assertEquals(None, self.conf.get('foo'))
3377
def test_get_default_bool_True(self):
3378
self.register_bool_option('foo', u'True')
3379
self.assertEquals(True, self.conf.get('foo'))
3381
def test_get_default_bool_False(self):
3382
self.register_bool_option('foo', False)
3383
self.assertEquals(False, self.conf.get('foo'))
3385
def test_get_default_bool_False_as_string(self):
3386
self.register_bool_option('foo', u'False')
3387
self.assertEquals(False, self.conf.get('foo'))
3389
def test_get_default_bool_from_env_converted(self):
3390
self.register_bool_option('foo', u'True', default_from_env=['FOO'])
3391
self.overrideEnv('FOO', 'False')
3392
self.assertEquals(False, self.conf.get('foo'))
3394
def test_get_default_bool_when_conversion_fails(self):
3395
self.register_bool_option('foo', default='True')
3396
self.conf.store._load_from_string('foo=invalid boolean')
3397
self.assertEquals(True, self.conf.get('foo'))
3399
def register_integer_option(self, name,
3400
default=None, default_from_env=None):
3401
i = config.Option(name, help='An integer.',
3402
default=default, default_from_env=default_from_env,
3403
from_unicode=config.int_from_store)
3404
self.registry.register(i)
3406
def test_get_default_integer_None(self):
3407
self.register_integer_option('foo')
3408
self.assertEquals(None, self.conf.get('foo'))
3410
def test_get_default_integer(self):
3411
self.register_integer_option('foo', 42)
3412
self.assertEquals(42, self.conf.get('foo'))
3414
def test_get_default_integer_as_string(self):
3415
self.register_integer_option('foo', u'42')
3416
self.assertEquals(42, self.conf.get('foo'))
3418
def test_get_default_integer_from_env(self):
3419
self.register_integer_option('foo', default_from_env=['FOO'])
3420
self.overrideEnv('FOO', '18')
3421
self.assertEquals(18, self.conf.get('foo'))
3423
def test_get_default_integer_when_conversion_fails(self):
3424
self.register_integer_option('foo', default='12')
3425
self.conf.store._load_from_string('foo=invalid integer')
3426
self.assertEquals(12, self.conf.get('foo'))
3428
def register_list_option(self, name, default=None, default_from_env=None):
3429
l = config.Option(name, help='A list.',
3430
default=default, default_from_env=default_from_env,
3431
from_unicode=config.list_from_store)
3432
self.registry.register(l)
3434
def test_get_default_list_None(self):
3435
self.register_list_option('foo')
3436
self.assertEquals(None, self.conf.get('foo'))
3438
def test_get_default_list_empty(self):
3439
self.register_list_option('foo', '')
3440
self.assertEquals([], self.conf.get('foo'))
3442
def test_get_default_list_from_env(self):
3443
self.register_list_option('foo', default_from_env=['FOO'])
3444
self.overrideEnv('FOO', '')
3445
self.assertEquals([], self.conf.get('foo'))
3447
def test_get_with_list_converter_no_item(self):
3448
self.register_list_option('foo', None)
3449
self.conf.store._load_from_string('foo=,')
3450
self.assertEquals([], self.conf.get('foo'))
3452
def test_get_with_list_converter_many_items(self):
3453
self.register_list_option('foo', None)
3454
self.conf.store._load_from_string('foo=m,o,r,e')
3455
self.assertEquals(['m', 'o', 'r', 'e'], self.conf.get('foo'))
3457
def test_get_with_list_converter_embedded_spaces_many_items(self):
3458
self.register_list_option('foo', None)
3459
self.conf.store._load_from_string('foo=" bar", "baz "')
3460
self.assertEquals([' bar', 'baz '], self.conf.get('foo'))
3462
def test_get_with_list_converter_stripped_spaces_many_items(self):
3463
self.register_list_option('foo', None)
3464
self.conf.store._load_from_string('foo= bar , baz ')
3465
self.assertEquals(['bar', 'baz'], self.conf.get('foo'))
3468
class TestIterOptionRefs(tests.TestCase):
3469
"""iter_option_refs is a bit unusual, document some cases."""
3471
def assertRefs(self, expected, string):
3472
self.assertEquals(expected, list(config.iter_option_refs(string)))
3474
def test_empty(self):
3475
self.assertRefs([(False, '')], '')
3477
def test_no_refs(self):
3478
self.assertRefs([(False, 'foo bar')], 'foo bar')
3480
def test_single_ref(self):
3481
self.assertRefs([(False, ''), (True, '{foo}'), (False, '')], '{foo}')
3483
def test_broken_ref(self):
3484
self.assertRefs([(False, '{foo')], '{foo')
3486
def test_embedded_ref(self):
3487
self.assertRefs([(False, '{'), (True, '{foo}'), (False, '}')],
3490
def test_two_refs(self):
3491
self.assertRefs([(False, ''), (True, '{foo}'),
3492
(False, ''), (True, '{bar}'),
3497
class TestStackExpandOptions(tests.TestCaseWithTransport):
3500
super(TestStackExpandOptions, self).setUp()
3501
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3502
self.registry = config.option_registry
3503
self.conf = build_branch_stack(self)
3505
def assertExpansion(self, expected, string, env=None):
3506
self.assertEquals(expected, self.conf.expand_options(string, env))
3508
def test_no_expansion(self):
3509
self.assertExpansion('foo', 'foo')
3511
def test_expand_default_value(self):
3512
self.conf.store._load_from_string('bar=baz')
3513
self.registry.register(config.Option('foo', default=u'{bar}'))
3514
self.assertEquals('baz', self.conf.get('foo', expand=True))
3516
def test_expand_default_from_env(self):
3517
self.conf.store._load_from_string('bar=baz')
3518
self.registry.register(config.Option('foo', default_from_env=['FOO']))
3519
self.overrideEnv('FOO', '{bar}')
3520
self.assertEquals('baz', self.conf.get('foo', expand=True))
3522
def test_expand_default_on_failed_conversion(self):
3523
self.conf.store._load_from_string('baz=bogus\nbar=42\nfoo={baz}')
3524
self.registry.register(
3525
config.Option('foo', default=u'{bar}',
3526
from_unicode=config.int_from_store))
3527
self.assertEquals(42, self.conf.get('foo', expand=True))
3529
def test_env_adding_options(self):
3530
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3532
def test_env_overriding_options(self):
3533
self.conf.store._load_from_string('foo=baz')
3534
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3536
def test_simple_ref(self):
3537
self.conf.store._load_from_string('foo=xxx')
3538
self.assertExpansion('xxx', '{foo}')
3540
def test_unknown_ref(self):
3541
self.assertRaises(errors.ExpandingUnknownOption,
3542
self.conf.expand_options, '{foo}')
3544
def test_indirect_ref(self):
3545
self.conf.store._load_from_string('''
3549
self.assertExpansion('xxx', '{bar}')
3551
def test_embedded_ref(self):
3552
self.conf.store._load_from_string('''
3556
self.assertExpansion('xxx', '{{bar}}')
3558
def test_simple_loop(self):
3559
self.conf.store._load_from_string('foo={foo}')
3560
self.assertRaises(errors.OptionExpansionLoop,
3561
self.conf.expand_options, '{foo}')
3563
def test_indirect_loop(self):
3564
self.conf.store._load_from_string('''
3568
e = self.assertRaises(errors.OptionExpansionLoop,
3569
self.conf.expand_options, '{foo}')
3570
self.assertEquals('foo->bar->baz', e.refs)
3571
self.assertEquals('{foo}', e.string)
3573
def test_list(self):
3574
self.conf.store._load_from_string('''
3578
list={foo},{bar},{baz}
3580
self.registry.register(
3581
config.Option('list', from_unicode=config.list_from_store))
3582
self.assertEquals(['start', 'middle', 'end'],
3583
self.conf.get('list', expand=True))
3585
def test_cascading_list(self):
3586
self.conf.store._load_from_string('''
3592
self.registry.register(
3593
config.Option('list', from_unicode=config.list_from_store))
3594
self.assertEquals(['start', 'middle', 'end'],
3595
self.conf.get('list', expand=True))
3597
def test_pathologically_hidden_list(self):
3598
self.conf.store._load_from_string('''
3604
hidden={start}{middle}{end}
3606
# What matters is what the registration says, the conversion happens
3607
# only after all expansions have been performed
3608
self.registry.register(
3609
config.Option('hidden', from_unicode=config.list_from_store))
3610
self.assertEquals(['bin', 'go'],
3611
self.conf.get('hidden', expand=True))
3614
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
3617
super(TestStackCrossSectionsExpand, self).setUp()
3619
def get_config(self, location, string):
3622
# Since we don't save the config we won't strictly require to inherit
3623
# from TestCaseInTempDir, but an error occurs so quickly...
3624
c = config.LocationStack(location)
3625
c.store._load_from_string(string)
3628
def test_dont_cross_unrelated_section(self):
3629
c = self.get_config('/another/branch/path','''
3634
[/another/branch/path]
3637
self.assertRaises(errors.ExpandingUnknownOption,
3638
c.get, 'bar', expand=True)
3640
def test_cross_related_sections(self):
3641
c = self.get_config('/project/branch/path','''
3645
[/project/branch/path]
3648
self.assertEquals('quux', c.get('bar', expand=True))
3651
class TestStackCrossStoresExpand(tests.TestCaseWithTransport):
3653
def test_cross_global_locations(self):
3654
l_store = config.LocationStore()
3655
l_store._load_from_string('''
3661
g_store = config.GlobalStore()
3662
g_store._load_from_string('''
3668
stack = config.LocationStack('/branch')
3669
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
3670
self.assertEquals('loc-foo', stack.get('gfoo', expand=True))
3673
class TestStackExpandSectionLocals(tests.TestCaseWithTransport):
3675
def test_expand_locals_empty(self):
3676
l_store = config.LocationStore()
3677
l_store._load_from_string('''
3678
[/home/user/project]
3683
stack = config.LocationStack('/home/user/project/')
3684
self.assertEquals('', stack.get('base', expand=True))
3685
self.assertEquals('', stack.get('rel', expand=True))
3687
def test_expand_basename_locally(self):
3688
l_store = config.LocationStore()
3689
l_store._load_from_string('''
3690
[/home/user/project]
3694
stack = config.LocationStack('/home/user/project/branch')
3695
self.assertEquals('branch', stack.get('bfoo', expand=True))
3697
def test_expand_basename_locally_longer_path(self):
3698
l_store = config.LocationStore()
3699
l_store._load_from_string('''
3704
stack = config.LocationStack('/home/user/project/dir/branch')
3705
self.assertEquals('branch', stack.get('bfoo', expand=True))
3707
def test_expand_relpath_locally(self):
3708
l_store = config.LocationStore()
3709
l_store._load_from_string('''
3710
[/home/user/project]
3711
lfoo = loc-foo/{relpath}
3714
stack = config.LocationStack('/home/user/project/branch')
3715
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
3717
def test_expand_relpath_unknonw_in_global(self):
3718
g_store = config.GlobalStore()
3719
g_store._load_from_string('''
3724
stack = config.LocationStack('/home/user/project/branch')
3725
self.assertRaises(errors.ExpandingUnknownOption,
3726
stack.get, 'gfoo', expand=True)
3728
def test_expand_local_option_locally(self):
3729
l_store = config.LocationStore()
3730
l_store._load_from_string('''
3731
[/home/user/project]
3732
lfoo = loc-foo/{relpath}
3736
g_store = config.GlobalStore()
3737
g_store._load_from_string('''
3743
stack = config.LocationStack('/home/user/project/branch')
3744
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
3745
self.assertEquals('loc-foo/branch', stack.get('gfoo', expand=True))
3747
def test_locals_dont_leak(self):
3748
"""Make sure we chose the right local in presence of several sections.
3750
l_store = config.LocationStore()
3751
l_store._load_from_string('''
3753
lfoo = loc-foo/{relpath}
3754
[/home/user/project]
3755
lfoo = loc-foo/{relpath}
3758
stack = config.LocationStack('/home/user/project/branch')
3759
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
3760
stack = config.LocationStack('/home/user/bar/baz')
3761
self.assertEquals('loc-foo/bar/baz', stack.get('lfoo', expand=True))
3765
class TestStackSet(TestStackWithTransport):
3767
def test_simple_set(self):
3768
conf = self.get_stack(self)
3769
self.assertEquals(None, conf.get('foo'))
3770
conf.set('foo', 'baz')
3771
# Did we get it back ?
3772
self.assertEquals('baz', conf.get('foo'))
3774
def test_set_creates_a_new_section(self):
3775
conf = self.get_stack(self)
3776
conf.set('foo', 'baz')
3777
self.assertEquals, 'baz', conf.get('foo')
3779
def test_set_hook(self):
3783
config.ConfigHooks.install_named_hook('set', hook, None)
3784
self.assertLength(0, calls)
3785
conf = self.get_stack(self)
3786
conf.set('foo', 'bar')
3787
self.assertLength(1, calls)
3788
self.assertEquals((conf, 'foo', 'bar'), calls[0])
3791
class TestStackRemove(TestStackWithTransport):
3793
def test_remove_existing(self):
3794
conf = self.get_stack(self)
3795
conf.set('foo', 'bar')
3796
self.assertEquals('bar', conf.get('foo'))
3798
# Did we get it back ?
3799
self.assertEquals(None, conf.get('foo'))
3801
def test_remove_unknown(self):
3802
conf = self.get_stack(self)
3803
self.assertRaises(KeyError, conf.remove, 'I_do_not_exist')
3805
def test_remove_hook(self):
3809
config.ConfigHooks.install_named_hook('remove', hook, None)
3810
self.assertLength(0, calls)
3811
conf = self.get_stack(self)
3812
conf.set('foo', 'bar')
3814
self.assertLength(1, calls)
3815
self.assertEquals((conf, 'foo'), calls[0])
3818
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
3821
super(TestConfigGetOptions, self).setUp()
3822
create_configs(self)
3824
def test_no_variable(self):
3825
# Using branch should query branch, locations and bazaar
3826
self.assertOptions([], self.branch_config)
3828
def test_option_in_bazaar(self):
3829
self.bazaar_config.set_user_option('file', 'bazaar')
3830
self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
3833
def test_option_in_locations(self):
3834
self.locations_config.set_user_option('file', 'locations')
3836
[('file', 'locations', self.tree.basedir, 'locations')],
3837
self.locations_config)
3839
def test_option_in_branch(self):
3840
self.branch_config.set_user_option('file', 'branch')
3841
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
3844
def test_option_in_bazaar_and_branch(self):
3845
self.bazaar_config.set_user_option('file', 'bazaar')
3846
self.branch_config.set_user_option('file', 'branch')
3847
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
3848
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3851
def test_option_in_branch_and_locations(self):
3852
# Hmm, locations override branch :-/
3853
self.locations_config.set_user_option('file', 'locations')
3854
self.branch_config.set_user_option('file', 'branch')
3856
[('file', 'locations', self.tree.basedir, 'locations'),
3857
('file', 'branch', 'DEFAULT', 'branch'),],
3860
def test_option_in_bazaar_locations_and_branch(self):
3861
self.bazaar_config.set_user_option('file', 'bazaar')
3862
self.locations_config.set_user_option('file', 'locations')
3863
self.branch_config.set_user_option('file', 'branch')
3865
[('file', 'locations', self.tree.basedir, 'locations'),
3866
('file', 'branch', 'DEFAULT', 'branch'),
3867
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3871
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
3874
super(TestConfigRemoveOption, self).setUp()
3875
create_configs_with_file_option(self)
3877
def test_remove_in_locations(self):
3878
self.locations_config.remove_user_option('file', self.tree.basedir)
3880
[('file', 'branch', 'DEFAULT', 'branch'),
3881
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3884
def test_remove_in_branch(self):
3885
self.branch_config.remove_user_option('file')
3887
[('file', 'locations', self.tree.basedir, 'locations'),
3888
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3891
def test_remove_in_bazaar(self):
3892
self.bazaar_config.remove_user_option('file')
3894
[('file', 'locations', self.tree.basedir, 'locations'),
3895
('file', 'branch', 'DEFAULT', 'branch'),],
3899
class TestConfigGetSections(tests.TestCaseWithTransport):
3902
super(TestConfigGetSections, self).setUp()
3903
create_configs(self)
3905
def assertSectionNames(self, expected, conf, name=None):
3906
"""Check which sections are returned for a given config.
3908
If fallback configurations exist their sections can be included.
3910
:param expected: A list of section names.
3912
:param conf: The configuration that will be queried.
3914
:param name: An optional section name that will be passed to
3917
sections = list(conf._get_sections(name))
3918
self.assertLength(len(expected), sections)
3919
self.assertEqual(expected, [name for name, _, _ in sections])
3921
def test_bazaar_default_section(self):
3922
self.assertSectionNames(['DEFAULT'], self.bazaar_config)
3924
def test_locations_default_section(self):
3925
# No sections are defined in an empty file
3926
self.assertSectionNames([], self.locations_config)
3928
def test_locations_named_section(self):
3929
self.locations_config.set_user_option('file', 'locations')
3930
self.assertSectionNames([self.tree.basedir], self.locations_config)
3932
def test_locations_matching_sections(self):
3933
loc_config = self.locations_config
3934
loc_config.set_user_option('file', 'locations')
3935
# We need to cheat a bit here to create an option in sections above and
3936
# below the 'location' one.
3937
parser = loc_config._get_parser()
3938
# locations.cong deals with '/' ignoring native os.sep
3939
location_names = self.tree.basedir.split('/')
3940
parent = '/'.join(location_names[:-1])
3941
child = '/'.join(location_names + ['child'])
3943
parser[parent]['file'] = 'parent'
3945
parser[child]['file'] = 'child'
3946
self.assertSectionNames([self.tree.basedir, parent], loc_config)
3948
def test_branch_data_default_section(self):
3949
self.assertSectionNames([None],
3950
self.branch_config._get_branch_data_config())
3952
def test_branch_default_sections(self):
3953
# No sections are defined in an empty locations file
3954
self.assertSectionNames([None, 'DEFAULT'],
3956
# Unless we define an option
3957
self.branch_config._get_location_config().set_user_option(
3958
'file', 'locations')
3959
self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
3962
def test_bazaar_named_section(self):
3963
# We need to cheat as the API doesn't give direct access to sections
3964
# other than DEFAULT.
3965
self.bazaar_config.set_alias('bazaar', 'bzr')
3966
self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
3969
class TestAuthenticationConfigFile(tests.TestCase):
3970
"""Test the authentication.conf file matching"""
3972
def _got_user_passwd(self, expected_user, expected_password,
3973
config, *args, **kwargs):
3974
credentials = config.get_credentials(*args, **kwargs)
3975
if credentials is None:
3979
user = credentials['user']
3980
password = credentials['password']
3981
self.assertEquals(expected_user, user)
3982
self.assertEquals(expected_password, password)
3984
def test_empty_config(self):
3985
conf = config.AuthenticationConfig(_file=StringIO())
3986
self.assertEquals({}, conf._get_config())
3987
self._got_user_passwd(None, None, conf, 'http', 'foo.net')
3989
def test_non_utf8_config(self):
3990
conf = config.AuthenticationConfig(_file=StringIO(
3992
self.assertRaises(errors.ConfigContentError, conf._get_config)
3994
def test_missing_auth_section_header(self):
3995
conf = config.AuthenticationConfig(_file=StringIO('foo = bar'))
3996
self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
3998
def test_auth_section_header_not_closed(self):
3999
conf = config.AuthenticationConfig(_file=StringIO('[DEF'))
4000
self.assertRaises(errors.ParseConfigError, conf._get_config)
4002
def test_auth_value_not_boolean(self):
4003
conf = config.AuthenticationConfig(_file=StringIO(
4007
verify_certificates=askme # Error: Not a boolean
4009
self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
4011
def test_auth_value_not_int(self):
4012
conf = config.AuthenticationConfig(_file=StringIO(
4016
port=port # Error: Not an int
4018
self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
4020
def test_unknown_password_encoding(self):
4021
conf = config.AuthenticationConfig(_file=StringIO(
4025
password_encoding=unknown
4027
self.assertRaises(ValueError, conf.get_password,
4028
'ftp', 'foo.net', 'joe')
4030
def test_credentials_for_scheme_host(self):
4031
conf = config.AuthenticationConfig(_file=StringIO(
4032
"""# Identity on foo.net
4037
password=secret-pass
4040
self._got_user_passwd('joe', 'secret-pass', conf, 'ftp', 'foo.net')
4042
self._got_user_passwd(None, None, conf, 'http', 'foo.net')
4044
self._got_user_passwd(None, None, conf, 'ftp', 'bar.net')
4046
def test_credentials_for_host_port(self):
4047
conf = config.AuthenticationConfig(_file=StringIO(
4048
"""# Identity on foo.net
4054
password=secret-pass
4057
self._got_user_passwd('joe', 'secret-pass',
4058
conf, 'ftp', 'foo.net', port=10021)
4060
self._got_user_passwd(None, None, conf, 'ftp', 'foo.net')
4062
def test_for_matching_host(self):
4063
conf = config.AuthenticationConfig(_file=StringIO(
4064
"""# Identity on foo.net
4070
[sourceforge domain]
4077
self._got_user_passwd('georges', 'bendover',
4078
conf, 'bzr', 'foo.bzr.sf.net')
4080
self._got_user_passwd(None, None,
4081
conf, 'bzr', 'bbzr.sf.net')
4083
def test_for_matching_host_None(self):
4084
conf = config.AuthenticationConfig(_file=StringIO(
4085
"""# Identity on foo.net
4095
self._got_user_passwd('joe', 'joepass',
4096
conf, 'bzr', 'quux.net')
4097
# no host but different scheme
4098
self._got_user_passwd('georges', 'bendover',
4099
conf, 'ftp', 'quux.net')
4101
def test_credentials_for_path(self):
4102
conf = config.AuthenticationConfig(_file=StringIO(
4118
self._got_user_passwd(None, None,
4119
conf, 'http', host='bar.org', path='/dir3')
4121
self._got_user_passwd('georges', 'bendover',
4122
conf, 'http', host='bar.org', path='/dir2')
4124
self._got_user_passwd('jim', 'jimpass',
4125
conf, 'http', host='bar.org',path='/dir1/subdir')
4127
def test_credentials_for_user(self):
4128
conf = config.AuthenticationConfig(_file=StringIO(
4137
self._got_user_passwd('jim', 'jimpass',
4138
conf, 'http', 'bar.org')
4140
self._got_user_passwd('jim', 'jimpass',
4141
conf, 'http', 'bar.org', user='jim')
4142
# Don't get a different user if one is specified
4143
self._got_user_passwd(None, None,
4144
conf, 'http', 'bar.org', user='georges')
4146
def test_credentials_for_user_without_password(self):
4147
conf = config.AuthenticationConfig(_file=StringIO(
4154
# Get user but no password
4155
self._got_user_passwd('jim', None,
4156
conf, 'http', 'bar.org')
4158
def test_verify_certificates(self):
4159
conf = config.AuthenticationConfig(_file=StringIO(
4166
verify_certificates=False
4173
credentials = conf.get_credentials('https', 'bar.org')
4174
self.assertEquals(False, credentials.get('verify_certificates'))
4175
credentials = conf.get_credentials('https', 'foo.net')
4176
self.assertEquals(True, credentials.get('verify_certificates'))
4179
class TestAuthenticationStorage(tests.TestCaseInTempDir):
4181
def test_set_credentials(self):
4182
conf = config.AuthenticationConfig()
4183
conf.set_credentials('name', 'host', 'user', 'scheme', 'password',
4184
99, path='/foo', verify_certificates=False, realm='realm')
4185
credentials = conf.get_credentials(host='host', scheme='scheme',
4186
port=99, path='/foo',
4188
CREDENTIALS = {'name': 'name', 'user': 'user', 'password': 'password',
4189
'verify_certificates': False, 'scheme': 'scheme',
4190
'host': 'host', 'port': 99, 'path': '/foo',
4192
self.assertEqual(CREDENTIALS, credentials)
4193
credentials_from_disk = config.AuthenticationConfig().get_credentials(
4194
host='host', scheme='scheme', port=99, path='/foo', realm='realm')
4195
self.assertEqual(CREDENTIALS, credentials_from_disk)
4197
def test_reset_credentials_different_name(self):
4198
conf = config.AuthenticationConfig()
4199
conf.set_credentials('name', 'host', 'user', 'scheme', 'password'),
4200
conf.set_credentials('name2', 'host', 'user2', 'scheme', 'password'),
4201
self.assertIs(None, conf._get_config().get('name'))
4202
credentials = conf.get_credentials(host='host', scheme='scheme')
4203
CREDENTIALS = {'name': 'name2', 'user': 'user2', 'password':
4204
'password', 'verify_certificates': True,
4205
'scheme': 'scheme', 'host': 'host', 'port': None,
4206
'path': None, 'realm': None}
4207
self.assertEqual(CREDENTIALS, credentials)
4210
class TestAuthenticationConfig(tests.TestCase):
4211
"""Test AuthenticationConfig behaviour"""
4213
def _check_default_password_prompt(self, expected_prompt_format, scheme,
4214
host=None, port=None, realm=None,
4218
user, password = 'jim', 'precious'
4219
expected_prompt = expected_prompt_format % {
4220
'scheme': scheme, 'host': host, 'port': port,
4221
'user': user, 'realm': realm}
4223
stdout = tests.StringIOWrapper()
4224
stderr = tests.StringIOWrapper()
4225
ui.ui_factory = tests.TestUIFactory(stdin=password + '\n',
4226
stdout=stdout, stderr=stderr)
4227
# We use an empty conf so that the user is always prompted
4228
conf = config.AuthenticationConfig()
4229
self.assertEquals(password,
4230
conf.get_password(scheme, host, user, port=port,
4231
realm=realm, path=path))
4232
self.assertEquals(expected_prompt, stderr.getvalue())
4233
self.assertEquals('', stdout.getvalue())
4235
def _check_default_username_prompt(self, expected_prompt_format, scheme,
4236
host=None, port=None, realm=None,
4241
expected_prompt = expected_prompt_format % {
4242
'scheme': scheme, 'host': host, 'port': port,
4244
stdout = tests.StringIOWrapper()
4245
stderr = tests.StringIOWrapper()
4246
ui.ui_factory = tests.TestUIFactory(stdin=username+ '\n',
4247
stdout=stdout, stderr=stderr)
4248
# We use an empty conf so that the user is always prompted
4249
conf = config.AuthenticationConfig()
4250
self.assertEquals(username, conf.get_user(scheme, host, port=port,
4251
realm=realm, path=path, ask=True))
4252
self.assertEquals(expected_prompt, stderr.getvalue())
4253
self.assertEquals('', stdout.getvalue())
4255
def test_username_defaults_prompts(self):
4256
# HTTP prompts can't be tested here, see test_http.py
4257
self._check_default_username_prompt(u'FTP %(host)s username: ', 'ftp')
4258
self._check_default_username_prompt(
4259
u'FTP %(host)s:%(port)d username: ', 'ftp', port=10020)
4260
self._check_default_username_prompt(
4261
u'SSH %(host)s:%(port)d username: ', 'ssh', port=12345)
4263
def test_username_default_no_prompt(self):
4264
conf = config.AuthenticationConfig()
4265
self.assertEquals(None,
4266
conf.get_user('ftp', 'example.com'))
4267
self.assertEquals("explicitdefault",
4268
conf.get_user('ftp', 'example.com', default="explicitdefault"))
4270
def test_password_default_prompts(self):
4271
# HTTP prompts can't be tested here, see test_http.py
4272
self._check_default_password_prompt(
4273
u'FTP %(user)s@%(host)s password: ', 'ftp')
4274
self._check_default_password_prompt(
4275
u'FTP %(user)s@%(host)s:%(port)d password: ', 'ftp', port=10020)
4276
self._check_default_password_prompt(
4277
u'SSH %(user)s@%(host)s:%(port)d password: ', 'ssh', port=12345)
4278
# SMTP port handling is a bit special (it's handled if embedded in the
4280
# FIXME: should we: forbid that, extend it to other schemes, leave
4281
# things as they are that's fine thank you ?
4282
self._check_default_password_prompt(
4283
u'SMTP %(user)s@%(host)s password: ', 'smtp')
4284
self._check_default_password_prompt(
4285
u'SMTP %(user)s@%(host)s password: ', 'smtp', host='bar.org:10025')
4286
self._check_default_password_prompt(
4287
u'SMTP %(user)s@%(host)s:%(port)d password: ', 'smtp', port=10025)
4289
def test_ssh_password_emits_warning(self):
4290
conf = config.AuthenticationConfig(_file=StringIO(
4298
entered_password = 'typed-by-hand'
4299
stdout = tests.StringIOWrapper()
4300
stderr = tests.StringIOWrapper()
4301
ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
4302
stdout=stdout, stderr=stderr)
4304
# Since the password defined in the authentication config is ignored,
4305
# the user is prompted
4306
self.assertEquals(entered_password,
4307
conf.get_password('ssh', 'bar.org', user='jim'))
4308
self.assertContainsRe(
4310
'password ignored in section \[ssh with password\]')
4312
def test_ssh_without_password_doesnt_emit_warning(self):
4313
conf = config.AuthenticationConfig(_file=StringIO(
4320
entered_password = 'typed-by-hand'
4321
stdout = tests.StringIOWrapper()
4322
stderr = tests.StringIOWrapper()
4323
ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
4327
# Since the password defined in the authentication config is ignored,
4328
# the user is prompted
4329
self.assertEquals(entered_password,
4330
conf.get_password('ssh', 'bar.org', user='jim'))
4331
# No warning shoud be emitted since there is no password. We are only
4333
self.assertNotContainsRe(
4335
'password ignored in section \[ssh with password\]')
4337
def test_uses_fallback_stores(self):
4338
self.overrideAttr(config, 'credential_store_registry',
4339
config.CredentialStoreRegistry())
4340
store = StubCredentialStore()
4341
store.add_credentials("http", "example.com", "joe", "secret")
4342
config.credential_store_registry.register("stub", store, fallback=True)
4343
conf = config.AuthenticationConfig(_file=StringIO())
4344
creds = conf.get_credentials("http", "example.com")
4345
self.assertEquals("joe", creds["user"])
4346
self.assertEquals("secret", creds["password"])
4349
class StubCredentialStore(config.CredentialStore):
4355
def add_credentials(self, scheme, host, user, password=None):
4356
self._username[(scheme, host)] = user
4357
self._password[(scheme, host)] = password
4359
def get_credentials(self, scheme, host, port=None, user=None,
4360
path=None, realm=None):
4361
key = (scheme, host)
4362
if not key in self._username:
4364
return { "scheme": scheme, "host": host, "port": port,
4365
"user": self._username[key], "password": self._password[key]}
4368
class CountingCredentialStore(config.CredentialStore):
4373
def get_credentials(self, scheme, host, port=None, user=None,
4374
path=None, realm=None):
4379
class TestCredentialStoreRegistry(tests.TestCase):
4381
def _get_cs_registry(self):
4382
return config.credential_store_registry
4384
def test_default_credential_store(self):
4385
r = self._get_cs_registry()
4386
default = r.get_credential_store(None)
4387
self.assertIsInstance(default, config.PlainTextCredentialStore)
4389
def test_unknown_credential_store(self):
4390
r = self._get_cs_registry()
4391
# It's hard to imagine someone creating a credential store named
4392
# 'unknown' so we use that as an never registered key.
4393
self.assertRaises(KeyError, r.get_credential_store, 'unknown')
4395
def test_fallback_none_registered(self):
4396
r = config.CredentialStoreRegistry()
4397
self.assertEquals(None,
4398
r.get_fallback_credentials("http", "example.com"))
4400
def test_register(self):
4401
r = config.CredentialStoreRegistry()
4402
r.register("stub", StubCredentialStore(), fallback=False)
4403
r.register("another", StubCredentialStore(), fallback=True)
4404
self.assertEquals(["another", "stub"], r.keys())
4406
def test_register_lazy(self):
4407
r = config.CredentialStoreRegistry()
4408
r.register_lazy("stub", "bzrlib.tests.test_config",
4409
"StubCredentialStore", fallback=False)
4410
self.assertEquals(["stub"], r.keys())
4411
self.assertIsInstance(r.get_credential_store("stub"),
4412
StubCredentialStore)
4414
def test_is_fallback(self):
4415
r = config.CredentialStoreRegistry()
4416
r.register("stub1", None, fallback=False)
4417
r.register("stub2", None, fallback=True)
4418
self.assertEquals(False, r.is_fallback("stub1"))
4419
self.assertEquals(True, r.is_fallback("stub2"))
4421
def test_no_fallback(self):
4422
r = config.CredentialStoreRegistry()
4423
store = CountingCredentialStore()
4424
r.register("count", store, fallback=False)
4425
self.assertEquals(None,
4426
r.get_fallback_credentials("http", "example.com"))
4427
self.assertEquals(0, store._calls)
4429
def test_fallback_credentials(self):
4430
r = config.CredentialStoreRegistry()
4431
store = StubCredentialStore()
4432
store.add_credentials("http", "example.com",
4433
"somebody", "geheim")
4434
r.register("stub", store, fallback=True)
4435
creds = r.get_fallback_credentials("http", "example.com")
4436
self.assertEquals("somebody", creds["user"])
4437
self.assertEquals("geheim", creds["password"])
4439
def test_fallback_first_wins(self):
4440
r = config.CredentialStoreRegistry()
4441
stub1 = StubCredentialStore()
4442
stub1.add_credentials("http", "example.com",
4443
"somebody", "stub1")
4444
r.register("stub1", stub1, fallback=True)
4445
stub2 = StubCredentialStore()
4446
stub2.add_credentials("http", "example.com",
4447
"somebody", "stub2")
4448
r.register("stub2", stub1, fallback=True)
4449
creds = r.get_fallback_credentials("http", "example.com")
4450
self.assertEquals("somebody", creds["user"])
4451
self.assertEquals("stub1", creds["password"])
4454
class TestPlainTextCredentialStore(tests.TestCase):
4456
def test_decode_password(self):
4457
r = config.credential_store_registry
4458
plain_text = r.get_credential_store()
4459
decoded = plain_text.decode_password(dict(password='secret'))
4460
self.assertEquals('secret', decoded)
4463
# FIXME: Once we have a way to declare authentication to all test servers, we
4464
# can implement generic tests.
4465
# test_user_password_in_url
4466
# test_user_in_url_password_from_config
4467
# test_user_in_url_password_prompted
4468
# test_user_in_config
4469
# test_user_getpass.getuser
4470
# test_user_prompted ?
4471
class TestAuthenticationRing(tests.TestCaseWithTransport):
4475
class TestAutoUserId(tests.TestCase):
4476
"""Test inferring an automatic user name."""
4478
def test_auto_user_id(self):
4479
"""Automatic inference of user name.
4481
This is a bit hard to test in an isolated way, because it depends on
4482
system functions that go direct to /etc or perhaps somewhere else.
4483
But it's reasonable to say that on Unix, with an /etc/mailname, we ought
4484
to be able to choose a user name with no configuration.
4486
if sys.platform == 'win32':
4487
raise tests.TestSkipped(
4488
"User name inference not implemented on win32")
4489
realname, address = config._auto_user_id()
4490
if os.path.exists('/etc/mailname'):
4491
self.assertIsNot(None, realname)
4492
self.assertIsNot(None, address)
4494
self.assertEquals((None, None), (realname, address))