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):
333
self.base = "http://example.com/branches/demo"
336
self._transport = self.control_files = \
337
FakeControlFilesAndTransport()
339
def _get_config(self):
340
return config.TransportConfig(self._transport, 'branch.conf')
342
def lock_write(self):
349
class FakeControlFilesAndTransport(object):
353
self._transport = self
355
def get(self, filename):
358
return StringIO(self.files[filename])
360
raise errors.NoSuchFile(filename)
362
def get_bytes(self, filename):
365
return self.files[filename]
367
raise errors.NoSuchFile(filename)
369
def put(self, filename, fileobj):
370
self.files[filename] = fileobj.read()
372
def put_file(self, filename, fileobj):
373
return self.put(filename, fileobj)
376
class InstrumentedConfig(config.Config):
377
"""An instrumented config that supplies stubs for template methods."""
380
super(InstrumentedConfig, self).__init__()
382
self._signatures = config.CHECK_NEVER
384
def _get_user_id(self):
385
self._calls.append('_get_user_id')
386
return "Robert Collins <robert.collins@example.org>"
388
def _get_signature_checking(self):
389
self._calls.append('_get_signature_checking')
390
return self._signatures
392
def _get_change_editor(self):
393
self._calls.append('_get_change_editor')
394
return 'vimdiff -fo @new_path @old_path'
397
bool_config = """[DEFAULT]
406
class TestConfigObj(tests.TestCase):
408
def test_get_bool(self):
409
co = config.ConfigObj(StringIO(bool_config))
410
self.assertIs(co.get_bool('DEFAULT', 'active'), True)
411
self.assertIs(co.get_bool('DEFAULT', 'inactive'), False)
412
self.assertIs(co.get_bool('UPPERCASE', 'active'), True)
413
self.assertIs(co.get_bool('UPPERCASE', 'nonactive'), False)
415
def test_hash_sign_in_value(self):
417
Before 4.5.0, ConfigObj did not quote # signs in values, so they'd be
418
treated as comments when read in again. (#86838)
420
co = config.ConfigObj()
421
co['test'] = 'foo#bar'
423
co.write(outfile=outfile)
424
lines = outfile.getvalue().splitlines()
425
self.assertEqual(lines, ['test = "foo#bar"'])
426
co2 = config.ConfigObj(lines)
427
self.assertEqual(co2['test'], 'foo#bar')
429
def test_triple_quotes(self):
430
# Bug #710410: if the value string has triple quotes
431
# then ConfigObj versions up to 4.7.2 will quote them wrong
432
# and won't able to read them back
433
triple_quotes_value = '''spam
434
""" that's my spam """
436
co = config.ConfigObj()
437
co['test'] = triple_quotes_value
438
# While writing this test another bug in ConfigObj has been found:
439
# method co.write() without arguments produces list of lines
440
# one option per line, and multiline values are not split
441
# across multiple lines,
442
# and that breaks the parsing these lines back by ConfigObj.
443
# This issue only affects test, but it's better to avoid
444
# `co.write()` construct at all.
445
# [bialix 20110222] bug report sent to ConfigObj's author
447
co.write(outfile=outfile)
448
output = outfile.getvalue()
449
# now we're trying to read it back
450
co2 = config.ConfigObj(StringIO(output))
451
self.assertEquals(triple_quotes_value, co2['test'])
454
erroneous_config = """[section] # line 1
457
whocares=notme # line 4
461
class TestConfigObjErrors(tests.TestCase):
463
def test_duplicate_section_name_error_line(self):
465
co = configobj.ConfigObj(StringIO(erroneous_config),
467
except config.configobj.DuplicateError, e:
468
self.assertEqual(3, e.line_number)
470
self.fail('Error in config file not detected')
473
class TestConfig(tests.TestCase):
475
def test_constructs(self):
478
def test_no_default_editor(self):
481
self.applyDeprecated, deprecated_in((2, 4, 0)),
482
config.Config().get_editor)
484
def test_user_email(self):
485
my_config = InstrumentedConfig()
486
self.assertEqual('robert.collins@example.org', my_config.user_email())
487
self.assertEqual(['_get_user_id'], my_config._calls)
489
def test_username(self):
490
my_config = InstrumentedConfig()
491
self.assertEqual('Robert Collins <robert.collins@example.org>',
492
my_config.username())
493
self.assertEqual(['_get_user_id'], my_config._calls)
495
def test_signatures_default(self):
496
my_config = config.Config()
498
self.applyDeprecated(deprecated_in((2, 5, 0)),
499
my_config.signature_needed))
500
self.assertEqual(config.CHECK_IF_POSSIBLE,
501
self.applyDeprecated(deprecated_in((2, 5, 0)),
502
my_config.signature_checking))
503
self.assertEqual(config.SIGN_WHEN_REQUIRED,
504
self.applyDeprecated(deprecated_in((2, 5, 0)),
505
my_config.signing_policy))
507
def test_signatures_template_method(self):
508
my_config = InstrumentedConfig()
509
self.assertEqual(config.CHECK_NEVER,
510
self.applyDeprecated(deprecated_in((2, 5, 0)),
511
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
self.applyDeprecated(deprecated_in((2, 5, 0)),
519
my_config.signature_checking))
520
self.assertEqual(['_get_signature_checking'], my_config._calls)
522
def test_gpg_signing_command_default(self):
523
my_config = config.Config()
524
self.assertEqual('gpg',
525
self.applyDeprecated(deprecated_in((2, 5, 0)),
526
my_config.gpg_signing_command))
528
def test_get_user_option_default(self):
529
my_config = config.Config()
530
self.assertEqual(None, my_config.get_user_option('no_option'))
532
def test_post_commit_default(self):
533
my_config = config.Config()
534
self.assertEqual(None, self.applyDeprecated(deprecated_in((2, 5, 0)),
535
my_config.post_commit))
538
def test_log_format_default(self):
539
my_config = config.Config()
540
self.assertEqual('long', my_config.log_format())
542
def test_acceptable_keys_default(self):
543
my_config = config.Config()
544
self.assertEqual(None, self.applyDeprecated(deprecated_in((2, 5, 0)),
545
my_config.acceptable_keys))
547
def test_validate_signatures_in_log_default(self):
548
my_config = config.Config()
549
self.assertEqual(False, my_config.validate_signatures_in_log())
551
def test_get_change_editor(self):
552
my_config = InstrumentedConfig()
553
change_editor = my_config.get_change_editor('old_tree', 'new_tree')
554
self.assertEqual(['_get_change_editor'], my_config._calls)
555
self.assertIs(diff.DiffFromTool, change_editor.__class__)
556
self.assertEqual(['vimdiff', '-fo', '@new_path', '@old_path'],
557
change_editor.command_template)
560
class TestConfigPath(tests.TestCase):
563
super(TestConfigPath, self).setUp()
564
self.overrideEnv('HOME', '/home/bogus')
565
self.overrideEnv('XDG_CACHE_DIR', '')
566
if sys.platform == 'win32':
568
'BZR_HOME', r'C:\Documents and Settings\bogus\Application Data')
570
'C:/Documents and Settings/bogus/Application Data/bazaar/2.0'
572
self.bzr_home = '/home/bogus/.bazaar'
574
def test_config_dir(self):
575
self.assertEqual(config.config_dir(), self.bzr_home)
577
def test_config_filename(self):
578
self.assertEqual(config.config_filename(),
579
self.bzr_home + '/bazaar.conf')
581
def test_locations_config_filename(self):
582
self.assertEqual(config.locations_config_filename(),
583
self.bzr_home + '/locations.conf')
585
def test_authentication_config_filename(self):
586
self.assertEqual(config.authentication_config_filename(),
587
self.bzr_home + '/authentication.conf')
589
def test_xdg_cache_dir(self):
590
self.assertEqual(config.xdg_cache_dir(),
591
'/home/bogus/.cache')
594
class TestXDGConfigDir(tests.TestCaseInTempDir):
595
# must be in temp dir because config tests for the existence of the bazaar
596
# subdirectory of $XDG_CONFIG_HOME
599
if sys.platform in ('darwin', 'win32'):
600
raise tests.TestNotApplicable(
601
'XDG config dir not used on this platform')
602
super(TestXDGConfigDir, self).setUp()
603
self.overrideEnv('HOME', self.test_home_dir)
604
# BZR_HOME overrides everything we want to test so unset it.
605
self.overrideEnv('BZR_HOME', None)
607
def test_xdg_config_dir_exists(self):
608
"""When ~/.config/bazaar exists, use it as the config dir."""
609
newdir = osutils.pathjoin(self.test_home_dir, '.config', 'bazaar')
611
self.assertEqual(config.config_dir(), newdir)
613
def test_xdg_config_home(self):
614
"""When XDG_CONFIG_HOME is set, use it."""
615
xdgconfigdir = osutils.pathjoin(self.test_home_dir, 'xdgconfig')
616
self.overrideEnv('XDG_CONFIG_HOME', xdgconfigdir)
617
newdir = osutils.pathjoin(xdgconfigdir, 'bazaar')
619
self.assertEqual(config.config_dir(), newdir)
622
class TestIniConfig(tests.TestCaseInTempDir):
624
def make_config_parser(self, s):
625
conf = config.IniBasedConfig.from_string(s)
626
return conf, conf._get_parser()
629
class TestIniConfigBuilding(TestIniConfig):
631
def test_contructs(self):
632
my_config = config.IniBasedConfig()
634
def test_from_fp(self):
635
my_config = config.IniBasedConfig.from_string(sample_config_text)
636
self.assertIsInstance(my_config._get_parser(), configobj.ConfigObj)
638
def test_cached(self):
639
my_config = config.IniBasedConfig.from_string(sample_config_text)
640
parser = my_config._get_parser()
641
self.assertTrue(my_config._get_parser() is parser)
643
def _dummy_chown(self, path, uid, gid):
644
self.path, self.uid, self.gid = path, uid, gid
646
def test_ini_config_ownership(self):
647
"""Ensure that chown is happening during _write_config_file"""
648
self.requireFeature(features.chown_feature)
649
self.overrideAttr(os, 'chown', self._dummy_chown)
650
self.path = self.uid = self.gid = None
651
conf = config.IniBasedConfig(file_name='./foo.conf')
652
conf._write_config_file()
653
self.assertEquals(self.path, './foo.conf')
654
self.assertTrue(isinstance(self.uid, int))
655
self.assertTrue(isinstance(self.gid, int))
657
def test_get_filename_parameter_is_deprecated_(self):
658
conf = self.callDeprecated([
659
'IniBasedConfig.__init__(get_filename) was deprecated in 2.3.'
660
' Use file_name instead.'],
661
config.IniBasedConfig, lambda: 'ini.conf')
662
self.assertEqual('ini.conf', conf.file_name)
664
def test_get_parser_file_parameter_is_deprecated_(self):
665
config_file = StringIO(sample_config_text.encode('utf-8'))
666
conf = config.IniBasedConfig.from_string(sample_config_text)
667
conf = self.callDeprecated([
668
'IniBasedConfig._get_parser(file=xxx) was deprecated in 2.3.'
669
' Use IniBasedConfig(_content=xxx) instead.'],
670
conf._get_parser, file=config_file)
673
class TestIniConfigSaving(tests.TestCaseInTempDir):
675
def test_cant_save_without_a_file_name(self):
676
conf = config.IniBasedConfig()
677
self.assertRaises(AssertionError, conf._write_config_file)
679
def test_saved_with_content(self):
680
content = 'foo = bar\n'
681
conf = config.IniBasedConfig.from_string(
682
content, file_name='./test.conf', save=True)
683
self.assertFileEqual(content, 'test.conf')
686
class TestIniConfigOptionExpansionDefaultValue(tests.TestCaseInTempDir):
687
"""What is the default value of expand for config options.
689
This is an opt-in beta feature used to evaluate whether or not option
690
references can appear in dangerous place raising exceptions, disapearing
691
(and as such corrupting data) or if it's safe to activate the option by
694
Note that these tests relies on config._expand_default_value being already
695
overwritten in the parent class setUp.
699
super(TestIniConfigOptionExpansionDefaultValue, self).setUp()
703
self.warnings.append(args[0] % args[1:])
704
self.overrideAttr(trace, 'warning', warning)
706
def get_config(self, expand):
707
c = config.GlobalConfig.from_string('bzr.config.expand=%s' % (expand,),
711
def assertExpandIs(self, expected):
712
actual = config._get_expand_default_value()
713
#self.config.get_user_option_as_bool('bzr.config.expand')
714
self.assertEquals(expected, actual)
716
def test_default_is_None(self):
717
self.assertEquals(None, config._expand_default_value)
719
def test_default_is_False_even_if_None(self):
720
self.config = self.get_config(None)
721
self.assertExpandIs(False)
723
def test_default_is_False_even_if_invalid(self):
724
self.config = self.get_config('<your choice>')
725
self.assertExpandIs(False)
727
# Huh ? My choice is False ? Thanks, always happy to hear that :D
728
# Wait, you've been warned !
729
self.assertLength(1, self.warnings)
731
'Value "<your choice>" is not a boolean for "bzr.config.expand"',
734
def test_default_is_True(self):
735
self.config = self.get_config(True)
736
self.assertExpandIs(True)
738
def test_default_is_False(self):
739
self.config = self.get_config(False)
740
self.assertExpandIs(False)
743
class TestIniConfigOptionExpansion(tests.TestCase):
744
"""Test option expansion from the IniConfig level.
746
What we really want here is to test the Config level, but the class being
747
abstract as far as storing values is concerned, this can't be done
750
# FIXME: This should be rewritten when all configs share a storage
751
# implementation -- vila 2011-02-18
753
def get_config(self, string=None):
756
c = config.IniBasedConfig.from_string(string)
759
def assertExpansion(self, expected, conf, string, env=None):
760
self.assertEquals(expected, conf.expand_options(string, env))
762
def test_no_expansion(self):
763
c = self.get_config('')
764
self.assertExpansion('foo', c, 'foo')
766
def test_env_adding_options(self):
767
c = self.get_config('')
768
self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
770
def test_env_overriding_options(self):
771
c = self.get_config('foo=baz')
772
self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
774
def test_simple_ref(self):
775
c = self.get_config('foo=xxx')
776
self.assertExpansion('xxx', c, '{foo}')
778
def test_unknown_ref(self):
779
c = self.get_config('')
780
self.assertRaises(errors.ExpandingUnknownOption,
781
c.expand_options, '{foo}')
783
def test_indirect_ref(self):
784
c = self.get_config('''
788
self.assertExpansion('xxx', c, '{bar}')
790
def test_embedded_ref(self):
791
c = self.get_config('''
795
self.assertExpansion('xxx', c, '{{bar}}')
797
def test_simple_loop(self):
798
c = self.get_config('foo={foo}')
799
self.assertRaises(errors.OptionExpansionLoop, c.expand_options, '{foo}')
801
def test_indirect_loop(self):
802
c = self.get_config('''
806
e = self.assertRaises(errors.OptionExpansionLoop,
807
c.expand_options, '{foo}')
808
self.assertEquals('foo->bar->baz', e.refs)
809
self.assertEquals('{foo}', e.string)
812
conf = self.get_config('''
816
list={foo},{bar},{baz}
818
self.assertEquals(['start', 'middle', 'end'],
819
conf.get_user_option('list', expand=True))
821
def test_cascading_list(self):
822
conf = self.get_config('''
828
self.assertEquals(['start', 'middle', 'end'],
829
conf.get_user_option('list', expand=True))
831
def test_pathological_hidden_list(self):
832
conf = self.get_config('''
838
hidden={start}{middle}{end}
840
# Nope, it's either a string or a list, and the list wins as soon as a
841
# ',' appears, so the string concatenation never occur.
842
self.assertEquals(['{foo', '}', '{', 'bar}'],
843
conf.get_user_option('hidden', expand=True))
846
class TestLocationConfigOptionExpansion(tests.TestCaseInTempDir):
848
def get_config(self, location, string=None):
851
# Since we don't save the config we won't strictly require to inherit
852
# from TestCaseInTempDir, but an error occurs so quickly...
853
c = config.LocationConfig.from_string(string, location)
856
def test_dont_cross_unrelated_section(self):
857
c = self.get_config('/another/branch/path','''
862
[/another/branch/path]
865
self.assertRaises(errors.ExpandingUnknownOption,
866
c.get_user_option, 'bar', expand=True)
868
def test_cross_related_sections(self):
869
c = self.get_config('/project/branch/path','''
873
[/project/branch/path]
876
self.assertEquals('quux', c.get_user_option('bar', expand=True))
879
class TestIniBaseConfigOnDisk(tests.TestCaseInTempDir):
881
def test_cannot_reload_without_name(self):
882
conf = config.IniBasedConfig.from_string(sample_config_text)
883
self.assertRaises(AssertionError, conf.reload)
885
def test_reload_see_new_value(self):
886
c1 = config.IniBasedConfig.from_string('editor=vim\n',
887
file_name='./test/conf')
888
c1._write_config_file()
889
c2 = config.IniBasedConfig.from_string('editor=emacs\n',
890
file_name='./test/conf')
891
c2._write_config_file()
892
self.assertEqual('vim', c1.get_user_option('editor'))
893
self.assertEqual('emacs', c2.get_user_option('editor'))
894
# Make sure we get the Right value
896
self.assertEqual('emacs', c1.get_user_option('editor'))
899
class TestLockableConfig(tests.TestCaseInTempDir):
901
scenarios = lockable_config_scenarios()
906
config_section = None
909
super(TestLockableConfig, self).setUp()
910
self._content = '[%s]\none=1\ntwo=2\n' % (self.config_section,)
911
self.config = self.create_config(self._content)
913
def get_existing_config(self):
914
return self.config_class(*self.config_args)
916
def create_config(self, content):
917
kwargs = dict(save=True)
918
c = self.config_class.from_string(content, *self.config_args, **kwargs)
921
def test_simple_read_access(self):
922
self.assertEquals('1', self.config.get_user_option('one'))
924
def test_simple_write_access(self):
925
self.config.set_user_option('one', 'one')
926
self.assertEquals('one', self.config.get_user_option('one'))
928
def test_listen_to_the_last_speaker(self):
930
c2 = self.get_existing_config()
931
c1.set_user_option('one', 'ONE')
932
c2.set_user_option('two', 'TWO')
933
self.assertEquals('ONE', c1.get_user_option('one'))
934
self.assertEquals('TWO', c2.get_user_option('two'))
935
# The second update respect the first one
936
self.assertEquals('ONE', c2.get_user_option('one'))
938
def test_last_speaker_wins(self):
939
# If the same config is not shared, the same variable modified twice
940
# can only see a single result.
942
c2 = self.get_existing_config()
943
c1.set_user_option('one', 'c1')
944
c2.set_user_option('one', 'c2')
945
self.assertEquals('c2', c2._get_user_option('one'))
946
# The first modification is still available until another refresh
948
self.assertEquals('c1', c1._get_user_option('one'))
949
c1.set_user_option('two', 'done')
950
self.assertEquals('c2', c1._get_user_option('one'))
952
def test_writes_are_serialized(self):
954
c2 = self.get_existing_config()
956
# We spawn a thread that will pause *during* the write
957
before_writing = threading.Event()
958
after_writing = threading.Event()
959
writing_done = threading.Event()
960
c1_orig = c1._write_config_file
961
def c1_write_config_file():
964
# The lock is held. We wait for the main thread to decide when to
967
c1._write_config_file = c1_write_config_file
969
c1.set_user_option('one', 'c1')
971
t1 = threading.Thread(target=c1_set_option)
972
# Collect the thread after the test
973
self.addCleanup(t1.join)
974
# Be ready to unblock the thread if the test goes wrong
975
self.addCleanup(after_writing.set)
977
before_writing.wait()
978
self.assertTrue(c1._lock.is_held)
979
self.assertRaises(errors.LockContention,
980
c2.set_user_option, 'one', 'c2')
981
self.assertEquals('c1', c1.get_user_option('one'))
982
# Let the lock be released
985
c2.set_user_option('one', 'c2')
986
self.assertEquals('c2', c2.get_user_option('one'))
988
def test_read_while_writing(self):
990
# We spawn a thread that will pause *during* the write
991
ready_to_write = threading.Event()
992
do_writing = threading.Event()
993
writing_done = threading.Event()
994
c1_orig = c1._write_config_file
995
def c1_write_config_file():
997
# The lock is held. We wait for the main thread to decide when to
1002
c1._write_config_file = c1_write_config_file
1003
def c1_set_option():
1004
c1.set_user_option('one', 'c1')
1005
t1 = threading.Thread(target=c1_set_option)
1006
# Collect the thread after the test
1007
self.addCleanup(t1.join)
1008
# Be ready to unblock the thread if the test goes wrong
1009
self.addCleanup(do_writing.set)
1011
# Ensure the thread is ready to write
1012
ready_to_write.wait()
1013
self.assertTrue(c1._lock.is_held)
1014
self.assertEquals('c1', c1.get_user_option('one'))
1015
# If we read during the write, we get the old value
1016
c2 = self.get_existing_config()
1017
self.assertEquals('1', c2.get_user_option('one'))
1018
# Let the writing occur and ensure it occurred
1021
# Now we get the updated value
1022
c3 = self.get_existing_config()
1023
self.assertEquals('c1', c3.get_user_option('one'))
1026
class TestGetUserOptionAs(TestIniConfig):
1028
def test_get_user_option_as_bool(self):
1029
conf, parser = self.make_config_parser("""
1032
an_invalid_bool = maybe
1033
a_list = hmm, who knows ? # This is interpreted as a list !
1035
get_bool = conf.get_user_option_as_bool
1036
self.assertEqual(True, get_bool('a_true_bool'))
1037
self.assertEqual(False, get_bool('a_false_bool'))
1040
warnings.append(args[0] % args[1:])
1041
self.overrideAttr(trace, 'warning', warning)
1042
msg = 'Value "%s" is not a boolean for "%s"'
1043
self.assertIs(None, get_bool('an_invalid_bool'))
1044
self.assertEquals(msg % ('maybe', 'an_invalid_bool'), warnings[0])
1046
self.assertIs(None, get_bool('not_defined_in_this_config'))
1047
self.assertEquals([], warnings)
1049
def test_get_user_option_as_list(self):
1050
conf, parser = self.make_config_parser("""
1055
get_list = conf.get_user_option_as_list
1056
self.assertEqual(['a', 'b', 'c'], get_list('a_list'))
1057
self.assertEqual(['1'], get_list('length_1'))
1058
self.assertEqual('x', conf.get_user_option('one_item'))
1059
# automatically cast to list
1060
self.assertEqual(['x'], get_list('one_item'))
1062
def test_get_user_option_as_int_from_SI(self):
1063
conf, parser = self.make_config_parser("""
1072
get_si = conf.get_user_option_as_int_from_SI
1073
self.assertEqual(100, get_si('plain'))
1074
self.assertEqual(5000, get_si('si_k'))
1075
self.assertEqual(5000, get_si('si_kb'))
1076
self.assertEqual(5000000, get_si('si_m'))
1077
self.assertEqual(5000000, get_si('si_mb'))
1078
self.assertEqual(5000000000, get_si('si_g'))
1079
self.assertEqual(5000000000, get_si('si_gb'))
1080
self.assertEqual(None, get_si('non-exist'))
1081
self.assertEqual(42, get_si('non-exist-with-default', 42))
1083
class TestSupressWarning(TestIniConfig):
1085
def make_warnings_config(self, s):
1086
conf, parser = self.make_config_parser(s)
1087
return conf.suppress_warning
1089
def test_suppress_warning_unknown(self):
1090
suppress_warning = self.make_warnings_config('')
1091
self.assertEqual(False, suppress_warning('unknown_warning'))
1093
def test_suppress_warning_known(self):
1094
suppress_warning = self.make_warnings_config('suppress_warnings=a,b')
1095
self.assertEqual(False, suppress_warning('c'))
1096
self.assertEqual(True, suppress_warning('a'))
1097
self.assertEqual(True, suppress_warning('b'))
1100
class TestGetConfig(tests.TestCase):
1102
def test_constructs(self):
1103
my_config = config.GlobalConfig()
1105
def test_calls_read_filenames(self):
1106
# replace the class that is constructed, to check its parameters
1107
oldparserclass = config.ConfigObj
1108
config.ConfigObj = InstrumentedConfigObj
1109
my_config = config.GlobalConfig()
1111
parser = my_config._get_parser()
1113
config.ConfigObj = oldparserclass
1114
self.assertIsInstance(parser, InstrumentedConfigObj)
1115
self.assertEqual(parser._calls, [('__init__', config.config_filename(),
1119
class TestBranchConfig(tests.TestCaseWithTransport):
1121
def test_constructs(self):
1122
branch = FakeBranch()
1123
my_config = config.BranchConfig(branch)
1124
self.assertRaises(TypeError, config.BranchConfig)
1126
def test_get_location_config(self):
1127
branch = FakeBranch()
1128
my_config = config.BranchConfig(branch)
1129
location_config = my_config._get_location_config()
1130
self.assertEqual(branch.base, location_config.location)
1131
self.assertIs(location_config, my_config._get_location_config())
1133
def test_get_config(self):
1134
"""The Branch.get_config method works properly"""
1135
b = bzrdir.BzrDir.create_standalone_workingtree('.').branch
1136
my_config = b.get_config()
1137
self.assertIs(my_config.get_user_option('wacky'), None)
1138
my_config.set_user_option('wacky', 'unlikely')
1139
self.assertEqual(my_config.get_user_option('wacky'), 'unlikely')
1141
# Ensure we get the same thing if we start again
1142
b2 = branch.Branch.open('.')
1143
my_config2 = b2.get_config()
1144
self.assertEqual(my_config2.get_user_option('wacky'), 'unlikely')
1146
def test_has_explicit_nickname(self):
1147
b = self.make_branch('.')
1148
self.assertFalse(b.get_config().has_explicit_nickname())
1150
self.assertTrue(b.get_config().has_explicit_nickname())
1152
def test_config_url(self):
1153
"""The Branch.get_config will use section that uses a local url"""
1154
branch = self.make_branch('branch')
1155
self.assertEqual('branch', branch.nick)
1157
local_url = urlutils.local_path_to_url('branch')
1158
conf = config.LocationConfig.from_string(
1159
'[%s]\nnickname = foobar' % (local_url,),
1160
local_url, save=True)
1161
self.assertEqual('foobar', branch.nick)
1163
def test_config_local_path(self):
1164
"""The Branch.get_config will use a local system path"""
1165
branch = self.make_branch('branch')
1166
self.assertEqual('branch', branch.nick)
1168
local_path = osutils.getcwd().encode('utf8')
1169
conf = config.LocationConfig.from_string(
1170
'[%s/branch]\nnickname = barry' % (local_path,),
1171
'branch', save=True)
1172
self.assertEqual('barry', branch.nick)
1174
def test_config_creates_local(self):
1175
"""Creating a new entry in config uses a local path."""
1176
branch = self.make_branch('branch', format='knit')
1177
branch.set_push_location('http://foobar')
1178
local_path = osutils.getcwd().encode('utf8')
1179
# Surprisingly ConfigObj doesn't create a trailing newline
1180
self.check_file_contents(config.locations_config_filename(),
1182
'push_location = http://foobar\n'
1183
'push_location:policy = norecurse\n'
1186
def test_autonick_urlencoded(self):
1187
b = self.make_branch('!repo')
1188
self.assertEqual('!repo', b.get_config().get_nickname())
1190
def test_warn_if_masked(self):
1193
warnings.append(args[0] % args[1:])
1194
self.overrideAttr(trace, 'warning', warning)
1196
def set_option(store, warn_masked=True):
1198
conf.set_user_option('example_option', repr(store), store=store,
1199
warn_masked=warn_masked)
1200
def assertWarning(warning):
1202
self.assertEqual(0, len(warnings))
1204
self.assertEqual(1, len(warnings))
1205
self.assertEqual(warning, warnings[0])
1206
branch = self.make_branch('.')
1207
conf = branch.get_config()
1208
set_option(config.STORE_GLOBAL)
1210
set_option(config.STORE_BRANCH)
1212
set_option(config.STORE_GLOBAL)
1213
assertWarning('Value "4" is masked by "3" from branch.conf')
1214
set_option(config.STORE_GLOBAL, warn_masked=False)
1216
set_option(config.STORE_LOCATION)
1218
set_option(config.STORE_BRANCH)
1219
assertWarning('Value "3" is masked by "0" from locations.conf')
1220
set_option(config.STORE_BRANCH, warn_masked=False)
1224
class TestGlobalConfigItems(tests.TestCaseInTempDir):
1226
def test_user_id(self):
1227
my_config = config.GlobalConfig.from_string(sample_config_text)
1228
self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
1229
my_config._get_user_id())
1231
def test_absent_user_id(self):
1232
my_config = config.GlobalConfig()
1233
self.assertEqual(None, my_config._get_user_id())
1235
def test_configured_editor(self):
1236
my_config = config.GlobalConfig.from_string(sample_config_text)
1237
editor = self.applyDeprecated(
1238
deprecated_in((2, 4, 0)), my_config.get_editor)
1239
self.assertEqual('vim', editor)
1241
def test_signatures_always(self):
1242
my_config = config.GlobalConfig.from_string(sample_always_signatures)
1243
self.assertEqual(config.CHECK_NEVER,
1244
self.applyDeprecated(deprecated_in((2, 5, 0)),
1245
my_config.signature_checking))
1246
self.assertEqual(config.SIGN_ALWAYS,
1247
self.applyDeprecated(deprecated_in((2, 5, 0)),
1248
my_config.signing_policy))
1249
self.assertEqual(True,
1250
self.applyDeprecated(deprecated_in((2, 5, 0)),
1251
my_config.signature_needed))
1253
def test_signatures_if_possible(self):
1254
my_config = config.GlobalConfig.from_string(sample_maybe_signatures)
1255
self.assertEqual(config.CHECK_NEVER,
1256
self.applyDeprecated(deprecated_in((2, 5, 0)),
1257
my_config.signature_checking))
1258
self.assertEqual(config.SIGN_WHEN_REQUIRED,
1259
self.applyDeprecated(deprecated_in((2, 5, 0)),
1260
my_config.signing_policy))
1261
self.assertEqual(False, self.applyDeprecated(deprecated_in((2, 5, 0)),
1262
my_config.signature_needed))
1264
def test_signatures_ignore(self):
1265
my_config = config.GlobalConfig.from_string(sample_ignore_signatures)
1266
self.assertEqual(config.CHECK_ALWAYS,
1267
self.applyDeprecated(deprecated_in((2, 5, 0)),
1268
my_config.signature_checking))
1269
self.assertEqual(config.SIGN_NEVER,
1270
self.applyDeprecated(deprecated_in((2, 5, 0)),
1271
my_config.signing_policy))
1272
self.assertEqual(False, self.applyDeprecated(deprecated_in((2, 5, 0)),
1273
my_config.signature_needed))
1275
def _get_sample_config(self):
1276
my_config = config.GlobalConfig.from_string(sample_config_text)
1279
def test_gpg_signing_command(self):
1280
my_config = self._get_sample_config()
1281
self.assertEqual("gnome-gpg",
1282
self.applyDeprecated(
1283
deprecated_in((2, 5, 0)), my_config.gpg_signing_command))
1284
self.assertEqual(False, self.applyDeprecated(deprecated_in((2, 5, 0)),
1285
my_config.signature_needed))
1287
def test_gpg_signing_key(self):
1288
my_config = self._get_sample_config()
1289
self.assertEqual("DD4D5088",
1290
self.applyDeprecated(deprecated_in((2, 5, 0)),
1291
my_config.gpg_signing_key))
1293
def _get_empty_config(self):
1294
my_config = config.GlobalConfig()
1297
def test_gpg_signing_command_unset(self):
1298
my_config = self._get_empty_config()
1299
self.assertEqual("gpg",
1300
self.applyDeprecated(
1301
deprecated_in((2, 5, 0)), my_config.gpg_signing_command))
1303
def test_get_user_option_default(self):
1304
my_config = self._get_empty_config()
1305
self.assertEqual(None, my_config.get_user_option('no_option'))
1307
def test_get_user_option_global(self):
1308
my_config = self._get_sample_config()
1309
self.assertEqual("something",
1310
my_config.get_user_option('user_global_option'))
1312
def test_post_commit_default(self):
1313
my_config = self._get_sample_config()
1314
self.assertEqual(None,
1315
self.applyDeprecated(deprecated_in((2, 5, 0)),
1316
my_config.post_commit))
1318
def test_configured_logformat(self):
1319
my_config = self._get_sample_config()
1320
self.assertEqual("short", my_config.log_format())
1322
def test_configured_acceptable_keys(self):
1323
my_config = self._get_sample_config()
1324
self.assertEqual("amy",
1325
self.applyDeprecated(deprecated_in((2, 5, 0)),
1326
my_config.acceptable_keys))
1328
def test_configured_validate_signatures_in_log(self):
1329
my_config = self._get_sample_config()
1330
self.assertEqual(True, my_config.validate_signatures_in_log())
1332
def test_get_alias(self):
1333
my_config = self._get_sample_config()
1334
self.assertEqual('help', my_config.get_alias('h'))
1336
def test_get_aliases(self):
1337
my_config = self._get_sample_config()
1338
aliases = my_config.get_aliases()
1339
self.assertEqual(2, len(aliases))
1340
sorted_keys = sorted(aliases)
1341
self.assertEqual('help', aliases[sorted_keys[0]])
1342
self.assertEqual(sample_long_alias, aliases[sorted_keys[1]])
1344
def test_get_no_alias(self):
1345
my_config = self._get_sample_config()
1346
self.assertEqual(None, my_config.get_alias('foo'))
1348
def test_get_long_alias(self):
1349
my_config = self._get_sample_config()
1350
self.assertEqual(sample_long_alias, my_config.get_alias('ll'))
1352
def test_get_change_editor(self):
1353
my_config = self._get_sample_config()
1354
change_editor = my_config.get_change_editor('old', 'new')
1355
self.assertIs(diff.DiffFromTool, change_editor.__class__)
1356
self.assertEqual('vimdiff -of @new_path @old_path',
1357
' '.join(change_editor.command_template))
1359
def test_get_no_change_editor(self):
1360
my_config = self._get_empty_config()
1361
change_editor = my_config.get_change_editor('old', 'new')
1362
self.assertIs(None, change_editor)
1364
def test_get_merge_tools(self):
1365
conf = self._get_sample_config()
1366
tools = conf.get_merge_tools()
1367
self.log(repr(tools))
1369
{u'funkytool' : u'funkytool "arg with spaces" {this_temp}',
1370
u'sometool' : u'sometool {base} {this} {other} -o {result}',
1371
u'newtool' : u'"newtool with spaces" {this_temp}'},
1374
def test_get_merge_tools_empty(self):
1375
conf = self._get_empty_config()
1376
tools = conf.get_merge_tools()
1377
self.assertEqual({}, tools)
1379
def test_find_merge_tool(self):
1380
conf = self._get_sample_config()
1381
cmdline = conf.find_merge_tool('sometool')
1382
self.assertEqual('sometool {base} {this} {other} -o {result}', cmdline)
1384
def test_find_merge_tool_not_found(self):
1385
conf = self._get_sample_config()
1386
cmdline = conf.find_merge_tool('DOES NOT EXIST')
1387
self.assertIs(cmdline, None)
1389
def test_find_merge_tool_known(self):
1390
conf = self._get_empty_config()
1391
cmdline = conf.find_merge_tool('kdiff3')
1392
self.assertEquals('kdiff3 {base} {this} {other} -o {result}', cmdline)
1394
def test_find_merge_tool_override_known(self):
1395
conf = self._get_empty_config()
1396
conf.set_user_option('bzr.mergetool.kdiff3', 'kdiff3 blah')
1397
cmdline = conf.find_merge_tool('kdiff3')
1398
self.assertEqual('kdiff3 blah', cmdline)
1401
class TestGlobalConfigSavingOptions(tests.TestCaseInTempDir):
1403
def test_empty(self):
1404
my_config = config.GlobalConfig()
1405
self.assertEqual(0, len(my_config.get_aliases()))
1407
def test_set_alias(self):
1408
my_config = config.GlobalConfig()
1409
alias_value = 'commit --strict'
1410
my_config.set_alias('commit', alias_value)
1411
new_config = config.GlobalConfig()
1412
self.assertEqual(alias_value, new_config.get_alias('commit'))
1414
def test_remove_alias(self):
1415
my_config = config.GlobalConfig()
1416
my_config.set_alias('commit', 'commit --strict')
1417
# Now remove the alias again.
1418
my_config.unset_alias('commit')
1419
new_config = config.GlobalConfig()
1420
self.assertIs(None, new_config.get_alias('commit'))
1423
class TestLocationConfig(tests.TestCaseInTempDir, TestOptionsMixin):
1425
def test_constructs(self):
1426
my_config = config.LocationConfig('http://example.com')
1427
self.assertRaises(TypeError, config.LocationConfig)
1429
def test_branch_calls_read_filenames(self):
1430
# This is testing the correct file names are provided.
1431
# TODO: consolidate with the test for GlobalConfigs filename checks.
1433
# replace the class that is constructed, to check its parameters
1434
oldparserclass = config.ConfigObj
1435
config.ConfigObj = InstrumentedConfigObj
1437
my_config = config.LocationConfig('http://www.example.com')
1438
parser = my_config._get_parser()
1440
config.ConfigObj = oldparserclass
1441
self.assertIsInstance(parser, InstrumentedConfigObj)
1442
self.assertEqual(parser._calls,
1443
[('__init__', config.locations_config_filename(),
1446
def test_get_global_config(self):
1447
my_config = config.BranchConfig(FakeBranch('http://example.com'))
1448
global_config = my_config._get_global_config()
1449
self.assertIsInstance(global_config, config.GlobalConfig)
1450
self.assertIs(global_config, my_config._get_global_config())
1452
def assertLocationMatching(self, expected):
1453
self.assertEqual(expected,
1454
list(self.my_location_config._get_matching_sections()))
1456
def test__get_matching_sections_no_match(self):
1457
self.get_branch_config('/')
1458
self.assertLocationMatching([])
1460
def test__get_matching_sections_exact(self):
1461
self.get_branch_config('http://www.example.com')
1462
self.assertLocationMatching([('http://www.example.com', '')])
1464
def test__get_matching_sections_suffix_does_not(self):
1465
self.get_branch_config('http://www.example.com-com')
1466
self.assertLocationMatching([])
1468
def test__get_matching_sections_subdir_recursive(self):
1469
self.get_branch_config('http://www.example.com/com')
1470
self.assertLocationMatching([('http://www.example.com', 'com')])
1472
def test__get_matching_sections_ignoreparent(self):
1473
self.get_branch_config('http://www.example.com/ignoreparent')
1474
self.assertLocationMatching([('http://www.example.com/ignoreparent',
1477
def test__get_matching_sections_ignoreparent_subdir(self):
1478
self.get_branch_config(
1479
'http://www.example.com/ignoreparent/childbranch')
1480
self.assertLocationMatching([('http://www.example.com/ignoreparent',
1483
def test__get_matching_sections_subdir_trailing_slash(self):
1484
self.get_branch_config('/b')
1485
self.assertLocationMatching([('/b/', '')])
1487
def test__get_matching_sections_subdir_child(self):
1488
self.get_branch_config('/a/foo')
1489
self.assertLocationMatching([('/a/*', ''), ('/a/', 'foo')])
1491
def test__get_matching_sections_subdir_child_child(self):
1492
self.get_branch_config('/a/foo/bar')
1493
self.assertLocationMatching([('/a/*', 'bar'), ('/a/', 'foo/bar')])
1495
def test__get_matching_sections_trailing_slash_with_children(self):
1496
self.get_branch_config('/a/')
1497
self.assertLocationMatching([('/a/', '')])
1499
def test__get_matching_sections_explicit_over_glob(self):
1500
# XXX: 2006-09-08 jamesh
1501
# This test only passes because ord('c') > ord('*'). If there
1502
# was a config section for '/a/?', it would get precedence
1504
self.get_branch_config('/a/c')
1505
self.assertLocationMatching([('/a/c', ''), ('/a/*', ''), ('/a/', 'c')])
1507
def test__get_option_policy_normal(self):
1508
self.get_branch_config('http://www.example.com')
1510
self.my_location_config._get_config_policy(
1511
'http://www.example.com', 'normal_option'),
1514
def test__get_option_policy_norecurse(self):
1515
self.get_branch_config('http://www.example.com')
1517
self.my_location_config._get_option_policy(
1518
'http://www.example.com', 'norecurse_option'),
1519
config.POLICY_NORECURSE)
1520
# Test old recurse=False setting:
1522
self.my_location_config._get_option_policy(
1523
'http://www.example.com/norecurse', 'normal_option'),
1524
config.POLICY_NORECURSE)
1526
def test__get_option_policy_normal(self):
1527
self.get_branch_config('http://www.example.com')
1529
self.my_location_config._get_option_policy(
1530
'http://www.example.com', 'appendpath_option'),
1531
config.POLICY_APPENDPATH)
1533
def test__get_options_with_policy(self):
1534
self.get_branch_config('/dir/subdir',
1535
location_config="""\
1537
other_url = /other-dir
1538
other_url:policy = appendpath
1540
other_url = /other-subdir
1543
[(u'other_url', u'/other-subdir', u'/dir/subdir', 'locations'),
1544
(u'other_url', u'/other-dir', u'/dir', 'locations'),
1545
(u'other_url:policy', u'appendpath', u'/dir', 'locations')],
1546
self.my_location_config)
1548
def test_location_without_username(self):
1549
self.get_branch_config('http://www.example.com/ignoreparent')
1550
self.assertEqual(u'Erik B\u00e5gfors <erik@bagfors.nu>',
1551
self.my_config.username())
1553
def test_location_not_listed(self):
1554
"""Test that the global username is used when no location matches"""
1555
self.get_branch_config('/home/robertc/sources')
1556
self.assertEqual(u'Erik B\u00e5gfors <erik@bagfors.nu>',
1557
self.my_config.username())
1559
def test_overriding_location(self):
1560
self.get_branch_config('http://www.example.com/foo')
1561
self.assertEqual('Robert Collins <robertc@example.org>',
1562
self.my_config.username())
1564
def test_signatures_not_set(self):
1565
self.get_branch_config('http://www.example.com',
1566
global_config=sample_ignore_signatures)
1567
self.assertEqual(config.CHECK_ALWAYS,
1568
self.applyDeprecated(deprecated_in((2, 5, 0)),
1569
self.my_config.signature_checking))
1570
self.assertEqual(config.SIGN_NEVER,
1571
self.applyDeprecated(deprecated_in((2, 5, 0)),
1572
self.my_config.signing_policy))
1574
def test_signatures_never(self):
1575
self.get_branch_config('/a/c')
1576
self.assertEqual(config.CHECK_NEVER,
1577
self.applyDeprecated(deprecated_in((2, 5, 0)),
1578
self.my_config.signature_checking))
1580
def test_signatures_when_available(self):
1581
self.get_branch_config('/a/', global_config=sample_ignore_signatures)
1582
self.assertEqual(config.CHECK_IF_POSSIBLE,
1583
self.applyDeprecated(deprecated_in((2, 5, 0)),
1584
self.my_config.signature_checking))
1586
def test_signatures_always(self):
1587
self.get_branch_config('/b')
1588
self.assertEqual(config.CHECK_ALWAYS,
1589
self.applyDeprecated(deprecated_in((2, 5, 0)),
1590
self.my_config.signature_checking))
1592
def test_gpg_signing_command(self):
1593
self.get_branch_config('/b')
1594
self.assertEqual("gnome-gpg",
1595
self.applyDeprecated(deprecated_in((2, 5, 0)),
1596
self.my_config.gpg_signing_command))
1598
def test_gpg_signing_command_missing(self):
1599
self.get_branch_config('/a')
1600
self.assertEqual("false",
1601
self.applyDeprecated(deprecated_in((2, 5, 0)),
1602
self.my_config.gpg_signing_command))
1604
def test_gpg_signing_key(self):
1605
self.get_branch_config('/b')
1606
self.assertEqual("DD4D5088", self.applyDeprecated(deprecated_in((2, 5, 0)),
1607
self.my_config.gpg_signing_key))
1609
def test_gpg_signing_key_default(self):
1610
self.get_branch_config('/a')
1611
self.assertEqual("erik@bagfors.nu",
1612
self.applyDeprecated(deprecated_in((2, 5, 0)),
1613
self.my_config.gpg_signing_key))
1615
def test_get_user_option_global(self):
1616
self.get_branch_config('/a')
1617
self.assertEqual('something',
1618
self.my_config.get_user_option('user_global_option'))
1620
def test_get_user_option_local(self):
1621
self.get_branch_config('/a')
1622
self.assertEqual('local',
1623
self.my_config.get_user_option('user_local_option'))
1625
def test_get_user_option_appendpath(self):
1626
# returned as is for the base path:
1627
self.get_branch_config('http://www.example.com')
1628
self.assertEqual('append',
1629
self.my_config.get_user_option('appendpath_option'))
1630
# Extra path components get appended:
1631
self.get_branch_config('http://www.example.com/a/b/c')
1632
self.assertEqual('append/a/b/c',
1633
self.my_config.get_user_option('appendpath_option'))
1634
# Overriden for http://www.example.com/dir, where it is a
1636
self.get_branch_config('http://www.example.com/dir/a/b/c')
1637
self.assertEqual('normal',
1638
self.my_config.get_user_option('appendpath_option'))
1640
def test_get_user_option_norecurse(self):
1641
self.get_branch_config('http://www.example.com')
1642
self.assertEqual('norecurse',
1643
self.my_config.get_user_option('norecurse_option'))
1644
self.get_branch_config('http://www.example.com/dir')
1645
self.assertEqual(None,
1646
self.my_config.get_user_option('norecurse_option'))
1647
# http://www.example.com/norecurse is a recurse=False section
1648
# that redefines normal_option. Subdirectories do not pick up
1649
# this redefinition.
1650
self.get_branch_config('http://www.example.com/norecurse')
1651
self.assertEqual('norecurse',
1652
self.my_config.get_user_option('normal_option'))
1653
self.get_branch_config('http://www.example.com/norecurse/subdir')
1654
self.assertEqual('normal',
1655
self.my_config.get_user_option('normal_option'))
1657
def test_set_user_option_norecurse(self):
1658
self.get_branch_config('http://www.example.com')
1659
self.my_config.set_user_option('foo', 'bar',
1660
store=config.STORE_LOCATION_NORECURSE)
1662
self.my_location_config._get_option_policy(
1663
'http://www.example.com', 'foo'),
1664
config.POLICY_NORECURSE)
1666
def test_set_user_option_appendpath(self):
1667
self.get_branch_config('http://www.example.com')
1668
self.my_config.set_user_option('foo', 'bar',
1669
store=config.STORE_LOCATION_APPENDPATH)
1671
self.my_location_config._get_option_policy(
1672
'http://www.example.com', 'foo'),
1673
config.POLICY_APPENDPATH)
1675
def test_set_user_option_change_policy(self):
1676
self.get_branch_config('http://www.example.com')
1677
self.my_config.set_user_option('norecurse_option', 'normal',
1678
store=config.STORE_LOCATION)
1680
self.my_location_config._get_option_policy(
1681
'http://www.example.com', 'norecurse_option'),
1684
def test_set_user_option_recurse_false_section(self):
1685
# The following section has recurse=False set. The test is to
1686
# make sure that a normal option can be added to the section,
1687
# converting recurse=False to the norecurse policy.
1688
self.get_branch_config('http://www.example.com/norecurse')
1689
self.callDeprecated(['The recurse option is deprecated as of 0.14. '
1690
'The section "http://www.example.com/norecurse" '
1691
'has been converted to use policies.'],
1692
self.my_config.set_user_option,
1693
'foo', 'bar', store=config.STORE_LOCATION)
1695
self.my_location_config._get_option_policy(
1696
'http://www.example.com/norecurse', 'foo'),
1698
# The previously existing option is still norecurse:
1700
self.my_location_config._get_option_policy(
1701
'http://www.example.com/norecurse', 'normal_option'),
1702
config.POLICY_NORECURSE)
1704
def test_post_commit_default(self):
1705
self.get_branch_config('/a/c')
1706
self.assertEqual('bzrlib.tests.test_config.post_commit',
1707
self.applyDeprecated(deprecated_in((2, 5, 0)),
1708
self.my_config.post_commit))
1710
def get_branch_config(self, location, global_config=None,
1711
location_config=None):
1712
my_branch = FakeBranch(location)
1713
if global_config is None:
1714
global_config = sample_config_text
1715
if location_config is None:
1716
location_config = sample_branches_text
1718
my_global_config = config.GlobalConfig.from_string(global_config,
1720
my_location_config = config.LocationConfig.from_string(
1721
location_config, my_branch.base, save=True)
1722
my_config = config.BranchConfig(my_branch)
1723
self.my_config = my_config
1724
self.my_location_config = my_config._get_location_config()
1726
def test_set_user_setting_sets_and_saves(self):
1727
self.get_branch_config('/a/c')
1728
record = InstrumentedConfigObj("foo")
1729
self.my_location_config._parser = record
1731
self.callDeprecated(['The recurse option is deprecated as of '
1732
'0.14. The section "/a/c" has been '
1733
'converted to use policies.'],
1734
self.my_config.set_user_option,
1735
'foo', 'bar', store=config.STORE_LOCATION)
1736
self.assertEqual([('reload',),
1737
('__contains__', '/a/c'),
1738
('__contains__', '/a/c/'),
1739
('__setitem__', '/a/c', {}),
1740
('__getitem__', '/a/c'),
1741
('__setitem__', 'foo', 'bar'),
1742
('__getitem__', '/a/c'),
1743
('as_bool', 'recurse'),
1744
('__getitem__', '/a/c'),
1745
('__delitem__', 'recurse'),
1746
('__getitem__', '/a/c'),
1748
('__getitem__', '/a/c'),
1749
('__contains__', 'foo:policy'),
1753
def test_set_user_setting_sets_and_saves2(self):
1754
self.get_branch_config('/a/c')
1755
self.assertIs(self.my_config.get_user_option('foo'), None)
1756
self.my_config.set_user_option('foo', 'bar')
1758
self.my_config.branch.control_files.files['branch.conf'].strip(),
1760
self.assertEqual(self.my_config.get_user_option('foo'), 'bar')
1761
self.my_config.set_user_option('foo', 'baz',
1762
store=config.STORE_LOCATION)
1763
self.assertEqual(self.my_config.get_user_option('foo'), 'baz')
1764
self.my_config.set_user_option('foo', 'qux')
1765
self.assertEqual(self.my_config.get_user_option('foo'), 'baz')
1767
def test_get_bzr_remote_path(self):
1768
my_config = config.LocationConfig('/a/c')
1769
self.assertEqual('bzr', my_config.get_bzr_remote_path())
1770
my_config.set_user_option('bzr_remote_path', '/path-bzr')
1771
self.assertEqual('/path-bzr', my_config.get_bzr_remote_path())
1772
self.overrideEnv('BZR_REMOTE_PATH', '/environ-bzr')
1773
self.assertEqual('/environ-bzr', my_config.get_bzr_remote_path())
1776
precedence_global = 'option = global'
1777
precedence_branch = 'option = branch'
1778
precedence_location = """
1782
[http://example.com/specific]
1786
class TestBranchConfigItems(tests.TestCaseInTempDir):
1788
def get_branch_config(self, global_config=None, location=None,
1789
location_config=None, branch_data_config=None):
1790
my_branch = FakeBranch(location)
1791
if global_config is not None:
1792
my_global_config = config.GlobalConfig.from_string(global_config,
1794
if location_config is not None:
1795
my_location_config = config.LocationConfig.from_string(
1796
location_config, my_branch.base, save=True)
1797
my_config = config.BranchConfig(my_branch)
1798
if branch_data_config is not None:
1799
my_config.branch.control_files.files['branch.conf'] = \
1803
def test_user_id(self):
1804
branch = FakeBranch()
1805
my_config = config.BranchConfig(branch)
1806
self.assertIsNot(None, my_config.username())
1807
my_config.branch.control_files.files['email'] = "John"
1808
my_config.set_user_option('email',
1809
"Robert Collins <robertc@example.org>")
1810
self.assertEqual("Robert Collins <robertc@example.org>",
1811
my_config.username())
1813
def test_BZR_EMAIL_OVERRIDES(self):
1814
self.overrideEnv('BZR_EMAIL', "Robert Collins <robertc@example.org>")
1815
branch = FakeBranch()
1816
my_config = config.BranchConfig(branch)
1817
self.assertEqual("Robert Collins <robertc@example.org>",
1818
my_config.username())
1820
def test_signatures_forced(self):
1821
my_config = self.get_branch_config(
1822
global_config=sample_always_signatures)
1823
self.assertEqual(config.CHECK_NEVER,
1824
self.applyDeprecated(deprecated_in((2, 5, 0)),
1825
my_config.signature_checking))
1826
self.assertEqual(config.SIGN_ALWAYS,
1827
self.applyDeprecated(deprecated_in((2, 5, 0)),
1828
my_config.signing_policy))
1829
self.assertTrue(self.applyDeprecated(deprecated_in((2, 5, 0)),
1830
my_config.signature_needed))
1832
def test_signatures_forced_branch(self):
1833
my_config = self.get_branch_config(
1834
global_config=sample_ignore_signatures,
1835
branch_data_config=sample_always_signatures)
1836
self.assertEqual(config.CHECK_NEVER,
1837
self.applyDeprecated(deprecated_in((2, 5, 0)),
1838
my_config.signature_checking))
1839
self.assertEqual(config.SIGN_ALWAYS,
1840
self.applyDeprecated(deprecated_in((2, 5, 0)),
1841
my_config.signing_policy))
1842
self.assertTrue(self.applyDeprecated(deprecated_in((2, 5, 0)),
1843
my_config.signature_needed))
1845
def test_gpg_signing_command(self):
1846
my_config = self.get_branch_config(
1847
global_config=sample_config_text,
1848
# branch data cannot set gpg_signing_command
1849
branch_data_config="gpg_signing_command=pgp")
1850
self.assertEqual('gnome-gpg',
1851
self.applyDeprecated(deprecated_in((2, 5, 0)),
1852
my_config.gpg_signing_command))
1854
def test_get_user_option_global(self):
1855
my_config = self.get_branch_config(global_config=sample_config_text)
1856
self.assertEqual('something',
1857
my_config.get_user_option('user_global_option'))
1859
def test_post_commit_default(self):
1860
my_config = self.get_branch_config(global_config=sample_config_text,
1862
location_config=sample_branches_text)
1863
self.assertEqual(my_config.branch.base, '/a/c')
1864
self.assertEqual('bzrlib.tests.test_config.post_commit',
1865
self.applyDeprecated(deprecated_in((2, 5, 0)),
1866
my_config.post_commit))
1867
my_config.set_user_option('post_commit', 'rmtree_root')
1868
# post-commit is ignored when present in branch data
1869
self.assertEqual('bzrlib.tests.test_config.post_commit',
1870
self.applyDeprecated(deprecated_in((2, 5, 0)),
1871
my_config.post_commit))
1872
my_config.set_user_option('post_commit', 'rmtree_root',
1873
store=config.STORE_LOCATION)
1874
self.assertEqual('rmtree_root',
1875
self.applyDeprecated(deprecated_in((2, 5, 0)),
1876
my_config.post_commit))
1878
def test_config_precedence(self):
1879
# FIXME: eager test, luckily no persitent config file makes it fail
1881
my_config = self.get_branch_config(global_config=precedence_global)
1882
self.assertEqual(my_config.get_user_option('option'), 'global')
1883
my_config = self.get_branch_config(global_config=precedence_global,
1884
branch_data_config=precedence_branch)
1885
self.assertEqual(my_config.get_user_option('option'), 'branch')
1886
my_config = self.get_branch_config(
1887
global_config=precedence_global,
1888
branch_data_config=precedence_branch,
1889
location_config=precedence_location)
1890
self.assertEqual(my_config.get_user_option('option'), 'recurse')
1891
my_config = self.get_branch_config(
1892
global_config=precedence_global,
1893
branch_data_config=precedence_branch,
1894
location_config=precedence_location,
1895
location='http://example.com/specific')
1896
self.assertEqual(my_config.get_user_option('option'), 'exact')
1898
def test_get_mail_client(self):
1899
config = self.get_branch_config()
1900
client = config.get_mail_client()
1901
self.assertIsInstance(client, mail_client.DefaultMail)
1904
config.set_user_option('mail_client', 'evolution')
1905
client = config.get_mail_client()
1906
self.assertIsInstance(client, mail_client.Evolution)
1908
config.set_user_option('mail_client', 'kmail')
1909
client = config.get_mail_client()
1910
self.assertIsInstance(client, mail_client.KMail)
1912
config.set_user_option('mail_client', 'mutt')
1913
client = config.get_mail_client()
1914
self.assertIsInstance(client, mail_client.Mutt)
1916
config.set_user_option('mail_client', 'thunderbird')
1917
client = config.get_mail_client()
1918
self.assertIsInstance(client, mail_client.Thunderbird)
1921
config.set_user_option('mail_client', 'default')
1922
client = config.get_mail_client()
1923
self.assertIsInstance(client, mail_client.DefaultMail)
1925
config.set_user_option('mail_client', 'editor')
1926
client = config.get_mail_client()
1927
self.assertIsInstance(client, mail_client.Editor)
1929
config.set_user_option('mail_client', 'mapi')
1930
client = config.get_mail_client()
1931
self.assertIsInstance(client, mail_client.MAPIClient)
1933
config.set_user_option('mail_client', 'xdg-email')
1934
client = config.get_mail_client()
1935
self.assertIsInstance(client, mail_client.XDGEmail)
1937
config.set_user_option('mail_client', 'firebird')
1938
self.assertRaises(errors.UnknownMailClient, config.get_mail_client)
1941
class TestMailAddressExtraction(tests.TestCase):
1943
def test_extract_email_address(self):
1944
self.assertEqual('jane@test.com',
1945
config.extract_email_address('Jane <jane@test.com>'))
1946
self.assertRaises(errors.NoEmailInUsername,
1947
config.extract_email_address, 'Jane Tester')
1949
def test_parse_username(self):
1950
self.assertEqual(('', 'jdoe@example.com'),
1951
config.parse_username('jdoe@example.com'))
1952
self.assertEqual(('', 'jdoe@example.com'),
1953
config.parse_username('<jdoe@example.com>'))
1954
self.assertEqual(('John Doe', 'jdoe@example.com'),
1955
config.parse_username('John Doe <jdoe@example.com>'))
1956
self.assertEqual(('John Doe', ''),
1957
config.parse_username('John Doe'))
1958
self.assertEqual(('John Doe', 'jdoe@example.com'),
1959
config.parse_username('John Doe jdoe@example.com'))
1961
class TestTreeConfig(tests.TestCaseWithTransport):
1963
def test_get_value(self):
1964
"""Test that retreiving a value from a section is possible"""
1965
branch = self.make_branch('.')
1966
tree_config = config.TreeConfig(branch)
1967
tree_config.set_option('value', 'key', 'SECTION')
1968
tree_config.set_option('value2', 'key2')
1969
tree_config.set_option('value3-top', 'key3')
1970
tree_config.set_option('value3-section', 'key3', 'SECTION')
1971
value = tree_config.get_option('key', 'SECTION')
1972
self.assertEqual(value, 'value')
1973
value = tree_config.get_option('key2')
1974
self.assertEqual(value, 'value2')
1975
self.assertEqual(tree_config.get_option('non-existant'), None)
1976
value = tree_config.get_option('non-existant', 'SECTION')
1977
self.assertEqual(value, None)
1978
value = tree_config.get_option('non-existant', default='default')
1979
self.assertEqual(value, 'default')
1980
self.assertEqual(tree_config.get_option('key2', 'NOSECTION'), None)
1981
value = tree_config.get_option('key2', 'NOSECTION', default='default')
1982
self.assertEqual(value, 'default')
1983
value = tree_config.get_option('key3')
1984
self.assertEqual(value, 'value3-top')
1985
value = tree_config.get_option('key3', 'SECTION')
1986
self.assertEqual(value, 'value3-section')
1989
class TestTransportConfig(tests.TestCaseWithTransport):
1991
def test_load_utf8(self):
1992
"""Ensure we can load an utf8-encoded file."""
1993
t = self.get_transport()
1994
unicode_user = u'b\N{Euro Sign}ar'
1995
unicode_content = u'user=%s' % (unicode_user,)
1996
utf8_content = unicode_content.encode('utf8')
1997
# Store the raw content in the config file
1998
t.put_bytes('foo.conf', utf8_content)
1999
conf = config.TransportConfig(t, 'foo.conf')
2000
self.assertEquals(unicode_user, conf.get_option('user'))
2002
def test_load_non_ascii(self):
2003
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2004
t = self.get_transport()
2005
t.put_bytes('foo.conf', 'user=foo\n#\xff\n')
2006
conf = config.TransportConfig(t, 'foo.conf')
2007
self.assertRaises(errors.ConfigContentError, conf._get_configobj)
2009
def test_load_erroneous_content(self):
2010
"""Ensure we display a proper error on content that can't be parsed."""
2011
t = self.get_transport()
2012
t.put_bytes('foo.conf', '[open_section\n')
2013
conf = config.TransportConfig(t, 'foo.conf')
2014
self.assertRaises(errors.ParseConfigError, conf._get_configobj)
2016
def test_load_permission_denied(self):
2017
"""Ensure we get an empty config file if the file is inaccessible."""
2020
warnings.append(args[0] % args[1:])
2021
self.overrideAttr(trace, 'warning', warning)
2023
class DenyingTransport(object):
2025
def __init__(self, base):
2028
def get_bytes(self, relpath):
2029
raise errors.PermissionDenied(relpath, "")
2031
cfg = config.TransportConfig(
2032
DenyingTransport("nonexisting://"), 'control.conf')
2033
self.assertIs(None, cfg.get_option('non-existant', 'SECTION'))
2036
[u'Permission denied while trying to open configuration file '
2037
u'nonexisting:///control.conf.'])
2039
def test_get_value(self):
2040
"""Test that retreiving a value from a section is possible"""
2041
bzrdir_config = config.TransportConfig(self.get_transport('.'),
2043
bzrdir_config.set_option('value', 'key', 'SECTION')
2044
bzrdir_config.set_option('value2', 'key2')
2045
bzrdir_config.set_option('value3-top', 'key3')
2046
bzrdir_config.set_option('value3-section', 'key3', 'SECTION')
2047
value = bzrdir_config.get_option('key', 'SECTION')
2048
self.assertEqual(value, 'value')
2049
value = bzrdir_config.get_option('key2')
2050
self.assertEqual(value, 'value2')
2051
self.assertEqual(bzrdir_config.get_option('non-existant'), None)
2052
value = bzrdir_config.get_option('non-existant', 'SECTION')
2053
self.assertEqual(value, None)
2054
value = bzrdir_config.get_option('non-existant', default='default')
2055
self.assertEqual(value, 'default')
2056
self.assertEqual(bzrdir_config.get_option('key2', 'NOSECTION'), None)
2057
value = bzrdir_config.get_option('key2', 'NOSECTION',
2059
self.assertEqual(value, 'default')
2060
value = bzrdir_config.get_option('key3')
2061
self.assertEqual(value, 'value3-top')
2062
value = bzrdir_config.get_option('key3', 'SECTION')
2063
self.assertEqual(value, 'value3-section')
2065
def test_set_unset_default_stack_on(self):
2066
my_dir = self.make_bzrdir('.')
2067
bzrdir_config = config.BzrDirConfig(my_dir)
2068
self.assertIs(None, bzrdir_config.get_default_stack_on())
2069
bzrdir_config.set_default_stack_on('Foo')
2070
self.assertEqual('Foo', bzrdir_config._config.get_option(
2071
'default_stack_on'))
2072
self.assertEqual('Foo', bzrdir_config.get_default_stack_on())
2073
bzrdir_config.set_default_stack_on(None)
2074
self.assertIs(None, bzrdir_config.get_default_stack_on())
2077
class TestOldConfigHooks(tests.TestCaseWithTransport):
2080
super(TestOldConfigHooks, self).setUp()
2081
create_configs_with_file_option(self)
2083
def assertGetHook(self, conf, name, value):
2087
config.OldConfigHooks.install_named_hook('get', hook, None)
2089
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2090
self.assertLength(0, calls)
2091
actual_value = conf.get_user_option(name)
2092
self.assertEquals(value, actual_value)
2093
self.assertLength(1, calls)
2094
self.assertEquals((conf, name, value), calls[0])
2096
def test_get_hook_bazaar(self):
2097
self.assertGetHook(self.bazaar_config, 'file', 'bazaar')
2099
def test_get_hook_locations(self):
2100
self.assertGetHook(self.locations_config, 'file', 'locations')
2102
def test_get_hook_branch(self):
2103
# Since locations masks branch, we define a different option
2104
self.branch_config.set_user_option('file2', 'branch')
2105
self.assertGetHook(self.branch_config, 'file2', 'branch')
2107
def assertSetHook(self, conf, name, value):
2111
config.OldConfigHooks.install_named_hook('set', hook, None)
2113
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2114
self.assertLength(0, calls)
2115
conf.set_user_option(name, value)
2116
self.assertLength(1, calls)
2117
# We can't assert the conf object below as different configs use
2118
# different means to implement set_user_option and we care only about
2120
self.assertEquals((name, value), calls[0][1:])
2122
def test_set_hook_bazaar(self):
2123
self.assertSetHook(self.bazaar_config, 'foo', 'bazaar')
2125
def test_set_hook_locations(self):
2126
self.assertSetHook(self.locations_config, 'foo', 'locations')
2128
def test_set_hook_branch(self):
2129
self.assertSetHook(self.branch_config, 'foo', 'branch')
2131
def assertRemoveHook(self, conf, name, section_name=None):
2135
config.OldConfigHooks.install_named_hook('remove', hook, None)
2137
config.OldConfigHooks.uninstall_named_hook, 'remove', None)
2138
self.assertLength(0, calls)
2139
conf.remove_user_option(name, section_name)
2140
self.assertLength(1, calls)
2141
# We can't assert the conf object below as different configs use
2142
# different means to implement remove_user_option and we care only about
2144
self.assertEquals((name,), calls[0][1:])
2146
def test_remove_hook_bazaar(self):
2147
self.assertRemoveHook(self.bazaar_config, 'file')
2149
def test_remove_hook_locations(self):
2150
self.assertRemoveHook(self.locations_config, 'file',
2151
self.locations_config.location)
2153
def test_remove_hook_branch(self):
2154
self.assertRemoveHook(self.branch_config, 'file')
2156
def assertLoadHook(self, name, conf_class, *conf_args):
2160
config.OldConfigHooks.install_named_hook('load', hook, None)
2162
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2163
self.assertLength(0, calls)
2165
conf = conf_class(*conf_args)
2166
# Access an option to trigger a load
2167
conf.get_user_option(name)
2168
self.assertLength(1, calls)
2169
# Since we can't assert about conf, we just use the number of calls ;-/
2171
def test_load_hook_bazaar(self):
2172
self.assertLoadHook('file', config.GlobalConfig)
2174
def test_load_hook_locations(self):
2175
self.assertLoadHook('file', config.LocationConfig, self.tree.basedir)
2177
def test_load_hook_branch(self):
2178
self.assertLoadHook('file', config.BranchConfig, self.tree.branch)
2180
def assertSaveHook(self, conf):
2184
config.OldConfigHooks.install_named_hook('save', hook, None)
2186
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2187
self.assertLength(0, calls)
2188
# Setting an option triggers a save
2189
conf.set_user_option('foo', 'bar')
2190
self.assertLength(1, calls)
2191
# Since we can't assert about conf, we just use the number of calls ;-/
2193
def test_save_hook_bazaar(self):
2194
self.assertSaveHook(self.bazaar_config)
2196
def test_save_hook_locations(self):
2197
self.assertSaveHook(self.locations_config)
2199
def test_save_hook_branch(self):
2200
self.assertSaveHook(self.branch_config)
2203
class TestOldConfigHooksForRemote(tests.TestCaseWithTransport):
2204
"""Tests config hooks for remote configs.
2206
No tests for the remove hook as this is not implemented there.
2210
super(TestOldConfigHooksForRemote, self).setUp()
2211
self.transport_server = test_server.SmartTCPServer_for_testing
2212
create_configs_with_file_option(self)
2214
def assertGetHook(self, conf, name, value):
2218
config.OldConfigHooks.install_named_hook('get', hook, None)
2220
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2221
self.assertLength(0, calls)
2222
actual_value = conf.get_option(name)
2223
self.assertEquals(value, actual_value)
2224
self.assertLength(1, calls)
2225
self.assertEquals((conf, name, value), calls[0])
2227
def test_get_hook_remote_branch(self):
2228
remote_branch = branch.Branch.open(self.get_url('tree'))
2229
self.assertGetHook(remote_branch._get_config(), 'file', 'branch')
2231
def test_get_hook_remote_bzrdir(self):
2232
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2233
conf = remote_bzrdir._get_config()
2234
conf.set_option('remotedir', 'file')
2235
self.assertGetHook(conf, 'file', 'remotedir')
2237
def assertSetHook(self, conf, name, value):
2241
config.OldConfigHooks.install_named_hook('set', hook, None)
2243
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2244
self.assertLength(0, calls)
2245
conf.set_option(value, name)
2246
self.assertLength(1, calls)
2247
# We can't assert the conf object below as different configs use
2248
# different means to implement set_user_option and we care only about
2250
self.assertEquals((name, value), calls[0][1:])
2252
def test_set_hook_remote_branch(self):
2253
remote_branch = branch.Branch.open(self.get_url('tree'))
2254
self.addCleanup(remote_branch.lock_write().unlock)
2255
self.assertSetHook(remote_branch._get_config(), 'file', 'remote')
2257
def test_set_hook_remote_bzrdir(self):
2258
remote_branch = branch.Branch.open(self.get_url('tree'))
2259
self.addCleanup(remote_branch.lock_write().unlock)
2260
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2261
self.assertSetHook(remote_bzrdir._get_config(), 'file', 'remotedir')
2263
def assertLoadHook(self, expected_nb_calls, name, conf_class, *conf_args):
2267
config.OldConfigHooks.install_named_hook('load', hook, None)
2269
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2270
self.assertLength(0, calls)
2272
conf = conf_class(*conf_args)
2273
# Access an option to trigger a load
2274
conf.get_option(name)
2275
self.assertLength(expected_nb_calls, calls)
2276
# Since we can't assert about conf, we just use the number of calls ;-/
2278
def test_load_hook_remote_branch(self):
2279
remote_branch = branch.Branch.open(self.get_url('tree'))
2280
self.assertLoadHook(1, 'file', remote.RemoteBranchConfig, remote_branch)
2282
def test_load_hook_remote_bzrdir(self):
2283
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2284
# The config file doesn't exist, set an option to force its creation
2285
conf = remote_bzrdir._get_config()
2286
conf.set_option('remotedir', 'file')
2287
# We get one call for the server and one call for the client, this is
2288
# caused by the differences in implementations betwen
2289
# SmartServerBzrDirRequestConfigFile (in smart/bzrdir.py) and
2290
# SmartServerBranchGetConfigFile (in smart/branch.py)
2291
self.assertLoadHook(2 ,'file', remote.RemoteBzrDirConfig, remote_bzrdir)
2293
def assertSaveHook(self, conf):
2297
config.OldConfigHooks.install_named_hook('save', hook, None)
2299
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2300
self.assertLength(0, calls)
2301
# Setting an option triggers a save
2302
conf.set_option('foo', 'bar')
2303
self.assertLength(1, calls)
2304
# Since we can't assert about conf, we just use the number of calls ;-/
2306
def test_save_hook_remote_branch(self):
2307
remote_branch = branch.Branch.open(self.get_url('tree'))
2308
self.addCleanup(remote_branch.lock_write().unlock)
2309
self.assertSaveHook(remote_branch._get_config())
2311
def test_save_hook_remote_bzrdir(self):
2312
remote_branch = branch.Branch.open(self.get_url('tree'))
2313
self.addCleanup(remote_branch.lock_write().unlock)
2314
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2315
self.assertSaveHook(remote_bzrdir._get_config())
2318
class TestOption(tests.TestCase):
2320
def test_default_value(self):
2321
opt = config.Option('foo', default='bar')
2322
self.assertEquals('bar', opt.get_default())
2324
def test_callable_default_value(self):
2325
def bar_as_unicode():
2327
opt = config.Option('foo', default=bar_as_unicode)
2328
self.assertEquals('bar', opt.get_default())
2330
def test_default_value_from_env(self):
2331
opt = config.Option('foo', default='bar', default_from_env=['FOO'])
2332
self.overrideEnv('FOO', 'quux')
2333
# Env variable provides a default taking over the option one
2334
self.assertEquals('quux', opt.get_default())
2336
def test_first_default_value_from_env_wins(self):
2337
opt = config.Option('foo', default='bar',
2338
default_from_env=['NO_VALUE', 'FOO', 'BAZ'])
2339
self.overrideEnv('FOO', 'foo')
2340
self.overrideEnv('BAZ', 'baz')
2341
# The first env var set wins
2342
self.assertEquals('foo', opt.get_default())
2344
def test_not_supported_list_default_value(self):
2345
self.assertRaises(AssertionError, config.Option, 'foo', default=[1])
2347
def test_not_supported_object_default_value(self):
2348
self.assertRaises(AssertionError, config.Option, 'foo',
2351
def test_not_supported_callable_default_value_not_unicode(self):
2352
def bar_not_unicode():
2354
opt = config.Option('foo', default=bar_not_unicode)
2355
self.assertRaises(AssertionError, opt.get_default)
2358
class TestOptionConverterMixin(object):
2360
def assertConverted(self, expected, opt, value):
2361
self.assertEquals(expected, opt.convert_from_unicode(value))
2363
def assertWarns(self, opt, value):
2366
warnings.append(args[0] % args[1:])
2367
self.overrideAttr(trace, 'warning', warning)
2368
self.assertEquals(None, opt.convert_from_unicode(value))
2369
self.assertLength(1, warnings)
2371
'Value "%s" is not valid for "%s"' % (value, opt.name),
2374
def assertErrors(self, opt, value):
2375
self.assertRaises(errors.ConfigOptionValueError,
2376
opt.convert_from_unicode, value)
2378
def assertConvertInvalid(self, opt, invalid_value):
2380
self.assertEquals(None, opt.convert_from_unicode(invalid_value))
2381
opt.invalid = 'warning'
2382
self.assertWarns(opt, invalid_value)
2383
opt.invalid = 'error'
2384
self.assertErrors(opt, invalid_value)
2387
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2389
def get_option(self):
2390
return config.Option('foo', help='A boolean.',
2391
from_unicode=config.bool_from_store)
2393
def test_convert_invalid(self):
2394
opt = self.get_option()
2395
# A string that is not recognized as a boolean
2396
self.assertConvertInvalid(opt, u'invalid-boolean')
2397
# A list of strings is never recognized as a boolean
2398
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2400
def test_convert_valid(self):
2401
opt = self.get_option()
2402
self.assertConverted(True, opt, u'True')
2403
self.assertConverted(True, opt, u'1')
2404
self.assertConverted(False, opt, u'False')
2407
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2409
def get_option(self):
2410
return config.Option('foo', help='An integer.',
2411
from_unicode=config.int_from_store)
2413
def test_convert_invalid(self):
2414
opt = self.get_option()
2415
# A string that is not recognized as an integer
2416
self.assertConvertInvalid(opt, u'forty-two')
2417
# A list of strings is never recognized as an integer
2418
self.assertConvertInvalid(opt, [u'a', u'list'])
2420
def test_convert_valid(self):
2421
opt = self.get_option()
2422
self.assertConverted(16, opt, u'16')
2425
class TestOptionWithListConverter(tests.TestCase, TestOptionConverterMixin):
2427
def get_option(self):
2428
return config.Option('foo', help='A list.',
2429
from_unicode=config.list_from_store)
2431
def test_convert_invalid(self):
2432
# No string is invalid as all forms can be converted to a list
2435
def test_convert_valid(self):
2436
opt = self.get_option()
2437
# An empty string is an empty list
2438
self.assertConverted([], opt, '') # Using a bare str() just in case
2439
self.assertConverted([], opt, u'')
2441
self.assertConverted([u'True'], opt, u'True')
2443
self.assertConverted([u'42'], opt, u'42')
2445
self.assertConverted([u'bar'], opt, u'bar')
2446
# A list remains a list (configObj will turn a string containing commas
2447
# into a list, but that's not what we're testing here)
2448
self.assertConverted([u'foo', u'1', u'True'],
2449
opt, [u'foo', u'1', u'True'])
2452
class TestOptionConverterMixin(object):
2454
def assertConverted(self, expected, opt, value):
2455
self.assertEquals(expected, opt.convert_from_unicode(value))
2457
def assertWarns(self, opt, value):
2460
warnings.append(args[0] % args[1:])
2461
self.overrideAttr(trace, 'warning', warning)
2462
self.assertEquals(None, opt.convert_from_unicode(value))
2463
self.assertLength(1, warnings)
2465
'Value "%s" is not valid for "%s"' % (value, opt.name),
2468
def assertErrors(self, opt, value):
2469
self.assertRaises(errors.ConfigOptionValueError,
2470
opt.convert_from_unicode, value)
2472
def assertConvertInvalid(self, opt, invalid_value):
2474
self.assertEquals(None, opt.convert_from_unicode(invalid_value))
2475
opt.invalid = 'warning'
2476
self.assertWarns(opt, invalid_value)
2477
opt.invalid = 'error'
2478
self.assertErrors(opt, invalid_value)
2481
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2483
def get_option(self):
2484
return config.Option('foo', help='A boolean.',
2485
from_unicode=config.bool_from_store)
2487
def test_convert_invalid(self):
2488
opt = self.get_option()
2489
# A string that is not recognized as a boolean
2490
self.assertConvertInvalid(opt, u'invalid-boolean')
2491
# A list of strings is never recognized as a boolean
2492
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2494
def test_convert_valid(self):
2495
opt = self.get_option()
2496
self.assertConverted(True, opt, u'True')
2497
self.assertConverted(True, opt, u'1')
2498
self.assertConverted(False, opt, u'False')
2501
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2503
def get_option(self):
2504
return config.Option('foo', help='An integer.',
2505
from_unicode=config.int_from_store)
2507
def test_convert_invalid(self):
2508
opt = self.get_option()
2509
# A string that is not recognized as an integer
2510
self.assertConvertInvalid(opt, u'forty-two')
2511
# A list of strings is never recognized as an integer
2512
self.assertConvertInvalid(opt, [u'a', u'list'])
2514
def test_convert_valid(self):
2515
opt = self.get_option()
2516
self.assertConverted(16, opt, u'16')
2519
class TestOptionWithListConverter(tests.TestCase, TestOptionConverterMixin):
2521
def get_option(self):
2522
return config.Option('foo', help='A list.',
2523
from_unicode=config.list_from_store)
2525
def test_convert_invalid(self):
2526
opt = self.get_option()
2527
# We don't even try to convert a list into a list, we only expect
2529
self.assertConvertInvalid(opt, [1])
2530
# No string is invalid as all forms can be converted to a list
2532
def test_convert_valid(self):
2533
opt = self.get_option()
2534
# An empty string is an empty list
2535
self.assertConverted([], opt, '') # Using a bare str() just in case
2536
self.assertConverted([], opt, u'')
2538
self.assertConverted([u'True'], opt, u'True')
2540
self.assertConverted([u'42'], opt, u'42')
2542
self.assertConverted([u'bar'], opt, u'bar')
2545
class TestOptionRegistry(tests.TestCase):
2548
super(TestOptionRegistry, self).setUp()
2549
# Always start with an empty registry
2550
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2551
self.registry = config.option_registry
2553
def test_register(self):
2554
opt = config.Option('foo')
2555
self.registry.register(opt)
2556
self.assertIs(opt, self.registry.get('foo'))
2558
def test_registered_help(self):
2559
opt = config.Option('foo', help='A simple option')
2560
self.registry.register(opt)
2561
self.assertEquals('A simple option', self.registry.get_help('foo'))
2563
lazy_option = config.Option('lazy_foo', help='Lazy help')
2565
def test_register_lazy(self):
2566
self.registry.register_lazy('lazy_foo', self.__module__,
2567
'TestOptionRegistry.lazy_option')
2568
self.assertIs(self.lazy_option, self.registry.get('lazy_foo'))
2570
def test_registered_lazy_help(self):
2571
self.registry.register_lazy('lazy_foo', self.__module__,
2572
'TestOptionRegistry.lazy_option')
2573
self.assertEquals('Lazy help', self.registry.get_help('lazy_foo'))
2576
class TestRegisteredOptions(tests.TestCase):
2577
"""All registered options should verify some constraints."""
2579
scenarios = [(key, {'option_name': key, 'option': option}) for key, option
2580
in config.option_registry.iteritems()]
2583
super(TestRegisteredOptions, self).setUp()
2584
self.registry = config.option_registry
2586
def test_proper_name(self):
2587
# An option should be registered under its own name, this can't be
2588
# checked at registration time for the lazy ones.
2589
self.assertEquals(self.option_name, self.option.name)
2591
def test_help_is_set(self):
2592
option_help = self.registry.get_help(self.option_name)
2593
self.assertNotEquals(None, option_help)
2594
# Come on, think about the user, he really wants to know what the
2596
self.assertIsNot(None, option_help)
2597
self.assertNotEquals('', option_help)
2600
class TestSection(tests.TestCase):
2602
# FIXME: Parametrize so that all sections produced by Stores run these
2603
# tests -- vila 2011-04-01
2605
def test_get_a_value(self):
2606
a_dict = dict(foo='bar')
2607
section = config.Section('myID', a_dict)
2608
self.assertEquals('bar', section.get('foo'))
2610
def test_get_unknown_option(self):
2612
section = config.Section(None, a_dict)
2613
self.assertEquals('out of thin air',
2614
section.get('foo', 'out of thin air'))
2616
def test_options_is_shared(self):
2618
section = config.Section(None, a_dict)
2619
self.assertIs(a_dict, section.options)
2622
class TestMutableSection(tests.TestCase):
2624
scenarios = [('mutable',
2626
lambda opts: config.MutableSection('myID', opts)},),
2630
a_dict = dict(foo='bar')
2631
section = self.get_section(a_dict)
2632
section.set('foo', 'new_value')
2633
self.assertEquals('new_value', section.get('foo'))
2634
# The change appears in the shared section
2635
self.assertEquals('new_value', a_dict.get('foo'))
2636
# We keep track of the change
2637
self.assertTrue('foo' in section.orig)
2638
self.assertEquals('bar', section.orig.get('foo'))
2640
def test_set_preserve_original_once(self):
2641
a_dict = dict(foo='bar')
2642
section = self.get_section(a_dict)
2643
section.set('foo', 'first_value')
2644
section.set('foo', 'second_value')
2645
# We keep track of the original value
2646
self.assertTrue('foo' in section.orig)
2647
self.assertEquals('bar', section.orig.get('foo'))
2649
def test_remove(self):
2650
a_dict = dict(foo='bar')
2651
section = self.get_section(a_dict)
2652
section.remove('foo')
2653
# We get None for unknown options via the default value
2654
self.assertEquals(None, section.get('foo'))
2655
# Or we just get the default value
2656
self.assertEquals('unknown', section.get('foo', 'unknown'))
2657
self.assertFalse('foo' in section.options)
2658
# We keep track of the deletion
2659
self.assertTrue('foo' in section.orig)
2660
self.assertEquals('bar', section.orig.get('foo'))
2662
def test_remove_new_option(self):
2664
section = self.get_section(a_dict)
2665
section.set('foo', 'bar')
2666
section.remove('foo')
2667
self.assertFalse('foo' in section.options)
2668
# The option didn't exist initially so it we need to keep track of it
2669
# with a special value
2670
self.assertTrue('foo' in section.orig)
2671
self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
2674
class TestCommandLineStore(tests.TestCase):
2677
super(TestCommandLineStore, self).setUp()
2678
self.store = config.CommandLineStore()
2680
def get_section(self):
2681
"""Get the unique section for the command line overrides."""
2682
sections = list(self.store.get_sections())
2683
self.assertLength(1, sections)
2684
store, section = sections[0]
2685
self.assertEquals(self.store, store)
2688
def test_no_override(self):
2689
self.store._from_cmdline([])
2690
section = self.get_section()
2691
self.assertLength(0, list(section.iter_option_names()))
2693
def test_simple_override(self):
2694
self.store._from_cmdline(['a=b'])
2695
section = self.get_section()
2696
self.assertEqual('b', section.get('a'))
2698
def test_list_override(self):
2699
self.store._from_cmdline(['l=1,2,3'])
2700
val = self.get_section().get('l')
2701
self.assertEqual('1,2,3', val)
2702
# Reminder: lists should be registered as such explicitely, otherwise
2703
# the conversion needs to be done afterwards.
2704
self.assertEqual(['1', '2', '3'], config.list_from_store(val))
2706
def test_multiple_overrides(self):
2707
self.store._from_cmdline(['a=b', 'x=y'])
2708
section = self.get_section()
2709
self.assertEquals('b', section.get('a'))
2710
self.assertEquals('y', section.get('x'))
2712
def test_wrong_syntax(self):
2713
self.assertRaises(errors.BzrCommandError,
2714
self.store._from_cmdline, ['a=b', 'c'])
2717
class TestStore(tests.TestCaseWithTransport):
2719
def assertSectionContent(self, expected, (store, section)):
2720
"""Assert that some options have the proper values in a section."""
2721
expected_name, expected_options = expected
2722
self.assertEquals(expected_name, section.id)
2725
dict([(k, section.get(k)) for k in expected_options.keys()]))
2728
class TestReadonlyStore(TestStore):
2730
scenarios = [(key, {'get_store': builder}) for key, builder
2731
in config.test_store_builder_registry.iteritems()]
2733
def test_building_delays_load(self):
2734
store = self.get_store(self)
2735
self.assertEquals(False, store.is_loaded())
2736
store._load_from_string('')
2737
self.assertEquals(True, store.is_loaded())
2739
def test_get_no_sections_for_empty(self):
2740
store = self.get_store(self)
2741
store._load_from_string('')
2742
self.assertEquals([], list(store.get_sections()))
2744
def test_get_default_section(self):
2745
store = self.get_store(self)
2746
store._load_from_string('foo=bar')
2747
sections = list(store.get_sections())
2748
self.assertLength(1, sections)
2749
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2751
def test_get_named_section(self):
2752
store = self.get_store(self)
2753
store._load_from_string('[baz]\nfoo=bar')
2754
sections = list(store.get_sections())
2755
self.assertLength(1, sections)
2756
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2758
def test_load_from_string_fails_for_non_empty_store(self):
2759
store = self.get_store(self)
2760
store._load_from_string('foo=bar')
2761
self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2764
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2765
"""Simulate loading a config store 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
t = self.get_transport()
2778
# From http://pad.lv/799212
2779
unicode_user = u'b\N{Euro Sign}ar'
2780
unicode_content = u'user=%s' % (unicode_user,)
2781
utf8_content = unicode_content.encode('utf8')
2782
# Store the raw content in the config file
2783
t.put_bytes('foo.conf', utf8_content)
2784
store = config.TransportIniFileStore(t, 'foo.conf')
2786
stack = config.Stack([store.get_sections], store)
2787
self.assertEquals(unicode_user, stack.get('user'))
2789
def test_load_non_ascii(self):
2790
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2791
t = self.get_transport()
2792
t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2793
store = config.TransportIniFileStore(t, 'foo.conf')
2794
self.assertRaises(errors.ConfigContentError, store.load)
2796
def test_load_erroneous_content(self):
2797
"""Ensure we display a proper error on content that can't be parsed."""
2798
t = self.get_transport()
2799
t.put_bytes('foo.conf', '[open_section\n')
2800
store = config.TransportIniFileStore(t, 'foo.conf')
2801
self.assertRaises(errors.ParseConfigError, store.load)
2803
def test_load_permission_denied(self):
2804
"""Ensure we get warned when trying to load an inaccessible file."""
2807
warnings.append(args[0] % args[1:])
2808
self.overrideAttr(trace, 'warning', warning)
2810
t = self.get_transport()
2812
def get_bytes(relpath):
2813
raise errors.PermissionDenied(relpath, "")
2814
t.get_bytes = get_bytes
2815
store = config.TransportIniFileStore(t, 'foo.conf')
2816
self.assertRaises(errors.PermissionDenied, store.load)
2819
[u'Permission denied while trying to load configuration store %s.'
2820
% store.external_url()])
2823
class TestIniConfigContent(tests.TestCaseWithTransport):
2824
"""Simulate loading a IniBasedConfig with content of various encodings.
2826
All files produced by bzr are in utf8 content.
2828
Users may modify them manually and end up with a file that can't be
2829
loaded. We need to issue proper error messages in this case.
2832
invalid_utf8_char = '\xff'
2834
def test_load_utf8(self):
2835
"""Ensure we can load an utf8-encoded file."""
2836
# From http://pad.lv/799212
2837
unicode_user = u'b\N{Euro Sign}ar'
2838
unicode_content = u'user=%s' % (unicode_user,)
2839
utf8_content = unicode_content.encode('utf8')
2840
# Store the raw content in the config file
2841
with open('foo.conf', 'wb') as f:
2842
f.write(utf8_content)
2843
conf = config.IniBasedConfig(file_name='foo.conf')
2844
self.assertEquals(unicode_user, conf.get_user_option('user'))
2846
def test_load_badly_encoded_content(self):
2847
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2848
with open('foo.conf', 'wb') as f:
2849
f.write('user=foo\n#%s\n' % (self.invalid_utf8_char,))
2850
conf = config.IniBasedConfig(file_name='foo.conf')
2851
self.assertRaises(errors.ConfigContentError, conf._get_parser)
2853
def test_load_erroneous_content(self):
2854
"""Ensure we display a proper error on content that can't be parsed."""
2855
with open('foo.conf', 'wb') as f:
2856
f.write('[open_section\n')
2857
conf = config.IniBasedConfig(file_name='foo.conf')
2858
self.assertRaises(errors.ParseConfigError, conf._get_parser)
2861
class TestMutableStore(TestStore):
2863
scenarios = [(key, {'store_id': key, 'get_store': builder}) for key, builder
2864
in config.test_store_builder_registry.iteritems()]
2867
super(TestMutableStore, self).setUp()
2868
self.transport = self.get_transport()
2870
def has_store(self, store):
2871
store_basename = urlutils.relative_url(self.transport.external_url(),
2872
store.external_url())
2873
return self.transport.has(store_basename)
2875
def test_save_empty_creates_no_file(self):
2876
# FIXME: There should be a better way than relying on the test
2877
# parametrization to identify branch.conf -- vila 2011-0526
2878
if self.store_id in ('branch', 'remote_branch'):
2879
raise tests.TestNotApplicable(
2880
'branch.conf is *always* created when a branch is initialized')
2881
store = self.get_store(self)
2883
self.assertEquals(False, self.has_store(store))
2885
def test_save_emptied_succeeds(self):
2886
store = self.get_store(self)
2887
store._load_from_string('foo=bar\n')
2888
section = store.get_mutable_section(None)
2889
section.remove('foo')
2891
self.assertEquals(True, self.has_store(store))
2892
modified_store = self.get_store(self)
2893
sections = list(modified_store.get_sections())
2894
self.assertLength(0, sections)
2896
def test_save_with_content_succeeds(self):
2897
# FIXME: There should be a better way than relying on the test
2898
# parametrization to identify branch.conf -- vila 2011-0526
2899
if self.store_id in ('branch', 'remote_branch'):
2900
raise tests.TestNotApplicable(
2901
'branch.conf is *always* created when a branch is initialized')
2902
store = self.get_store(self)
2903
store._load_from_string('foo=bar\n')
2904
self.assertEquals(False, self.has_store(store))
2906
self.assertEquals(True, self.has_store(store))
2907
modified_store = self.get_store(self)
2908
sections = list(modified_store.get_sections())
2909
self.assertLength(1, sections)
2910
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2912
def test_set_option_in_empty_store(self):
2913
store = self.get_store(self)
2914
section = store.get_mutable_section(None)
2915
section.set('foo', 'bar')
2917
modified_store = self.get_store(self)
2918
sections = list(modified_store.get_sections())
2919
self.assertLength(1, sections)
2920
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2922
def test_set_option_in_default_section(self):
2923
store = self.get_store(self)
2924
store._load_from_string('')
2925
section = store.get_mutable_section(None)
2926
section.set('foo', 'bar')
2928
modified_store = self.get_store(self)
2929
sections = list(modified_store.get_sections())
2930
self.assertLength(1, sections)
2931
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2933
def test_set_option_in_named_section(self):
2934
store = self.get_store(self)
2935
store._load_from_string('')
2936
section = store.get_mutable_section('baz')
2937
section.set('foo', 'bar')
2939
modified_store = self.get_store(self)
2940
sections = list(modified_store.get_sections())
2941
self.assertLength(1, sections)
2942
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2944
def test_load_hook(self):
2945
# We first needs to ensure that the store exists
2946
store = self.get_store(self)
2947
section = store.get_mutable_section('baz')
2948
section.set('foo', 'bar')
2950
# Now we can try to load it
2951
store = self.get_store(self)
2955
config.ConfigHooks.install_named_hook('load', hook, None)
2956
self.assertLength(0, calls)
2958
self.assertLength(1, calls)
2959
self.assertEquals((store,), calls[0])
2961
def test_save_hook(self):
2965
config.ConfigHooks.install_named_hook('save', hook, None)
2966
self.assertLength(0, calls)
2967
store = self.get_store(self)
2968
section = store.get_mutable_section('baz')
2969
section.set('foo', 'bar')
2971
self.assertLength(1, calls)
2972
self.assertEquals((store,), calls[0])
2975
class TestTransportIniFileStore(TestStore):
2977
def test_loading_unknown_file_fails(self):
2978
store = config.TransportIniFileStore(self.get_transport(),
2980
self.assertRaises(errors.NoSuchFile, store.load)
2982
def test_invalid_content(self):
2983
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
2984
self.assertEquals(False, store.is_loaded())
2985
exc = self.assertRaises(
2986
errors.ParseConfigError, store._load_from_string,
2987
'this is invalid !')
2988
self.assertEndsWith(exc.filename, 'foo.conf')
2989
# And the load failed
2990
self.assertEquals(False, store.is_loaded())
2992
def test_get_embedded_sections(self):
2993
# A more complicated example (which also shows that section names and
2994
# option names share the same name space...)
2995
# FIXME: This should be fixed by forbidding dicts as values ?
2996
# -- vila 2011-04-05
2997
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
2998
store._load_from_string('''
3002
foo_in_DEFAULT=foo_DEFAULT
3010
sections = list(store.get_sections())
3011
self.assertLength(4, sections)
3012
# The default section has no name.
3013
# List values are provided as strings and need to be explicitly
3014
# converted by specifying from_unicode=list_from_store at option
3016
self.assertSectionContent((None, {'foo': 'bar', 'l': u'1,2'}),
3018
self.assertSectionContent(
3019
('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
3020
self.assertSectionContent(
3021
('bar', {'foo_in_bar': 'barbar'}), sections[2])
3022
# sub sections are provided as embedded dicts.
3023
self.assertSectionContent(
3024
('baz', {'foo_in_baz': 'barbaz', 'qux': {'foo_in_qux': 'quux'}}),
3028
class TestLockableIniFileStore(TestStore):
3030
def test_create_store_in_created_dir(self):
3031
self.assertPathDoesNotExist('dir')
3032
t = self.get_transport('dir/subdir')
3033
store = config.LockableIniFileStore(t, 'foo.conf')
3034
store.get_mutable_section(None).set('foo', 'bar')
3036
self.assertPathExists('dir/subdir')
3039
class TestConcurrentStoreUpdates(TestStore):
3040
"""Test that Stores properly handle conccurent updates.
3042
New Store implementation may fail some of these tests but until such
3043
implementations exist it's hard to properly filter them from the scenarios
3044
applied here. If you encounter such a case, contact the bzr devs.
3047
scenarios = [(key, {'get_stack': builder}) for key, builder
3048
in config.test_stack_builder_registry.iteritems()]
3051
super(TestConcurrentStoreUpdates, self).setUp()
3052
self.stack = self.get_stack(self)
3053
if not isinstance(self.stack, config._CompatibleStack):
3054
raise tests.TestNotApplicable(
3055
'%s is not meant to be compatible with the old config design'
3057
self.stack.set('one', '1')
3058
self.stack.set('two', '2')
3060
self.stack.store.save()
3062
def test_simple_read_access(self):
3063
self.assertEquals('1', self.stack.get('one'))
3065
def test_simple_write_access(self):
3066
self.stack.set('one', 'one')
3067
self.assertEquals('one', self.stack.get('one'))
3069
def test_listen_to_the_last_speaker(self):
3071
c2 = self.get_stack(self)
3072
c1.set('one', 'ONE')
3073
c2.set('two', 'TWO')
3074
self.assertEquals('ONE', c1.get('one'))
3075
self.assertEquals('TWO', c2.get('two'))
3076
# The second update respect the first one
3077
self.assertEquals('ONE', c2.get('one'))
3079
def test_last_speaker_wins(self):
3080
# If the same config is not shared, the same variable modified twice
3081
# can only see a single result.
3083
c2 = self.get_stack(self)
3086
self.assertEquals('c2', c2.get('one'))
3087
# The first modification is still available until another refresh
3089
self.assertEquals('c1', c1.get('one'))
3090
c1.set('two', 'done')
3091
self.assertEquals('c2', c1.get('one'))
3093
def test_writes_are_serialized(self):
3095
c2 = self.get_stack(self)
3097
# We spawn a thread that will pause *during* the config saving.
3098
before_writing = threading.Event()
3099
after_writing = threading.Event()
3100
writing_done = threading.Event()
3101
c1_save_without_locking_orig = c1.store.save_without_locking
3102
def c1_save_without_locking():
3103
before_writing.set()
3104
c1_save_without_locking_orig()
3105
# The lock is held. We wait for the main thread to decide when to
3107
after_writing.wait()
3108
c1.store.save_without_locking = c1_save_without_locking
3112
t1 = threading.Thread(target=c1_set)
3113
# Collect the thread after the test
3114
self.addCleanup(t1.join)
3115
# Be ready to unblock the thread if the test goes wrong
3116
self.addCleanup(after_writing.set)
3118
before_writing.wait()
3119
self.assertRaises(errors.LockContention,
3120
c2.set, 'one', 'c2')
3121
self.assertEquals('c1', c1.get('one'))
3122
# Let the lock be released
3126
self.assertEquals('c2', c2.get('one'))
3128
def test_read_while_writing(self):
3130
# We spawn a thread that will pause *during* the write
3131
ready_to_write = threading.Event()
3132
do_writing = threading.Event()
3133
writing_done = threading.Event()
3134
# We override the _save implementation so we know the store is locked
3135
c1_save_without_locking_orig = c1.store.save_without_locking
3136
def c1_save_without_locking():
3137
ready_to_write.set()
3138
# The lock is held. We wait for the main thread to decide when to
3141
c1_save_without_locking_orig()
3143
c1.store.save_without_locking = c1_save_without_locking
3146
t1 = threading.Thread(target=c1_set)
3147
# Collect the thread after the test
3148
self.addCleanup(t1.join)
3149
# Be ready to unblock the thread if the test goes wrong
3150
self.addCleanup(do_writing.set)
3152
# Ensure the thread is ready to write
3153
ready_to_write.wait()
3154
self.assertEquals('c1', c1.get('one'))
3155
# If we read during the write, we get the old value
3156
c2 = self.get_stack(self)
3157
self.assertEquals('1', c2.get('one'))
3158
# Let the writing occur and ensure it occurred
3161
# Now we get the updated value
3162
c3 = self.get_stack(self)
3163
self.assertEquals('c1', c3.get('one'))
3165
# FIXME: It may be worth looking into removing the lock dir when it's not
3166
# needed anymore and look at possible fallouts for concurrent lockers. This
3167
# will matter if/when we use config files outside of bazaar directories
3168
# (.bazaar or .bzr) -- vila 20110-04-111
3171
class TestSectionMatcher(TestStore):
3173
scenarios = [('location', {'matcher': config.LocationMatcher}),
3174
('id', {'matcher': config.NameMatcher}),]
3177
super(TestSectionMatcher, self).setUp()
3178
# Any simple store is good enough
3179
self.get_store = config.test_store_builder_registry.get('configobj')
3181
def test_no_matches_for_empty_stores(self):
3182
store = self.get_store(self)
3183
store._load_from_string('')
3184
matcher = self.matcher(store, '/bar')
3185
self.assertEquals([], list(matcher.get_sections()))
3187
def test_build_doesnt_load_store(self):
3188
store = self.get_store(self)
3189
matcher = self.matcher(store, '/bar')
3190
self.assertFalse(store.is_loaded())
3193
class TestLocationSection(tests.TestCase):
3195
def get_section(self, options, extra_path):
3196
section = config.Section('foo', options)
3197
# We don't care about the length so we use '0'
3198
return config.LocationSection(section, 0, extra_path)
3200
def test_simple_option(self):
3201
section = self.get_section({'foo': 'bar'}, '')
3202
self.assertEquals('bar', section.get('foo'))
3204
def test_option_with_extra_path(self):
3205
section = self.get_section({'foo': 'bar', 'foo:policy': 'appendpath'},
3207
self.assertEquals('bar/baz', section.get('foo'))
3209
def test_invalid_policy(self):
3210
section = self.get_section({'foo': 'bar', 'foo:policy': 'die'},
3212
# invalid policies are ignored
3213
self.assertEquals('bar', section.get('foo'))
3216
class TestLocationMatcher(TestStore):
3219
super(TestLocationMatcher, self).setUp()
3220
# Any simple store is good enough
3221
self.get_store = config.test_store_builder_registry.get('configobj')
3223
def test_unrelated_section_excluded(self):
3224
store = self.get_store(self)
3225
store._load_from_string('''
3233
section=/foo/bar/baz
3237
self.assertEquals(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
3239
[section.id for _, section in store.get_sections()])
3240
matcher = config.LocationMatcher(store, '/foo/bar/quux')
3241
sections = [section for s, section in matcher.get_sections()]
3242
self.assertEquals([3, 2],
3243
[section.length for section in sections])
3244
self.assertEquals(['/foo/bar', '/foo'],
3245
[section.id for section in sections])
3246
self.assertEquals(['quux', 'bar/quux'],
3247
[section.extra_path for section in sections])
3249
def test_more_specific_sections_first(self):
3250
store = self.get_store(self)
3251
store._load_from_string('''
3257
self.assertEquals(['/foo', '/foo/bar'],
3258
[section.id for _, section in store.get_sections()])
3259
matcher = config.LocationMatcher(store, '/foo/bar/baz')
3260
sections = [section for s, section in matcher.get_sections()]
3261
self.assertEquals([3, 2],
3262
[section.length for section in sections])
3263
self.assertEquals(['/foo/bar', '/foo'],
3264
[section.id for section in sections])
3265
self.assertEquals(['baz', 'bar/baz'],
3266
[section.extra_path for section in sections])
3268
def test_appendpath_in_no_name_section(self):
3269
# It's a bit weird to allow appendpath in a no-name section, but
3270
# someone may found a use for it
3271
store = self.get_store(self)
3272
store._load_from_string('''
3274
foo:policy = appendpath
3276
matcher = config.LocationMatcher(store, 'dir/subdir')
3277
sections = list(matcher.get_sections())
3278
self.assertLength(1, sections)
3279
self.assertEquals('bar/dir/subdir', sections[0][1].get('foo'))
3281
def test_file_urls_are_normalized(self):
3282
store = self.get_store(self)
3283
if sys.platform == 'win32':
3284
expected_url = 'file:///C:/dir/subdir'
3285
expected_location = 'C:/dir/subdir'
3287
expected_url = 'file:///dir/subdir'
3288
expected_location = '/dir/subdir'
3289
matcher = config.LocationMatcher(store, expected_url)
3290
self.assertEquals(expected_location, matcher.location)
3293
class TestNameMatcher(TestStore):
3296
super(TestNameMatcher, self).setUp()
3297
self.matcher = config.NameMatcher
3298
# Any simple store is good enough
3299
self.get_store = config.test_store_builder_registry.get('configobj')
3301
def get_matching_sections(self, name):
3302
store = self.get_store(self)
3303
store._load_from_string('''
3311
matcher = self.matcher(store, name)
3312
return list(matcher.get_sections())
3314
def test_matching(self):
3315
sections = self.get_matching_sections('foo')
3316
self.assertLength(1, sections)
3317
self.assertSectionContent(('foo', {'option': 'foo'}), sections[0])
3319
def test_not_matching(self):
3320
sections = self.get_matching_sections('baz')
3321
self.assertLength(0, sections)
3324
class TestStackGet(tests.TestCase):
3326
# FIXME: This should be parametrized for all known Stack or dedicated
3327
# paramerized tests created to avoid bloating -- vila 2011-03-31
3329
def overrideOptionRegistry(self):
3330
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3332
def test_single_config_get(self):
3333
conf = dict(foo='bar')
3334
conf_stack = config.Stack([conf])
3335
self.assertEquals('bar', conf_stack.get('foo'))
3337
def test_get_with_registered_default_value(self):
3338
conf_stack = config.Stack([dict()])
3339
opt = config.Option('foo', default='bar')
3340
self.overrideOptionRegistry()
3341
config.option_registry.register('foo', opt)
3342
self.assertEquals('bar', conf_stack.get('foo'))
3344
def test_get_without_registered_default_value(self):
3345
conf_stack = config.Stack([dict()])
3346
opt = config.Option('foo')
3347
self.overrideOptionRegistry()
3348
config.option_registry.register('foo', opt)
3349
self.assertEquals(None, conf_stack.get('foo'))
3351
def test_get_without_default_value_for_not_registered(self):
3352
conf_stack = config.Stack([dict()])
3353
opt = config.Option('foo')
3354
self.overrideOptionRegistry()
3355
self.assertEquals(None, conf_stack.get('foo'))
3357
def test_get_first_definition(self):
3358
conf1 = dict(foo='bar')
3359
conf2 = dict(foo='baz')
3360
conf_stack = config.Stack([conf1, conf2])
3361
self.assertEquals('bar', conf_stack.get('foo'))
3363
def test_get_embedded_definition(self):
3364
conf1 = dict(yy='12')
3365
conf2 = config.Stack([dict(xx='42'), dict(foo='baz')])
3366
conf_stack = config.Stack([conf1, conf2])
3367
self.assertEquals('baz', conf_stack.get('foo'))
3369
def test_get_for_empty_section_callable(self):
3370
conf_stack = config.Stack([lambda : []])
3371
self.assertEquals(None, conf_stack.get('foo'))
3373
def test_get_for_broken_callable(self):
3374
# Trying to use and invalid callable raises an exception on first use
3375
conf_stack = config.Stack([lambda : object()])
3376
self.assertRaises(TypeError, conf_stack.get, 'foo')
3379
class TestStackWithTransport(tests.TestCaseWithTransport):
3381
scenarios = [(key, {'get_stack': builder}) for key, builder
3382
in config.test_stack_builder_registry.iteritems()]
3385
class TestConcreteStacks(TestStackWithTransport):
3387
def test_build_stack(self):
3388
# Just a smoke test to help debug builders
3389
stack = self.get_stack(self)
3392
class TestStackGet(TestStackWithTransport):
3395
super(TestStackGet, self).setUp()
3396
self.conf = self.get_stack(self)
3398
def test_get_for_empty_stack(self):
3399
self.assertEquals(None, self.conf.get('foo'))
3401
def test_get_hook(self):
3402
self.conf.set('foo', 'bar')
3406
config.ConfigHooks.install_named_hook('get', hook, None)
3407
self.assertLength(0, calls)
3408
value = self.conf.get('foo')
3409
self.assertEquals('bar', value)
3410
self.assertLength(1, calls)
3411
self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
3414
class TestStackGetWithConverter(tests.TestCaseWithTransport):
3417
super(TestStackGetWithConverter, self).setUp()
3418
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3419
self.registry = config.option_registry
3420
# We just want a simple stack with a simple store so we can inject
3421
# whatever content the tests need without caring about what section
3422
# names are valid for a given store/stack.
3423
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3424
self.conf = config.Stack([store.get_sections], store)
3426
def register_bool_option(self, name, default=None, default_from_env=None):
3427
b = config.Option(name, help='A boolean.',
3428
default=default, default_from_env=default_from_env,
3429
from_unicode=config.bool_from_store)
3430
self.registry.register(b)
3432
def test_get_default_bool_None(self):
3433
self.register_bool_option('foo')
3434
self.assertEquals(None, self.conf.get('foo'))
3436
def test_get_default_bool_True(self):
3437
self.register_bool_option('foo', u'True')
3438
self.assertEquals(True, self.conf.get('foo'))
3440
def test_get_default_bool_False(self):
3441
self.register_bool_option('foo', False)
3442
self.assertEquals(False, self.conf.get('foo'))
3444
def test_get_default_bool_False_as_string(self):
3445
self.register_bool_option('foo', u'False')
3446
self.assertEquals(False, self.conf.get('foo'))
3448
def test_get_default_bool_from_env_converted(self):
3449
self.register_bool_option('foo', u'True', default_from_env=['FOO'])
3450
self.overrideEnv('FOO', 'False')
3451
self.assertEquals(False, self.conf.get('foo'))
3453
def test_get_default_bool_when_conversion_fails(self):
3454
self.register_bool_option('foo', default='True')
3455
self.conf.store._load_from_string('foo=invalid boolean')
3456
self.assertEquals(True, self.conf.get('foo'))
3458
def register_integer_option(self, name,
3459
default=None, default_from_env=None):
3460
i = config.Option(name, help='An integer.',
3461
default=default, default_from_env=default_from_env,
3462
from_unicode=config.int_from_store)
3463
self.registry.register(i)
3465
def test_get_default_integer_None(self):
3466
self.register_integer_option('foo')
3467
self.assertEquals(None, self.conf.get('foo'))
3469
def test_get_default_integer(self):
3470
self.register_integer_option('foo', 42)
3471
self.assertEquals(42, self.conf.get('foo'))
3473
def test_get_default_integer_as_string(self):
3474
self.register_integer_option('foo', u'42')
3475
self.assertEquals(42, self.conf.get('foo'))
3477
def test_get_default_integer_from_env(self):
3478
self.register_integer_option('foo', default_from_env=['FOO'])
3479
self.overrideEnv('FOO', '18')
3480
self.assertEquals(18, self.conf.get('foo'))
3482
def test_get_default_integer_when_conversion_fails(self):
3483
self.register_integer_option('foo', default='12')
3484
self.conf.store._load_from_string('foo=invalid integer')
3485
self.assertEquals(12, self.conf.get('foo'))
3487
def register_list_option(self, name, default=None, default_from_env=None):
3488
l = config.Option(name, help='A list.',
3489
default=default, default_from_env=default_from_env,
3490
from_unicode=config.list_from_store)
3491
self.registry.register(l)
3493
def test_get_default_list_None(self):
3494
self.register_list_option('foo')
3495
self.assertEquals(None, self.conf.get('foo'))
3497
def test_get_default_list_empty(self):
3498
self.register_list_option('foo', '')
3499
self.assertEquals([], self.conf.get('foo'))
3501
def test_get_default_list_from_env(self):
3502
self.register_list_option('foo', default_from_env=['FOO'])
3503
self.overrideEnv('FOO', '')
3504
self.assertEquals([], self.conf.get('foo'))
3506
def test_get_with_list_converter_no_item(self):
3507
self.register_list_option('foo', None)
3508
self.conf.store._load_from_string('foo=,')
3509
self.assertEquals([], self.conf.get('foo'))
3511
def test_get_with_list_converter_many_items(self):
3512
self.register_list_option('foo', None)
3513
self.conf.store._load_from_string('foo=m,o,r,e')
3514
self.assertEquals(['m', 'o', 'r', 'e'], self.conf.get('foo'))
3516
def test_get_with_list_converter_embedded_spaces_many_items(self):
3517
self.register_list_option('foo', None)
3518
self.conf.store._load_from_string('foo=" bar", "baz "')
3519
self.assertEquals([' bar', 'baz '], self.conf.get('foo'))
3521
def test_get_with_list_converter_stripped_spaces_many_items(self):
3522
self.register_list_option('foo', None)
3523
self.conf.store._load_from_string('foo= bar , baz ')
3524
self.assertEquals(['bar', 'baz'], self.conf.get('foo'))
3527
class TestIterOptionRefs(tests.TestCase):
3528
"""iter_option_refs is a bit unusual, document some cases."""
3530
def assertRefs(self, expected, string):
3531
self.assertEquals(expected, list(config.iter_option_refs(string)))
3533
def test_empty(self):
3534
self.assertRefs([(False, '')], '')
3536
def test_no_refs(self):
3537
self.assertRefs([(False, 'foo bar')], 'foo bar')
3539
def test_single_ref(self):
3540
self.assertRefs([(False, ''), (True, '{foo}'), (False, '')], '{foo}')
3542
def test_broken_ref(self):
3543
self.assertRefs([(False, '{foo')], '{foo')
3545
def test_embedded_ref(self):
3546
self.assertRefs([(False, '{'), (True, '{foo}'), (False, '}')],
3549
def test_two_refs(self):
3550
self.assertRefs([(False, ''), (True, '{foo}'),
3551
(False, ''), (True, '{bar}'),
3555
def test_newline_in_refs_are_not_matched(self):
3556
self.assertRefs([(False, '{\nxx}{xx\n}{{\n}}')], '{\nxx}{xx\n}{{\n}}')
3559
class TestStackExpandOptions(tests.TestCaseWithTransport):
3562
super(TestStackExpandOptions, self).setUp()
3563
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3564
self.registry = config.option_registry
3565
self.conf = build_branch_stack(self)
3567
def assertExpansion(self, expected, string, env=None):
3568
self.assertEquals(expected, self.conf.expand_options(string, env))
3570
def test_no_expansion(self):
3571
self.assertExpansion('foo', 'foo')
3573
def test_expand_default_value(self):
3574
self.conf.store._load_from_string('bar=baz')
3575
self.registry.register(config.Option('foo', default=u'{bar}'))
3576
self.assertEquals('baz', self.conf.get('foo', expand=True))
3578
def test_expand_default_from_env(self):
3579
self.conf.store._load_from_string('bar=baz')
3580
self.registry.register(config.Option('foo', default_from_env=['FOO']))
3581
self.overrideEnv('FOO', '{bar}')
3582
self.assertEquals('baz', self.conf.get('foo', expand=True))
3584
def test_expand_default_on_failed_conversion(self):
3585
self.conf.store._load_from_string('baz=bogus\nbar=42\nfoo={baz}')
3586
self.registry.register(
3587
config.Option('foo', default=u'{bar}',
3588
from_unicode=config.int_from_store))
3589
self.assertEquals(42, self.conf.get('foo', expand=True))
3591
def test_env_adding_options(self):
3592
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3594
def test_env_overriding_options(self):
3595
self.conf.store._load_from_string('foo=baz')
3596
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3598
def test_simple_ref(self):
3599
self.conf.store._load_from_string('foo=xxx')
3600
self.assertExpansion('xxx', '{foo}')
3602
def test_unknown_ref(self):
3603
self.assertRaises(errors.ExpandingUnknownOption,
3604
self.conf.expand_options, '{foo}')
3606
def test_indirect_ref(self):
3607
self.conf.store._load_from_string('''
3611
self.assertExpansion('xxx', '{bar}')
3613
def test_embedded_ref(self):
3614
self.conf.store._load_from_string('''
3618
self.assertExpansion('xxx', '{{bar}}')
3620
def test_simple_loop(self):
3621
self.conf.store._load_from_string('foo={foo}')
3622
self.assertRaises(errors.OptionExpansionLoop,
3623
self.conf.expand_options, '{foo}')
3625
def test_indirect_loop(self):
3626
self.conf.store._load_from_string('''
3630
e = self.assertRaises(errors.OptionExpansionLoop,
3631
self.conf.expand_options, '{foo}')
3632
self.assertEquals('foo->bar->baz', e.refs)
3633
self.assertEquals('{foo}', e.string)
3635
def test_list(self):
3636
self.conf.store._load_from_string('''
3640
list={foo},{bar},{baz}
3642
self.registry.register(
3643
config.Option('list', from_unicode=config.list_from_store))
3644
self.assertEquals(['start', 'middle', 'end'],
3645
self.conf.get('list', expand=True))
3647
def test_cascading_list(self):
3648
self.conf.store._load_from_string('''
3654
self.registry.register(
3655
config.Option('list', from_unicode=config.list_from_store))
3656
self.assertEquals(['start', 'middle', 'end'],
3657
self.conf.get('list', expand=True))
3659
def test_pathologically_hidden_list(self):
3660
self.conf.store._load_from_string('''
3666
hidden={start}{middle}{end}
3668
# What matters is what the registration says, the conversion happens
3669
# only after all expansions have been performed
3670
self.registry.register(
3671
config.Option('hidden', from_unicode=config.list_from_store))
3672
self.assertEquals(['bin', 'go'],
3673
self.conf.get('hidden', expand=True))
3676
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
3679
super(TestStackCrossSectionsExpand, self).setUp()
3681
def get_config(self, location, string):
3684
# Since we don't save the config we won't strictly require to inherit
3685
# from TestCaseInTempDir, but an error occurs so quickly...
3686
c = config.LocationStack(location)
3687
c.store._load_from_string(string)
3690
def test_dont_cross_unrelated_section(self):
3691
c = self.get_config('/another/branch/path','''
3696
[/another/branch/path]
3699
self.assertRaises(errors.ExpandingUnknownOption,
3700
c.get, 'bar', expand=True)
3702
def test_cross_related_sections(self):
3703
c = self.get_config('/project/branch/path','''
3707
[/project/branch/path]
3710
self.assertEquals('quux', c.get('bar', expand=True))
3713
class TestStackCrossStoresExpand(tests.TestCaseWithTransport):
3715
def test_cross_global_locations(self):
3716
l_store = config.LocationStore()
3717
l_store._load_from_string('''
3723
g_store = config.GlobalStore()
3724
g_store._load_from_string('''
3730
stack = config.LocationStack('/branch')
3731
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
3732
self.assertEquals('loc-foo', stack.get('gfoo', expand=True))
3735
class TestStackExpandSectionLocals(tests.TestCaseWithTransport):
3737
def test_expand_locals_empty(self):
3738
l_store = config.LocationStore()
3739
l_store._load_from_string('''
3740
[/home/user/project]
3745
stack = config.LocationStack('/home/user/project/')
3746
self.assertEquals('', stack.get('base', expand=True))
3747
self.assertEquals('', stack.get('rel', expand=True))
3749
def test_expand_basename_locally(self):
3750
l_store = config.LocationStore()
3751
l_store._load_from_string('''
3752
[/home/user/project]
3756
stack = config.LocationStack('/home/user/project/branch')
3757
self.assertEquals('branch', stack.get('bfoo', expand=True))
3759
def test_expand_basename_locally_longer_path(self):
3760
l_store = config.LocationStore()
3761
l_store._load_from_string('''
3766
stack = config.LocationStack('/home/user/project/dir/branch')
3767
self.assertEquals('branch', stack.get('bfoo', expand=True))
3769
def test_expand_relpath_locally(self):
3770
l_store = config.LocationStore()
3771
l_store._load_from_string('''
3772
[/home/user/project]
3773
lfoo = loc-foo/{relpath}
3776
stack = config.LocationStack('/home/user/project/branch')
3777
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
3779
def test_expand_relpath_unknonw_in_global(self):
3780
g_store = config.GlobalStore()
3781
g_store._load_from_string('''
3786
stack = config.LocationStack('/home/user/project/branch')
3787
self.assertRaises(errors.ExpandingUnknownOption,
3788
stack.get, 'gfoo', expand=True)
3790
def test_expand_local_option_locally(self):
3791
l_store = config.LocationStore()
3792
l_store._load_from_string('''
3793
[/home/user/project]
3794
lfoo = loc-foo/{relpath}
3798
g_store = config.GlobalStore()
3799
g_store._load_from_string('''
3805
stack = config.LocationStack('/home/user/project/branch')
3806
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
3807
self.assertEquals('loc-foo/branch', stack.get('gfoo', expand=True))
3809
def test_locals_dont_leak(self):
3810
"""Make sure we chose the right local in presence of several sections.
3812
l_store = config.LocationStore()
3813
l_store._load_from_string('''
3815
lfoo = loc-foo/{relpath}
3816
[/home/user/project]
3817
lfoo = loc-foo/{relpath}
3820
stack = config.LocationStack('/home/user/project/branch')
3821
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
3822
stack = config.LocationStack('/home/user/bar/baz')
3823
self.assertEquals('loc-foo/bar/baz', stack.get('lfoo', expand=True))
3827
class TestStackSet(TestStackWithTransport):
3829
def test_simple_set(self):
3830
conf = self.get_stack(self)
3831
self.assertEquals(None, conf.get('foo'))
3832
conf.set('foo', 'baz')
3833
# Did we get it back ?
3834
self.assertEquals('baz', conf.get('foo'))
3836
def test_set_creates_a_new_section(self):
3837
conf = self.get_stack(self)
3838
conf.set('foo', 'baz')
3839
self.assertEquals, 'baz', conf.get('foo')
3841
def test_set_hook(self):
3845
config.ConfigHooks.install_named_hook('set', hook, None)
3846
self.assertLength(0, calls)
3847
conf = self.get_stack(self)
3848
conf.set('foo', 'bar')
3849
self.assertLength(1, calls)
3850
self.assertEquals((conf, 'foo', 'bar'), calls[0])
3853
class TestStackRemove(TestStackWithTransport):
3855
def test_remove_existing(self):
3856
conf = self.get_stack(self)
3857
conf.set('foo', 'bar')
3858
self.assertEquals('bar', conf.get('foo'))
3860
# Did we get it back ?
3861
self.assertEquals(None, conf.get('foo'))
3863
def test_remove_unknown(self):
3864
conf = self.get_stack(self)
3865
self.assertRaises(KeyError, conf.remove, 'I_do_not_exist')
3867
def test_remove_hook(self):
3871
config.ConfigHooks.install_named_hook('remove', hook, None)
3872
self.assertLength(0, calls)
3873
conf = self.get_stack(self)
3874
conf.set('foo', 'bar')
3876
self.assertLength(1, calls)
3877
self.assertEquals((conf, 'foo'), calls[0])
3880
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
3883
super(TestConfigGetOptions, self).setUp()
3884
create_configs(self)
3886
def test_no_variable(self):
3887
# Using branch should query branch, locations and bazaar
3888
self.assertOptions([], self.branch_config)
3890
def test_option_in_bazaar(self):
3891
self.bazaar_config.set_user_option('file', 'bazaar')
3892
self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
3895
def test_option_in_locations(self):
3896
self.locations_config.set_user_option('file', 'locations')
3898
[('file', 'locations', self.tree.basedir, 'locations')],
3899
self.locations_config)
3901
def test_option_in_branch(self):
3902
self.branch_config.set_user_option('file', 'branch')
3903
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
3906
def test_option_in_bazaar_and_branch(self):
3907
self.bazaar_config.set_user_option('file', 'bazaar')
3908
self.branch_config.set_user_option('file', 'branch')
3909
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
3910
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3913
def test_option_in_branch_and_locations(self):
3914
# Hmm, locations override branch :-/
3915
self.locations_config.set_user_option('file', 'locations')
3916
self.branch_config.set_user_option('file', 'branch')
3918
[('file', 'locations', self.tree.basedir, 'locations'),
3919
('file', 'branch', 'DEFAULT', 'branch'),],
3922
def test_option_in_bazaar_locations_and_branch(self):
3923
self.bazaar_config.set_user_option('file', 'bazaar')
3924
self.locations_config.set_user_option('file', 'locations')
3925
self.branch_config.set_user_option('file', 'branch')
3927
[('file', 'locations', self.tree.basedir, 'locations'),
3928
('file', 'branch', 'DEFAULT', 'branch'),
3929
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3933
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
3936
super(TestConfigRemoveOption, self).setUp()
3937
create_configs_with_file_option(self)
3939
def test_remove_in_locations(self):
3940
self.locations_config.remove_user_option('file', self.tree.basedir)
3942
[('file', 'branch', 'DEFAULT', 'branch'),
3943
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3946
def test_remove_in_branch(self):
3947
self.branch_config.remove_user_option('file')
3949
[('file', 'locations', self.tree.basedir, 'locations'),
3950
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3953
def test_remove_in_bazaar(self):
3954
self.bazaar_config.remove_user_option('file')
3956
[('file', 'locations', self.tree.basedir, 'locations'),
3957
('file', 'branch', 'DEFAULT', 'branch'),],
3961
class TestConfigGetSections(tests.TestCaseWithTransport):
3964
super(TestConfigGetSections, self).setUp()
3965
create_configs(self)
3967
def assertSectionNames(self, expected, conf, name=None):
3968
"""Check which sections are returned for a given config.
3970
If fallback configurations exist their sections can be included.
3972
:param expected: A list of section names.
3974
:param conf: The configuration that will be queried.
3976
:param name: An optional section name that will be passed to
3979
sections = list(conf._get_sections(name))
3980
self.assertLength(len(expected), sections)
3981
self.assertEqual(expected, [name for name, _, _ in sections])
3983
def test_bazaar_default_section(self):
3984
self.assertSectionNames(['DEFAULT'], self.bazaar_config)
3986
def test_locations_default_section(self):
3987
# No sections are defined in an empty file
3988
self.assertSectionNames([], self.locations_config)
3990
def test_locations_named_section(self):
3991
self.locations_config.set_user_option('file', 'locations')
3992
self.assertSectionNames([self.tree.basedir], self.locations_config)
3994
def test_locations_matching_sections(self):
3995
loc_config = self.locations_config
3996
loc_config.set_user_option('file', 'locations')
3997
# We need to cheat a bit here to create an option in sections above and
3998
# below the 'location' one.
3999
parser = loc_config._get_parser()
4000
# locations.cong deals with '/' ignoring native os.sep
4001
location_names = self.tree.basedir.split('/')
4002
parent = '/'.join(location_names[:-1])
4003
child = '/'.join(location_names + ['child'])
4005
parser[parent]['file'] = 'parent'
4007
parser[child]['file'] = 'child'
4008
self.assertSectionNames([self.tree.basedir, parent], loc_config)
4010
def test_branch_data_default_section(self):
4011
self.assertSectionNames([None],
4012
self.branch_config._get_branch_data_config())
4014
def test_branch_default_sections(self):
4015
# No sections are defined in an empty locations file
4016
self.assertSectionNames([None, 'DEFAULT'],
4018
# Unless we define an option
4019
self.branch_config._get_location_config().set_user_option(
4020
'file', 'locations')
4021
self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
4024
def test_bazaar_named_section(self):
4025
# We need to cheat as the API doesn't give direct access to sections
4026
# other than DEFAULT.
4027
self.bazaar_config.set_alias('bazaar', 'bzr')
4028
self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
4031
class TestAuthenticationConfigFile(tests.TestCase):
4032
"""Test the authentication.conf file matching"""
4034
def _got_user_passwd(self, expected_user, expected_password,
4035
config, *args, **kwargs):
4036
credentials = config.get_credentials(*args, **kwargs)
4037
if credentials is None:
4041
user = credentials['user']
4042
password = credentials['password']
4043
self.assertEquals(expected_user, user)
4044
self.assertEquals(expected_password, password)
4046
def test_empty_config(self):
4047
conf = config.AuthenticationConfig(_file=StringIO())
4048
self.assertEquals({}, conf._get_config())
4049
self._got_user_passwd(None, None, conf, 'http', 'foo.net')
4051
def test_non_utf8_config(self):
4052
conf = config.AuthenticationConfig(_file=StringIO(
4054
self.assertRaises(errors.ConfigContentError, conf._get_config)
4056
def test_missing_auth_section_header(self):
4057
conf = config.AuthenticationConfig(_file=StringIO('foo = bar'))
4058
self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
4060
def test_auth_section_header_not_closed(self):
4061
conf = config.AuthenticationConfig(_file=StringIO('[DEF'))
4062
self.assertRaises(errors.ParseConfigError, conf._get_config)
4064
def test_auth_value_not_boolean(self):
4065
conf = config.AuthenticationConfig(_file=StringIO(
4069
verify_certificates=askme # Error: Not a boolean
4071
self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
4073
def test_auth_value_not_int(self):
4074
conf = config.AuthenticationConfig(_file=StringIO(
4078
port=port # Error: Not an int
4080
self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
4082
def test_unknown_password_encoding(self):
4083
conf = config.AuthenticationConfig(_file=StringIO(
4087
password_encoding=unknown
4089
self.assertRaises(ValueError, conf.get_password,
4090
'ftp', 'foo.net', 'joe')
4092
def test_credentials_for_scheme_host(self):
4093
conf = config.AuthenticationConfig(_file=StringIO(
4094
"""# Identity on foo.net
4099
password=secret-pass
4102
self._got_user_passwd('joe', 'secret-pass', conf, 'ftp', 'foo.net')
4104
self._got_user_passwd(None, None, conf, 'http', 'foo.net')
4106
self._got_user_passwd(None, None, conf, 'ftp', 'bar.net')
4108
def test_credentials_for_host_port(self):
4109
conf = config.AuthenticationConfig(_file=StringIO(
4110
"""# Identity on foo.net
4116
password=secret-pass
4119
self._got_user_passwd('joe', 'secret-pass',
4120
conf, 'ftp', 'foo.net', port=10021)
4122
self._got_user_passwd(None, None, conf, 'ftp', 'foo.net')
4124
def test_for_matching_host(self):
4125
conf = config.AuthenticationConfig(_file=StringIO(
4126
"""# Identity on foo.net
4132
[sourceforge domain]
4139
self._got_user_passwd('georges', 'bendover',
4140
conf, 'bzr', 'foo.bzr.sf.net')
4142
self._got_user_passwd(None, None,
4143
conf, 'bzr', 'bbzr.sf.net')
4145
def test_for_matching_host_None(self):
4146
conf = config.AuthenticationConfig(_file=StringIO(
4147
"""# Identity on foo.net
4157
self._got_user_passwd('joe', 'joepass',
4158
conf, 'bzr', 'quux.net')
4159
# no host but different scheme
4160
self._got_user_passwd('georges', 'bendover',
4161
conf, 'ftp', 'quux.net')
4163
def test_credentials_for_path(self):
4164
conf = config.AuthenticationConfig(_file=StringIO(
4180
self._got_user_passwd(None, None,
4181
conf, 'http', host='bar.org', path='/dir3')
4183
self._got_user_passwd('georges', 'bendover',
4184
conf, 'http', host='bar.org', path='/dir2')
4186
self._got_user_passwd('jim', 'jimpass',
4187
conf, 'http', host='bar.org',path='/dir1/subdir')
4189
def test_credentials_for_user(self):
4190
conf = config.AuthenticationConfig(_file=StringIO(
4199
self._got_user_passwd('jim', 'jimpass',
4200
conf, 'http', 'bar.org')
4202
self._got_user_passwd('jim', 'jimpass',
4203
conf, 'http', 'bar.org', user='jim')
4204
# Don't get a different user if one is specified
4205
self._got_user_passwd(None, None,
4206
conf, 'http', 'bar.org', user='georges')
4208
def test_credentials_for_user_without_password(self):
4209
conf = config.AuthenticationConfig(_file=StringIO(
4216
# Get user but no password
4217
self._got_user_passwd('jim', None,
4218
conf, 'http', 'bar.org')
4220
def test_verify_certificates(self):
4221
conf = config.AuthenticationConfig(_file=StringIO(
4228
verify_certificates=False
4235
credentials = conf.get_credentials('https', 'bar.org')
4236
self.assertEquals(False, credentials.get('verify_certificates'))
4237
credentials = conf.get_credentials('https', 'foo.net')
4238
self.assertEquals(True, credentials.get('verify_certificates'))
4241
class TestAuthenticationStorage(tests.TestCaseInTempDir):
4243
def test_set_credentials(self):
4244
conf = config.AuthenticationConfig()
4245
conf.set_credentials('name', 'host', 'user', 'scheme', 'password',
4246
99, path='/foo', verify_certificates=False, realm='realm')
4247
credentials = conf.get_credentials(host='host', scheme='scheme',
4248
port=99, path='/foo',
4250
CREDENTIALS = {'name': 'name', 'user': 'user', 'password': 'password',
4251
'verify_certificates': False, 'scheme': 'scheme',
4252
'host': 'host', 'port': 99, 'path': '/foo',
4254
self.assertEqual(CREDENTIALS, credentials)
4255
credentials_from_disk = config.AuthenticationConfig().get_credentials(
4256
host='host', scheme='scheme', port=99, path='/foo', realm='realm')
4257
self.assertEqual(CREDENTIALS, credentials_from_disk)
4259
def test_reset_credentials_different_name(self):
4260
conf = config.AuthenticationConfig()
4261
conf.set_credentials('name', 'host', 'user', 'scheme', 'password'),
4262
conf.set_credentials('name2', 'host', 'user2', 'scheme', 'password'),
4263
self.assertIs(None, conf._get_config().get('name'))
4264
credentials = conf.get_credentials(host='host', scheme='scheme')
4265
CREDENTIALS = {'name': 'name2', 'user': 'user2', 'password':
4266
'password', 'verify_certificates': True,
4267
'scheme': 'scheme', 'host': 'host', 'port': None,
4268
'path': None, 'realm': None}
4269
self.assertEqual(CREDENTIALS, credentials)
4272
class TestAuthenticationConfig(tests.TestCase):
4273
"""Test AuthenticationConfig behaviour"""
4275
def _check_default_password_prompt(self, expected_prompt_format, scheme,
4276
host=None, port=None, realm=None,
4280
user, password = 'jim', 'precious'
4281
expected_prompt = expected_prompt_format % {
4282
'scheme': scheme, 'host': host, 'port': port,
4283
'user': user, 'realm': realm}
4285
stdout = tests.StringIOWrapper()
4286
stderr = tests.StringIOWrapper()
4287
ui.ui_factory = tests.TestUIFactory(stdin=password + '\n',
4288
stdout=stdout, stderr=stderr)
4289
# We use an empty conf so that the user is always prompted
4290
conf = config.AuthenticationConfig()
4291
self.assertEquals(password,
4292
conf.get_password(scheme, host, user, port=port,
4293
realm=realm, path=path))
4294
self.assertEquals(expected_prompt, stderr.getvalue())
4295
self.assertEquals('', stdout.getvalue())
4297
def _check_default_username_prompt(self, expected_prompt_format, scheme,
4298
host=None, port=None, realm=None,
4303
expected_prompt = expected_prompt_format % {
4304
'scheme': scheme, 'host': host, 'port': port,
4306
stdout = tests.StringIOWrapper()
4307
stderr = tests.StringIOWrapper()
4308
ui.ui_factory = tests.TestUIFactory(stdin=username+ '\n',
4309
stdout=stdout, stderr=stderr)
4310
# We use an empty conf so that the user is always prompted
4311
conf = config.AuthenticationConfig()
4312
self.assertEquals(username, conf.get_user(scheme, host, port=port,
4313
realm=realm, path=path, ask=True))
4314
self.assertEquals(expected_prompt, stderr.getvalue())
4315
self.assertEquals('', stdout.getvalue())
4317
def test_username_defaults_prompts(self):
4318
# HTTP prompts can't be tested here, see test_http.py
4319
self._check_default_username_prompt(u'FTP %(host)s username: ', 'ftp')
4320
self._check_default_username_prompt(
4321
u'FTP %(host)s:%(port)d username: ', 'ftp', port=10020)
4322
self._check_default_username_prompt(
4323
u'SSH %(host)s:%(port)d username: ', 'ssh', port=12345)
4325
def test_username_default_no_prompt(self):
4326
conf = config.AuthenticationConfig()
4327
self.assertEquals(None,
4328
conf.get_user('ftp', 'example.com'))
4329
self.assertEquals("explicitdefault",
4330
conf.get_user('ftp', 'example.com', default="explicitdefault"))
4332
def test_password_default_prompts(self):
4333
# HTTP prompts can't be tested here, see test_http.py
4334
self._check_default_password_prompt(
4335
u'FTP %(user)s@%(host)s password: ', 'ftp')
4336
self._check_default_password_prompt(
4337
u'FTP %(user)s@%(host)s:%(port)d password: ', 'ftp', port=10020)
4338
self._check_default_password_prompt(
4339
u'SSH %(user)s@%(host)s:%(port)d password: ', 'ssh', port=12345)
4340
# SMTP port handling is a bit special (it's handled if embedded in the
4342
# FIXME: should we: forbid that, extend it to other schemes, leave
4343
# things as they are that's fine thank you ?
4344
self._check_default_password_prompt(
4345
u'SMTP %(user)s@%(host)s password: ', 'smtp')
4346
self._check_default_password_prompt(
4347
u'SMTP %(user)s@%(host)s password: ', 'smtp', host='bar.org:10025')
4348
self._check_default_password_prompt(
4349
u'SMTP %(user)s@%(host)s:%(port)d password: ', 'smtp', port=10025)
4351
def test_ssh_password_emits_warning(self):
4352
conf = config.AuthenticationConfig(_file=StringIO(
4360
entered_password = 'typed-by-hand'
4361
stdout = tests.StringIOWrapper()
4362
stderr = tests.StringIOWrapper()
4363
ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
4364
stdout=stdout, stderr=stderr)
4366
# Since the password defined in the authentication config is ignored,
4367
# the user is prompted
4368
self.assertEquals(entered_password,
4369
conf.get_password('ssh', 'bar.org', user='jim'))
4370
self.assertContainsRe(
4372
'password ignored in section \[ssh with password\]')
4374
def test_ssh_without_password_doesnt_emit_warning(self):
4375
conf = config.AuthenticationConfig(_file=StringIO(
4382
entered_password = 'typed-by-hand'
4383
stdout = tests.StringIOWrapper()
4384
stderr = tests.StringIOWrapper()
4385
ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
4389
# Since the password defined in the authentication config is ignored,
4390
# the user is prompted
4391
self.assertEquals(entered_password,
4392
conf.get_password('ssh', 'bar.org', user='jim'))
4393
# No warning shoud be emitted since there is no password. We are only
4395
self.assertNotContainsRe(
4397
'password ignored in section \[ssh with password\]')
4399
def test_uses_fallback_stores(self):
4400
self.overrideAttr(config, 'credential_store_registry',
4401
config.CredentialStoreRegistry())
4402
store = StubCredentialStore()
4403
store.add_credentials("http", "example.com", "joe", "secret")
4404
config.credential_store_registry.register("stub", store, fallback=True)
4405
conf = config.AuthenticationConfig(_file=StringIO())
4406
creds = conf.get_credentials("http", "example.com")
4407
self.assertEquals("joe", creds["user"])
4408
self.assertEquals("secret", creds["password"])
4411
class StubCredentialStore(config.CredentialStore):
4417
def add_credentials(self, scheme, host, user, password=None):
4418
self._username[(scheme, host)] = user
4419
self._password[(scheme, host)] = password
4421
def get_credentials(self, scheme, host, port=None, user=None,
4422
path=None, realm=None):
4423
key = (scheme, host)
4424
if not key in self._username:
4426
return { "scheme": scheme, "host": host, "port": port,
4427
"user": self._username[key], "password": self._password[key]}
4430
class CountingCredentialStore(config.CredentialStore):
4435
def get_credentials(self, scheme, host, port=None, user=None,
4436
path=None, realm=None):
4441
class TestCredentialStoreRegistry(tests.TestCase):
4443
def _get_cs_registry(self):
4444
return config.credential_store_registry
4446
def test_default_credential_store(self):
4447
r = self._get_cs_registry()
4448
default = r.get_credential_store(None)
4449
self.assertIsInstance(default, config.PlainTextCredentialStore)
4451
def test_unknown_credential_store(self):
4452
r = self._get_cs_registry()
4453
# It's hard to imagine someone creating a credential store named
4454
# 'unknown' so we use that as an never registered key.
4455
self.assertRaises(KeyError, r.get_credential_store, 'unknown')
4457
def test_fallback_none_registered(self):
4458
r = config.CredentialStoreRegistry()
4459
self.assertEquals(None,
4460
r.get_fallback_credentials("http", "example.com"))
4462
def test_register(self):
4463
r = config.CredentialStoreRegistry()
4464
r.register("stub", StubCredentialStore(), fallback=False)
4465
r.register("another", StubCredentialStore(), fallback=True)
4466
self.assertEquals(["another", "stub"], r.keys())
4468
def test_register_lazy(self):
4469
r = config.CredentialStoreRegistry()
4470
r.register_lazy("stub", "bzrlib.tests.test_config",
4471
"StubCredentialStore", fallback=False)
4472
self.assertEquals(["stub"], r.keys())
4473
self.assertIsInstance(r.get_credential_store("stub"),
4474
StubCredentialStore)
4476
def test_is_fallback(self):
4477
r = config.CredentialStoreRegistry()
4478
r.register("stub1", None, fallback=False)
4479
r.register("stub2", None, fallback=True)
4480
self.assertEquals(False, r.is_fallback("stub1"))
4481
self.assertEquals(True, r.is_fallback("stub2"))
4483
def test_no_fallback(self):
4484
r = config.CredentialStoreRegistry()
4485
store = CountingCredentialStore()
4486
r.register("count", store, fallback=False)
4487
self.assertEquals(None,
4488
r.get_fallback_credentials("http", "example.com"))
4489
self.assertEquals(0, store._calls)
4491
def test_fallback_credentials(self):
4492
r = config.CredentialStoreRegistry()
4493
store = StubCredentialStore()
4494
store.add_credentials("http", "example.com",
4495
"somebody", "geheim")
4496
r.register("stub", store, fallback=True)
4497
creds = r.get_fallback_credentials("http", "example.com")
4498
self.assertEquals("somebody", creds["user"])
4499
self.assertEquals("geheim", creds["password"])
4501
def test_fallback_first_wins(self):
4502
r = config.CredentialStoreRegistry()
4503
stub1 = StubCredentialStore()
4504
stub1.add_credentials("http", "example.com",
4505
"somebody", "stub1")
4506
r.register("stub1", stub1, fallback=True)
4507
stub2 = StubCredentialStore()
4508
stub2.add_credentials("http", "example.com",
4509
"somebody", "stub2")
4510
r.register("stub2", stub1, fallback=True)
4511
creds = r.get_fallback_credentials("http", "example.com")
4512
self.assertEquals("somebody", creds["user"])
4513
self.assertEquals("stub1", creds["password"])
4516
class TestPlainTextCredentialStore(tests.TestCase):
4518
def test_decode_password(self):
4519
r = config.credential_store_registry
4520
plain_text = r.get_credential_store()
4521
decoded = plain_text.decode_password(dict(password='secret'))
4522
self.assertEquals('secret', decoded)
4525
# FIXME: Once we have a way to declare authentication to all test servers, we
4526
# can implement generic tests.
4527
# test_user_password_in_url
4528
# test_user_in_url_password_from_config
4529
# test_user_in_url_password_prompted
4530
# test_user_in_config
4531
# test_user_getpass.getuser
4532
# test_user_prompted ?
4533
class TestAuthenticationRing(tests.TestCaseWithTransport):
4537
class TestAutoUserId(tests.TestCase):
4538
"""Test inferring an automatic user name."""
4540
def test_auto_user_id(self):
4541
"""Automatic inference of user name.
4543
This is a bit hard to test in an isolated way, because it depends on
4544
system functions that go direct to /etc or perhaps somewhere else.
4545
But it's reasonable to say that on Unix, with an /etc/mailname, we ought
4546
to be able to choose a user name with no configuration.
4548
if sys.platform == 'win32':
4549
raise tests.TestSkipped(
4550
"User name inference not implemented on win32")
4551
realname, address = config._auto_user_id()
4552
if os.path.exists('/etc/mailname'):
4553
self.assertIsNot(None, realname)
4554
self.assertIsNot(None, address)
4556
self.assertEquals((None, None), (realname, address))
4559
class EmailOptionTests(tests.TestCase):
4561
def test_default_email_uses_BZR_EMAIL(self):
4562
# BZR_EMAIL takes precedence over EMAIL
4563
self.overrideEnv('BZR_EMAIL', 'jelmer@samba.org')
4564
self.overrideEnv('EMAIL', 'jelmer@apache.org')
4565
self.assertEquals('jelmer@samba.org', config.default_email())
4567
def test_default_email_uses_EMAIL(self):
4568
self.overrideEnv('BZR_EMAIL', None)
4569
self.overrideEnv('EMAIL', 'jelmer@apache.org')
4570
self.assertEquals('jelmer@apache.org', config.default_email())
4572
def test_BZR_EMAIL_overrides(self):
4573
self.overrideEnv('BZR_EMAIL', 'jelmer@apache.org')
4574
self.assertEquals('jelmer@apache.org',
4575
config.email_from_store('jelmer@debian.org'))
4576
self.overrideEnv('BZR_EMAIL', None)
4577
self.overrideEnv('EMAIL', 'jelmer@samba.org')
4578
self.assertEquals('jelmer@debian.org',
4579
config.email_from_store('jelmer@debian.org'))