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
43
from bzrlib.tests import (
47
from bzrlib.util.configobj import configobj
50
def lockable_config_scenarios():
53
{'config_class': config.GlobalConfig,
55
'config_section': 'DEFAULT'}),
57
{'config_class': config.LocationConfig,
59
'config_section': '.'}),]
62
load_tests = scenarios.load_tests_apply_scenarios
65
sample_long_alias="log -r-15..-1 --line"
66
sample_config_text = u"""
68
email=Erik B\u00e5gfors <erik@bagfors.nu>
70
change_editor=vimdiff -of @new_path @old_path
71
gpg_signing_command=gnome-gpg
73
user_global_option=something
74
bzr.mergetool.sometool=sometool {base} {this} {other} -o {result}
75
bzr.mergetool.funkytool=funkytool "arg with spaces" {this_temp}
76
bzr.default_mergetool=sometool
79
ll=""" + sample_long_alias + "\n"
82
sample_always_signatures = """
84
check_signatures=ignore
85
create_signatures=always
88
sample_ignore_signatures = """
90
check_signatures=require
91
create_signatures=never
94
sample_maybe_signatures = """
96
check_signatures=ignore
97
create_signatures=when-required
100
sample_branches_text = """
101
[http://www.example.com]
103
email=Robert Collins <robertc@example.org>
104
normal_option = normal
105
appendpath_option = append
106
appendpath_option:policy = appendpath
107
norecurse_option = norecurse
108
norecurse_option:policy = norecurse
109
[http://www.example.com/ignoreparent]
110
# different project: ignore parent dir config
112
[http://www.example.com/norecurse]
113
# configuration items that only apply to this dir
115
normal_option = norecurse
116
[http://www.example.com/dir]
117
appendpath_option = normal
119
check_signatures=require
120
# test trailing / matching with no children
122
check_signatures=check-available
123
gpg_signing_command=false
124
user_local_option=local
125
# test trailing / matching
127
#subdirs will match but not the parent
129
check_signatures=ignore
130
post_commit=bzrlib.tests.test_config.post_commit
131
#testing explicit beats globs
135
def create_configs(test):
136
"""Create configuration files for a given test.
138
This requires creating a tree (and populate the ``test.tree`` attribute)
139
and its associated branch and will populate the following attributes:
141
- branch_config: A BranchConfig for the associated branch.
143
- locations_config : A LocationConfig for the associated branch
145
- bazaar_config: A GlobalConfig.
147
The tree and branch are created in a 'tree' subdirectory so the tests can
148
still use the test directory to stay outside of the branch.
150
tree = test.make_branch_and_tree('tree')
152
test.branch_config = config.BranchConfig(tree.branch)
153
test.locations_config = config.LocationConfig(tree.basedir)
154
test.bazaar_config = config.GlobalConfig()
157
def create_configs_with_file_option(test):
158
"""Create configuration files with a ``file`` option set in each.
160
This builds on ``create_configs`` and add one ``file`` option in each
161
configuration with a value which allows identifying the configuration file.
164
test.bazaar_config.set_user_option('file', 'bazaar')
165
test.locations_config.set_user_option('file', 'locations')
166
test.branch_config.set_user_option('file', 'branch')
169
class TestOptionsMixin:
171
def assertOptions(self, expected, conf):
172
# We don't care about the parser (as it will make tests hard to write
173
# and error-prone anyway)
174
self.assertThat([opt[:4] for opt in conf._get_options()],
175
matchers.Equals(expected))
178
class InstrumentedConfigObj(object):
179
"""A config obj look-enough-alike to record calls made to it."""
181
def __contains__(self, thing):
182
self._calls.append(('__contains__', thing))
185
def __getitem__(self, key):
186
self._calls.append(('__getitem__', key))
189
def __init__(self, input, encoding=None):
190
self._calls = [('__init__', input, encoding)]
192
def __setitem__(self, key, value):
193
self._calls.append(('__setitem__', key, value))
195
def __delitem__(self, key):
196
self._calls.append(('__delitem__', key))
199
self._calls.append(('keys',))
203
self._calls.append(('reload',))
205
def write(self, arg):
206
self._calls.append(('write',))
208
def as_bool(self, value):
209
self._calls.append(('as_bool', value))
212
def get_value(self, section, name):
213
self._calls.append(('get_value', section, name))
217
class FakeBranch(object):
219
def __init__(self, base=None, user_id=None):
221
self.base = "http://example.com/branches/demo"
224
self._transport = self.control_files = \
225
FakeControlFilesAndTransport(user_id=user_id)
227
def _get_config(self):
228
return config.TransportConfig(self._transport, 'branch.conf')
230
def lock_write(self):
237
class FakeControlFilesAndTransport(object):
239
def __init__(self, user_id=None):
242
self.files['email'] = user_id
243
self._transport = self
245
def get_utf8(self, filename):
247
raise AssertionError("get_utf8 should no longer be used")
249
def get(self, filename):
252
return StringIO(self.files[filename])
254
raise errors.NoSuchFile(filename)
256
def get_bytes(self, filename):
259
return self.files[filename]
261
raise errors.NoSuchFile(filename)
263
def put(self, filename, fileobj):
264
self.files[filename] = fileobj.read()
266
def put_file(self, filename, fileobj):
267
return self.put(filename, fileobj)
270
class InstrumentedConfig(config.Config):
271
"""An instrumented config that supplies stubs for template methods."""
274
super(InstrumentedConfig, self).__init__()
276
self._signatures = config.CHECK_NEVER
278
def _get_user_id(self):
279
self._calls.append('_get_user_id')
280
return "Robert Collins <robert.collins@example.org>"
282
def _get_signature_checking(self):
283
self._calls.append('_get_signature_checking')
284
return self._signatures
286
def _get_change_editor(self):
287
self._calls.append('_get_change_editor')
288
return 'vimdiff -fo @new_path @old_path'
291
bool_config = """[DEFAULT]
300
class TestConfigObj(tests.TestCase):
302
def test_get_bool(self):
303
co = config.ConfigObj(StringIO(bool_config))
304
self.assertIs(co.get_bool('DEFAULT', 'active'), True)
305
self.assertIs(co.get_bool('DEFAULT', 'inactive'), False)
306
self.assertIs(co.get_bool('UPPERCASE', 'active'), True)
307
self.assertIs(co.get_bool('UPPERCASE', 'nonactive'), False)
309
def test_hash_sign_in_value(self):
311
Before 4.5.0, ConfigObj did not quote # signs in values, so they'd be
312
treated as comments when read in again. (#86838)
314
co = config.ConfigObj()
315
co['test'] = 'foo#bar'
317
co.write(outfile=outfile)
318
lines = outfile.getvalue().splitlines()
319
self.assertEqual(lines, ['test = "foo#bar"'])
320
co2 = config.ConfigObj(lines)
321
self.assertEqual(co2['test'], 'foo#bar')
323
def test_triple_quotes(self):
324
# Bug #710410: if the value string has triple quotes
325
# then ConfigObj versions up to 4.7.2 will quote them wrong
326
# and won't able to read them back
327
triple_quotes_value = '''spam
328
""" that's my spam """
330
co = config.ConfigObj()
331
co['test'] = triple_quotes_value
332
# While writing this test another bug in ConfigObj has been found:
333
# method co.write() without arguments produces list of lines
334
# one option per line, and multiline values are not split
335
# across multiple lines,
336
# and that breaks the parsing these lines back by ConfigObj.
337
# This issue only affects test, but it's better to avoid
338
# `co.write()` construct at all.
339
# [bialix 20110222] bug report sent to ConfigObj's author
341
co.write(outfile=outfile)
342
output = outfile.getvalue()
343
# now we're trying to read it back
344
co2 = config.ConfigObj(StringIO(output))
345
self.assertEquals(triple_quotes_value, co2['test'])
348
erroneous_config = """[section] # line 1
351
whocares=notme # line 4
355
class TestConfigObjErrors(tests.TestCase):
357
def test_duplicate_section_name_error_line(self):
359
co = configobj.ConfigObj(StringIO(erroneous_config),
361
except config.configobj.DuplicateError, e:
362
self.assertEqual(3, e.line_number)
364
self.fail('Error in config file not detected')
367
class TestConfig(tests.TestCase):
369
def test_constructs(self):
372
def test_no_default_editor(self):
373
self.assertRaises(NotImplementedError, config.Config().get_editor)
375
def test_user_email(self):
376
my_config = InstrumentedConfig()
377
self.assertEqual('robert.collins@example.org', my_config.user_email())
378
self.assertEqual(['_get_user_id'], my_config._calls)
380
def test_username(self):
381
my_config = InstrumentedConfig()
382
self.assertEqual('Robert Collins <robert.collins@example.org>',
383
my_config.username())
384
self.assertEqual(['_get_user_id'], my_config._calls)
386
def test_signatures_default(self):
387
my_config = config.Config()
388
self.assertFalse(my_config.signature_needed())
389
self.assertEqual(config.CHECK_IF_POSSIBLE,
390
my_config.signature_checking())
391
self.assertEqual(config.SIGN_WHEN_REQUIRED,
392
my_config.signing_policy())
394
def test_signatures_template_method(self):
395
my_config = InstrumentedConfig()
396
self.assertEqual(config.CHECK_NEVER, my_config.signature_checking())
397
self.assertEqual(['_get_signature_checking'], my_config._calls)
399
def test_signatures_template_method_none(self):
400
my_config = InstrumentedConfig()
401
my_config._signatures = None
402
self.assertEqual(config.CHECK_IF_POSSIBLE,
403
my_config.signature_checking())
404
self.assertEqual(['_get_signature_checking'], my_config._calls)
406
def test_gpg_signing_command_default(self):
407
my_config = config.Config()
408
self.assertEqual('gpg', my_config.gpg_signing_command())
410
def test_get_user_option_default(self):
411
my_config = config.Config()
412
self.assertEqual(None, my_config.get_user_option('no_option'))
414
def test_post_commit_default(self):
415
my_config = config.Config()
416
self.assertEqual(None, my_config.post_commit())
418
def test_log_format_default(self):
419
my_config = config.Config()
420
self.assertEqual('long', my_config.log_format())
422
def test_get_change_editor(self):
423
my_config = InstrumentedConfig()
424
change_editor = my_config.get_change_editor('old_tree', 'new_tree')
425
self.assertEqual(['_get_change_editor'], my_config._calls)
426
self.assertIs(diff.DiffFromTool, change_editor.__class__)
427
self.assertEqual(['vimdiff', '-fo', '@new_path', '@old_path'],
428
change_editor.command_template)
431
class TestConfigPath(tests.TestCase):
434
super(TestConfigPath, self).setUp()
435
self.overrideEnv('HOME', '/home/bogus')
436
self.overrideEnv('XDG_CACHE_DIR', '')
437
if sys.platform == 'win32':
439
'BZR_HOME', r'C:\Documents and Settings\bogus\Application Data')
441
'C:/Documents and Settings/bogus/Application Data/bazaar/2.0'
443
self.bzr_home = '/home/bogus/.bazaar'
445
def test_config_dir(self):
446
self.assertEqual(config.config_dir(), self.bzr_home)
448
def test_config_filename(self):
449
self.assertEqual(config.config_filename(),
450
self.bzr_home + '/bazaar.conf')
452
def test_locations_config_filename(self):
453
self.assertEqual(config.locations_config_filename(),
454
self.bzr_home + '/locations.conf')
456
def test_authentication_config_filename(self):
457
self.assertEqual(config.authentication_config_filename(),
458
self.bzr_home + '/authentication.conf')
460
def test_xdg_cache_dir(self):
461
self.assertEqual(config.xdg_cache_dir(),
462
'/home/bogus/.cache')
465
class TestXDGConfigDir(tests.TestCaseInTempDir):
466
# must be in temp dir because config tests for the existence of the bazaar
467
# subdirectory of $XDG_CONFIG_HOME
470
if sys.platform in ('darwin', 'win32'):
471
raise tests.TestNotApplicable(
472
'XDG config dir not used on this platform')
473
super(TestXDGConfigDir, self).setUp()
474
self.overrideEnv('HOME', self.test_home_dir)
475
# BZR_HOME overrides everything we want to test so unset it.
476
self.overrideEnv('BZR_HOME', None)
478
def test_xdg_config_dir_exists(self):
479
"""When ~/.config/bazaar exists, use it as the config dir."""
480
newdir = osutils.pathjoin(self.test_home_dir, '.config', 'bazaar')
482
self.assertEqual(config.config_dir(), newdir)
484
def test_xdg_config_home(self):
485
"""When XDG_CONFIG_HOME is set, use it."""
486
xdgconfigdir = osutils.pathjoin(self.test_home_dir, 'xdgconfig')
487
self.overrideEnv('XDG_CONFIG_HOME', xdgconfigdir)
488
newdir = osutils.pathjoin(xdgconfigdir, 'bazaar')
490
self.assertEqual(config.config_dir(), newdir)
493
class TestIniConfig(tests.TestCaseInTempDir):
495
def make_config_parser(self, s):
496
conf = config.IniBasedConfig.from_string(s)
497
return conf, conf._get_parser()
500
class TestIniConfigBuilding(TestIniConfig):
502
def test_contructs(self):
503
my_config = config.IniBasedConfig()
505
def test_from_fp(self):
506
my_config = config.IniBasedConfig.from_string(sample_config_text)
507
self.assertIsInstance(my_config._get_parser(), configobj.ConfigObj)
509
def test_cached(self):
510
my_config = config.IniBasedConfig.from_string(sample_config_text)
511
parser = my_config._get_parser()
512
self.failUnless(my_config._get_parser() is parser)
514
def _dummy_chown(self, path, uid, gid):
515
self.path, self.uid, self.gid = path, uid, gid
517
def test_ini_config_ownership(self):
518
"""Ensure that chown is happening during _write_config_file"""
519
self.requireFeature(features.chown_feature)
520
self.overrideAttr(os, 'chown', self._dummy_chown)
521
self.path = self.uid = self.gid = None
522
conf = config.IniBasedConfig(file_name='./foo.conf')
523
conf._write_config_file()
524
self.assertEquals(self.path, './foo.conf')
525
self.assertTrue(isinstance(self.uid, int))
526
self.assertTrue(isinstance(self.gid, int))
528
def test_get_filename_parameter_is_deprecated_(self):
529
conf = self.callDeprecated([
530
'IniBasedConfig.__init__(get_filename) was deprecated in 2.3.'
531
' Use file_name instead.'],
532
config.IniBasedConfig, lambda: 'ini.conf')
533
self.assertEqual('ini.conf', conf.file_name)
535
def test_get_parser_file_parameter_is_deprecated_(self):
536
config_file = StringIO(sample_config_text.encode('utf-8'))
537
conf = config.IniBasedConfig.from_string(sample_config_text)
538
conf = self.callDeprecated([
539
'IniBasedConfig._get_parser(file=xxx) was deprecated in 2.3.'
540
' Use IniBasedConfig(_content=xxx) instead.'],
541
conf._get_parser, file=config_file)
544
class TestIniConfigSaving(tests.TestCaseInTempDir):
546
def test_cant_save_without_a_file_name(self):
547
conf = config.IniBasedConfig()
548
self.assertRaises(AssertionError, conf._write_config_file)
550
def test_saved_with_content(self):
551
content = 'foo = bar\n'
552
conf = config.IniBasedConfig.from_string(
553
content, file_name='./test.conf', save=True)
554
self.assertFileEqual(content, 'test.conf')
557
class TestIniConfigOptionExpansionDefaultValue(tests.TestCaseInTempDir):
558
"""What is the default value of expand for config options.
560
This is an opt-in beta feature used to evaluate whether or not option
561
references can appear in dangerous place raising exceptions, disapearing
562
(and as such corrupting data) or if it's safe to activate the option by
565
Note that these tests relies on config._expand_default_value being already
566
overwritten in the parent class setUp.
570
super(TestIniConfigOptionExpansionDefaultValue, self).setUp()
574
self.warnings.append(args[0] % args[1:])
575
self.overrideAttr(trace, 'warning', warning)
577
def get_config(self, expand):
578
c = config.GlobalConfig.from_string('bzr.config.expand=%s' % (expand,),
582
def assertExpandIs(self, expected):
583
actual = config._get_expand_default_value()
584
#self.config.get_user_option_as_bool('bzr.config.expand')
585
self.assertEquals(expected, actual)
587
def test_default_is_None(self):
588
self.assertEquals(None, config._expand_default_value)
590
def test_default_is_False_even_if_None(self):
591
self.config = self.get_config(None)
592
self.assertExpandIs(False)
594
def test_default_is_False_even_if_invalid(self):
595
self.config = self.get_config('<your choice>')
596
self.assertExpandIs(False)
598
# Huh ? My choice is False ? Thanks, always happy to hear that :D
599
# Wait, you've been warned !
600
self.assertLength(1, self.warnings)
602
'Value "<your choice>" is not a boolean for "bzr.config.expand"',
605
def test_default_is_True(self):
606
self.config = self.get_config(True)
607
self.assertExpandIs(True)
609
def test_default_is_False(self):
610
self.config = self.get_config(False)
611
self.assertExpandIs(False)
614
class TestIniConfigOptionExpansion(tests.TestCase):
615
"""Test option expansion from the IniConfig level.
617
What we really want here is to test the Config level, but the class being
618
abstract as far as storing values is concerned, this can't be done
621
# FIXME: This should be rewritten when all configs share a storage
622
# implementation -- vila 2011-02-18
624
def get_config(self, string=None):
627
c = config.IniBasedConfig.from_string(string)
630
def assertExpansion(self, expected, conf, string, env=None):
631
self.assertEquals(expected, conf.expand_options(string, env))
633
def test_no_expansion(self):
634
c = self.get_config('')
635
self.assertExpansion('foo', c, 'foo')
637
def test_env_adding_options(self):
638
c = self.get_config('')
639
self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
641
def test_env_overriding_options(self):
642
c = self.get_config('foo=baz')
643
self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
645
def test_simple_ref(self):
646
c = self.get_config('foo=xxx')
647
self.assertExpansion('xxx', c, '{foo}')
649
def test_unknown_ref(self):
650
c = self.get_config('')
651
self.assertRaises(errors.ExpandingUnknownOption,
652
c.expand_options, '{foo}')
654
def test_indirect_ref(self):
655
c = self.get_config('''
659
self.assertExpansion('xxx', c, '{bar}')
661
def test_embedded_ref(self):
662
c = self.get_config('''
666
self.assertExpansion('xxx', c, '{{bar}}')
668
def test_simple_loop(self):
669
c = self.get_config('foo={foo}')
670
self.assertRaises(errors.OptionExpansionLoop, c.expand_options, '{foo}')
672
def test_indirect_loop(self):
673
c = self.get_config('''
677
e = self.assertRaises(errors.OptionExpansionLoop,
678
c.expand_options, '{foo}')
679
self.assertEquals('foo->bar->baz', e.refs)
680
self.assertEquals('{foo}', e.string)
683
conf = self.get_config('''
687
list={foo},{bar},{baz}
689
self.assertEquals(['start', 'middle', 'end'],
690
conf.get_user_option('list', expand=True))
692
def test_cascading_list(self):
693
conf = self.get_config('''
699
self.assertEquals(['start', 'middle', 'end'],
700
conf.get_user_option('list', expand=True))
702
def test_pathological_hidden_list(self):
703
conf = self.get_config('''
709
hidden={start}{middle}{end}
711
# Nope, it's either a string or a list, and the list wins as soon as a
712
# ',' appears, so the string concatenation never occur.
713
self.assertEquals(['{foo', '}', '{', 'bar}'],
714
conf.get_user_option('hidden', expand=True))
716
class TestLocationConfigOptionExpansion(tests.TestCaseInTempDir):
718
def get_config(self, location, string=None):
721
# Since we don't save the config we won't strictly require to inherit
722
# from TestCaseInTempDir, but an error occurs so quickly...
723
c = config.LocationConfig.from_string(string, location)
726
def test_dont_cross_unrelated_section(self):
727
c = self.get_config('/another/branch/path','''
732
[/another/branch/path]
735
self.assertRaises(errors.ExpandingUnknownOption,
736
c.get_user_option, 'bar', expand=True)
738
def test_cross_related_sections(self):
739
c = self.get_config('/project/branch/path','''
743
[/project/branch/path]
746
self.assertEquals('quux', c.get_user_option('bar', expand=True))
749
class TestIniBaseConfigOnDisk(tests.TestCaseInTempDir):
751
def test_cannot_reload_without_name(self):
752
conf = config.IniBasedConfig.from_string(sample_config_text)
753
self.assertRaises(AssertionError, conf.reload)
755
def test_reload_see_new_value(self):
756
c1 = config.IniBasedConfig.from_string('editor=vim\n',
757
file_name='./test/conf')
758
c1._write_config_file()
759
c2 = config.IniBasedConfig.from_string('editor=emacs\n',
760
file_name='./test/conf')
761
c2._write_config_file()
762
self.assertEqual('vim', c1.get_user_option('editor'))
763
self.assertEqual('emacs', c2.get_user_option('editor'))
764
# Make sure we get the Right value
766
self.assertEqual('emacs', c1.get_user_option('editor'))
769
class TestLockableConfig(tests.TestCaseInTempDir):
771
scenarios = lockable_config_scenarios()
776
config_section = None
779
super(TestLockableConfig, self).setUp()
780
self._content = '[%s]\none=1\ntwo=2\n' % (self.config_section,)
781
self.config = self.create_config(self._content)
783
def get_existing_config(self):
784
return self.config_class(*self.config_args)
786
def create_config(self, content):
787
kwargs = dict(save=True)
788
c = self.config_class.from_string(content, *self.config_args, **kwargs)
791
def test_simple_read_access(self):
792
self.assertEquals('1', self.config.get_user_option('one'))
794
def test_simple_write_access(self):
795
self.config.set_user_option('one', 'one')
796
self.assertEquals('one', self.config.get_user_option('one'))
798
def test_listen_to_the_last_speaker(self):
800
c2 = self.get_existing_config()
801
c1.set_user_option('one', 'ONE')
802
c2.set_user_option('two', 'TWO')
803
self.assertEquals('ONE', c1.get_user_option('one'))
804
self.assertEquals('TWO', c2.get_user_option('two'))
805
# The second update respect the first one
806
self.assertEquals('ONE', c2.get_user_option('one'))
808
def test_last_speaker_wins(self):
809
# If the same config is not shared, the same variable modified twice
810
# can only see a single result.
812
c2 = self.get_existing_config()
813
c1.set_user_option('one', 'c1')
814
c2.set_user_option('one', 'c2')
815
self.assertEquals('c2', c2._get_user_option('one'))
816
# The first modification is still available until another refresh
818
self.assertEquals('c1', c1._get_user_option('one'))
819
c1.set_user_option('two', 'done')
820
self.assertEquals('c2', c1._get_user_option('one'))
822
def test_writes_are_serialized(self):
824
c2 = self.get_existing_config()
826
# We spawn a thread that will pause *during* the write
827
before_writing = threading.Event()
828
after_writing = threading.Event()
829
writing_done = threading.Event()
830
c1_orig = c1._write_config_file
831
def c1_write_config_file():
834
# The lock is held we wait for the main thread to decide when to
837
c1._write_config_file = c1_write_config_file
839
c1.set_user_option('one', 'c1')
841
t1 = threading.Thread(target=c1_set_option)
842
# Collect the thread after the test
843
self.addCleanup(t1.join)
844
# Be ready to unblock the thread if the test goes wrong
845
self.addCleanup(after_writing.set)
847
before_writing.wait()
848
self.assertTrue(c1._lock.is_held)
849
self.assertRaises(errors.LockContention,
850
c2.set_user_option, 'one', 'c2')
851
self.assertEquals('c1', c1.get_user_option('one'))
852
# Let the lock be released
855
c2.set_user_option('one', 'c2')
856
self.assertEquals('c2', c2.get_user_option('one'))
858
def test_read_while_writing(self):
860
# We spawn a thread that will pause *during* the write
861
ready_to_write = threading.Event()
862
do_writing = threading.Event()
863
writing_done = threading.Event()
864
c1_orig = c1._write_config_file
865
def c1_write_config_file():
867
# The lock is held we wait for the main thread to decide when to
872
c1._write_config_file = c1_write_config_file
874
c1.set_user_option('one', 'c1')
875
t1 = threading.Thread(target=c1_set_option)
876
# Collect the thread after the test
877
self.addCleanup(t1.join)
878
# Be ready to unblock the thread if the test goes wrong
879
self.addCleanup(do_writing.set)
881
# Ensure the thread is ready to write
882
ready_to_write.wait()
883
self.assertTrue(c1._lock.is_held)
884
self.assertEquals('c1', c1.get_user_option('one'))
885
# If we read during the write, we get the old value
886
c2 = self.get_existing_config()
887
self.assertEquals('1', c2.get_user_option('one'))
888
# Let the writing occur and ensure it occurred
891
# Now we get the updated value
892
c3 = self.get_existing_config()
893
self.assertEquals('c1', c3.get_user_option('one'))
896
class TestGetUserOptionAs(TestIniConfig):
898
def test_get_user_option_as_bool(self):
899
conf, parser = self.make_config_parser("""
902
an_invalid_bool = maybe
903
a_list = hmm, who knows ? # This is interpreted as a list !
905
get_bool = conf.get_user_option_as_bool
906
self.assertEqual(True, get_bool('a_true_bool'))
907
self.assertEqual(False, get_bool('a_false_bool'))
910
warnings.append(args[0] % args[1:])
911
self.overrideAttr(trace, 'warning', warning)
912
msg = 'Value "%s" is not a boolean for "%s"'
913
self.assertIs(None, get_bool('an_invalid_bool'))
914
self.assertEquals(msg % ('maybe', 'an_invalid_bool'), warnings[0])
916
self.assertIs(None, get_bool('not_defined_in_this_config'))
917
self.assertEquals([], warnings)
919
def test_get_user_option_as_list(self):
920
conf, parser = self.make_config_parser("""
925
get_list = conf.get_user_option_as_list
926
self.assertEqual(['a', 'b', 'c'], get_list('a_list'))
927
self.assertEqual(['1'], get_list('length_1'))
928
self.assertEqual('x', conf.get_user_option('one_item'))
929
# automatically cast to list
930
self.assertEqual(['x'], get_list('one_item'))
933
class TestSupressWarning(TestIniConfig):
935
def make_warnings_config(self, s):
936
conf, parser = self.make_config_parser(s)
937
return conf.suppress_warning
939
def test_suppress_warning_unknown(self):
940
suppress_warning = self.make_warnings_config('')
941
self.assertEqual(False, suppress_warning('unknown_warning'))
943
def test_suppress_warning_known(self):
944
suppress_warning = self.make_warnings_config('suppress_warnings=a,b')
945
self.assertEqual(False, suppress_warning('c'))
946
self.assertEqual(True, suppress_warning('a'))
947
self.assertEqual(True, suppress_warning('b'))
950
class TestGetConfig(tests.TestCase):
952
def test_constructs(self):
953
my_config = config.GlobalConfig()
955
def test_calls_read_filenames(self):
956
# replace the class that is constructed, to check its parameters
957
oldparserclass = config.ConfigObj
958
config.ConfigObj = InstrumentedConfigObj
959
my_config = config.GlobalConfig()
961
parser = my_config._get_parser()
963
config.ConfigObj = oldparserclass
964
self.failUnless(isinstance(parser, InstrumentedConfigObj))
965
self.assertEqual(parser._calls, [('__init__', config.config_filename(),
969
class TestBranchConfig(tests.TestCaseWithTransport):
971
def test_constructs(self):
972
branch = FakeBranch()
973
my_config = config.BranchConfig(branch)
974
self.assertRaises(TypeError, config.BranchConfig)
976
def test_get_location_config(self):
977
branch = FakeBranch()
978
my_config = config.BranchConfig(branch)
979
location_config = my_config._get_location_config()
980
self.assertEqual(branch.base, location_config.location)
981
self.failUnless(location_config is my_config._get_location_config())
983
def test_get_config(self):
984
"""The Branch.get_config method works properly"""
985
b = bzrdir.BzrDir.create_standalone_workingtree('.').branch
986
my_config = b.get_config()
987
self.assertIs(my_config.get_user_option('wacky'), None)
988
my_config.set_user_option('wacky', 'unlikely')
989
self.assertEqual(my_config.get_user_option('wacky'), 'unlikely')
991
# Ensure we get the same thing if we start again
992
b2 = branch.Branch.open('.')
993
my_config2 = b2.get_config()
994
self.assertEqual(my_config2.get_user_option('wacky'), 'unlikely')
996
def test_has_explicit_nickname(self):
997
b = self.make_branch('.')
998
self.assertFalse(b.get_config().has_explicit_nickname())
1000
self.assertTrue(b.get_config().has_explicit_nickname())
1002
def test_config_url(self):
1003
"""The Branch.get_config will use section that uses a local url"""
1004
branch = self.make_branch('branch')
1005
self.assertEqual('branch', branch.nick)
1007
local_url = urlutils.local_path_to_url('branch')
1008
conf = config.LocationConfig.from_string(
1009
'[%s]\nnickname = foobar' % (local_url,),
1010
local_url, save=True)
1011
self.assertEqual('foobar', branch.nick)
1013
def test_config_local_path(self):
1014
"""The Branch.get_config will use a local system path"""
1015
branch = self.make_branch('branch')
1016
self.assertEqual('branch', branch.nick)
1018
local_path = osutils.getcwd().encode('utf8')
1019
conf = config.LocationConfig.from_string(
1020
'[%s/branch]\nnickname = barry' % (local_path,),
1021
'branch', save=True)
1022
self.assertEqual('barry', branch.nick)
1024
def test_config_creates_local(self):
1025
"""Creating a new entry in config uses a local path."""
1026
branch = self.make_branch('branch', format='knit')
1027
branch.set_push_location('http://foobar')
1028
local_path = osutils.getcwd().encode('utf8')
1029
# Surprisingly ConfigObj doesn't create a trailing newline
1030
self.check_file_contents(config.locations_config_filename(),
1032
'push_location = http://foobar\n'
1033
'push_location:policy = norecurse\n'
1036
def test_autonick_urlencoded(self):
1037
b = self.make_branch('!repo')
1038
self.assertEqual('!repo', b.get_config().get_nickname())
1040
def test_warn_if_masked(self):
1043
warnings.append(args[0] % args[1:])
1044
self.overrideAttr(trace, 'warning', warning)
1046
def set_option(store, warn_masked=True):
1048
conf.set_user_option('example_option', repr(store), store=store,
1049
warn_masked=warn_masked)
1050
def assertWarning(warning):
1052
self.assertEqual(0, len(warnings))
1054
self.assertEqual(1, len(warnings))
1055
self.assertEqual(warning, warnings[0])
1056
branch = self.make_branch('.')
1057
conf = branch.get_config()
1058
set_option(config.STORE_GLOBAL)
1060
set_option(config.STORE_BRANCH)
1062
set_option(config.STORE_GLOBAL)
1063
assertWarning('Value "4" is masked by "3" from branch.conf')
1064
set_option(config.STORE_GLOBAL, warn_masked=False)
1066
set_option(config.STORE_LOCATION)
1068
set_option(config.STORE_BRANCH)
1069
assertWarning('Value "3" is masked by "0" from locations.conf')
1070
set_option(config.STORE_BRANCH, warn_masked=False)
1074
class TestGlobalConfigItems(tests.TestCaseInTempDir):
1076
def test_user_id(self):
1077
my_config = config.GlobalConfig.from_string(sample_config_text)
1078
self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
1079
my_config._get_user_id())
1081
def test_absent_user_id(self):
1082
my_config = config.GlobalConfig()
1083
self.assertEqual(None, my_config._get_user_id())
1085
def test_configured_editor(self):
1086
my_config = config.GlobalConfig.from_string(sample_config_text)
1087
self.assertEqual("vim", my_config.get_editor())
1089
def test_signatures_always(self):
1090
my_config = config.GlobalConfig.from_string(sample_always_signatures)
1091
self.assertEqual(config.CHECK_NEVER,
1092
my_config.signature_checking())
1093
self.assertEqual(config.SIGN_ALWAYS,
1094
my_config.signing_policy())
1095
self.assertEqual(True, my_config.signature_needed())
1097
def test_signatures_if_possible(self):
1098
my_config = config.GlobalConfig.from_string(sample_maybe_signatures)
1099
self.assertEqual(config.CHECK_NEVER,
1100
my_config.signature_checking())
1101
self.assertEqual(config.SIGN_WHEN_REQUIRED,
1102
my_config.signing_policy())
1103
self.assertEqual(False, my_config.signature_needed())
1105
def test_signatures_ignore(self):
1106
my_config = config.GlobalConfig.from_string(sample_ignore_signatures)
1107
self.assertEqual(config.CHECK_ALWAYS,
1108
my_config.signature_checking())
1109
self.assertEqual(config.SIGN_NEVER,
1110
my_config.signing_policy())
1111
self.assertEqual(False, my_config.signature_needed())
1113
def _get_sample_config(self):
1114
my_config = config.GlobalConfig.from_string(sample_config_text)
1117
def test_gpg_signing_command(self):
1118
my_config = self._get_sample_config()
1119
self.assertEqual("gnome-gpg", my_config.gpg_signing_command())
1120
self.assertEqual(False, my_config.signature_needed())
1122
def _get_empty_config(self):
1123
my_config = config.GlobalConfig()
1126
def test_gpg_signing_command_unset(self):
1127
my_config = self._get_empty_config()
1128
self.assertEqual("gpg", my_config.gpg_signing_command())
1130
def test_get_user_option_default(self):
1131
my_config = self._get_empty_config()
1132
self.assertEqual(None, my_config.get_user_option('no_option'))
1134
def test_get_user_option_global(self):
1135
my_config = self._get_sample_config()
1136
self.assertEqual("something",
1137
my_config.get_user_option('user_global_option'))
1139
def test_post_commit_default(self):
1140
my_config = self._get_sample_config()
1141
self.assertEqual(None, my_config.post_commit())
1143
def test_configured_logformat(self):
1144
my_config = self._get_sample_config()
1145
self.assertEqual("short", my_config.log_format())
1147
def test_get_alias(self):
1148
my_config = self._get_sample_config()
1149
self.assertEqual('help', my_config.get_alias('h'))
1151
def test_get_aliases(self):
1152
my_config = self._get_sample_config()
1153
aliases = my_config.get_aliases()
1154
self.assertEqual(2, len(aliases))
1155
sorted_keys = sorted(aliases)
1156
self.assertEqual('help', aliases[sorted_keys[0]])
1157
self.assertEqual(sample_long_alias, aliases[sorted_keys[1]])
1159
def test_get_no_alias(self):
1160
my_config = self._get_sample_config()
1161
self.assertEqual(None, my_config.get_alias('foo'))
1163
def test_get_long_alias(self):
1164
my_config = self._get_sample_config()
1165
self.assertEqual(sample_long_alias, my_config.get_alias('ll'))
1167
def test_get_change_editor(self):
1168
my_config = self._get_sample_config()
1169
change_editor = my_config.get_change_editor('old', 'new')
1170
self.assertIs(diff.DiffFromTool, change_editor.__class__)
1171
self.assertEqual('vimdiff -of @new_path @old_path',
1172
' '.join(change_editor.command_template))
1174
def test_get_no_change_editor(self):
1175
my_config = self._get_empty_config()
1176
change_editor = my_config.get_change_editor('old', 'new')
1177
self.assertIs(None, change_editor)
1179
def test_get_merge_tools(self):
1180
conf = self._get_sample_config()
1181
tools = conf.get_merge_tools()
1182
self.log(repr(tools))
1184
{u'funkytool' : u'funkytool "arg with spaces" {this_temp}',
1185
u'sometool' : u'sometool {base} {this} {other} -o {result}'},
1188
def test_get_merge_tools_empty(self):
1189
conf = self._get_empty_config()
1190
tools = conf.get_merge_tools()
1191
self.assertEqual({}, tools)
1193
def test_find_merge_tool(self):
1194
conf = self._get_sample_config()
1195
cmdline = conf.find_merge_tool('sometool')
1196
self.assertEqual('sometool {base} {this} {other} -o {result}', cmdline)
1198
def test_find_merge_tool_not_found(self):
1199
conf = self._get_sample_config()
1200
cmdline = conf.find_merge_tool('DOES NOT EXIST')
1201
self.assertIs(cmdline, None)
1203
def test_find_merge_tool_known(self):
1204
conf = self._get_empty_config()
1205
cmdline = conf.find_merge_tool('kdiff3')
1206
self.assertEquals('kdiff3 {base} {this} {other} -o {result}', cmdline)
1208
def test_find_merge_tool_override_known(self):
1209
conf = self._get_empty_config()
1210
conf.set_user_option('bzr.mergetool.kdiff3', 'kdiff3 blah')
1211
cmdline = conf.find_merge_tool('kdiff3')
1212
self.assertEqual('kdiff3 blah', cmdline)
1215
class TestGlobalConfigSavingOptions(tests.TestCaseInTempDir):
1217
def test_empty(self):
1218
my_config = config.GlobalConfig()
1219
self.assertEqual(0, len(my_config.get_aliases()))
1221
def test_set_alias(self):
1222
my_config = config.GlobalConfig()
1223
alias_value = 'commit --strict'
1224
my_config.set_alias('commit', alias_value)
1225
new_config = config.GlobalConfig()
1226
self.assertEqual(alias_value, new_config.get_alias('commit'))
1228
def test_remove_alias(self):
1229
my_config = config.GlobalConfig()
1230
my_config.set_alias('commit', 'commit --strict')
1231
# Now remove the alias again.
1232
my_config.unset_alias('commit')
1233
new_config = config.GlobalConfig()
1234
self.assertIs(None, new_config.get_alias('commit'))
1237
class TestLocationConfig(tests.TestCaseInTempDir, TestOptionsMixin):
1239
def test_constructs(self):
1240
my_config = config.LocationConfig('http://example.com')
1241
self.assertRaises(TypeError, config.LocationConfig)
1243
def test_branch_calls_read_filenames(self):
1244
# This is testing the correct file names are provided.
1245
# TODO: consolidate with the test for GlobalConfigs filename checks.
1247
# replace the class that is constructed, to check its parameters
1248
oldparserclass = config.ConfigObj
1249
config.ConfigObj = InstrumentedConfigObj
1251
my_config = config.LocationConfig('http://www.example.com')
1252
parser = my_config._get_parser()
1254
config.ConfigObj = oldparserclass
1255
self.failUnless(isinstance(parser, InstrumentedConfigObj))
1256
self.assertEqual(parser._calls,
1257
[('__init__', config.locations_config_filename(),
1260
def test_get_global_config(self):
1261
my_config = config.BranchConfig(FakeBranch('http://example.com'))
1262
global_config = my_config._get_global_config()
1263
self.failUnless(isinstance(global_config, config.GlobalConfig))
1264
self.failUnless(global_config is my_config._get_global_config())
1266
def test__get_matching_sections_no_match(self):
1267
self.get_branch_config('/')
1268
self.assertEqual([], self.my_location_config._get_matching_sections())
1270
def test__get_matching_sections_exact(self):
1271
self.get_branch_config('http://www.example.com')
1272
self.assertEqual([('http://www.example.com', '')],
1273
self.my_location_config._get_matching_sections())
1275
def test__get_matching_sections_suffix_does_not(self):
1276
self.get_branch_config('http://www.example.com-com')
1277
self.assertEqual([], self.my_location_config._get_matching_sections())
1279
def test__get_matching_sections_subdir_recursive(self):
1280
self.get_branch_config('http://www.example.com/com')
1281
self.assertEqual([('http://www.example.com', 'com')],
1282
self.my_location_config._get_matching_sections())
1284
def test__get_matching_sections_ignoreparent(self):
1285
self.get_branch_config('http://www.example.com/ignoreparent')
1286
self.assertEqual([('http://www.example.com/ignoreparent', '')],
1287
self.my_location_config._get_matching_sections())
1289
def test__get_matching_sections_ignoreparent_subdir(self):
1290
self.get_branch_config(
1291
'http://www.example.com/ignoreparent/childbranch')
1292
self.assertEqual([('http://www.example.com/ignoreparent',
1294
self.my_location_config._get_matching_sections())
1296
def test__get_matching_sections_subdir_trailing_slash(self):
1297
self.get_branch_config('/b')
1298
self.assertEqual([('/b/', '')],
1299
self.my_location_config._get_matching_sections())
1301
def test__get_matching_sections_subdir_child(self):
1302
self.get_branch_config('/a/foo')
1303
self.assertEqual([('/a/*', ''), ('/a/', 'foo')],
1304
self.my_location_config._get_matching_sections())
1306
def test__get_matching_sections_subdir_child_child(self):
1307
self.get_branch_config('/a/foo/bar')
1308
self.assertEqual([('/a/*', 'bar'), ('/a/', 'foo/bar')],
1309
self.my_location_config._get_matching_sections())
1311
def test__get_matching_sections_trailing_slash_with_children(self):
1312
self.get_branch_config('/a/')
1313
self.assertEqual([('/a/', '')],
1314
self.my_location_config._get_matching_sections())
1316
def test__get_matching_sections_explicit_over_glob(self):
1317
# XXX: 2006-09-08 jamesh
1318
# This test only passes because ord('c') > ord('*'). If there
1319
# was a config section for '/a/?', it would get precedence
1321
self.get_branch_config('/a/c')
1322
self.assertEqual([('/a/c', ''), ('/a/*', ''), ('/a/', 'c')],
1323
self.my_location_config._get_matching_sections())
1325
def test__get_option_policy_normal(self):
1326
self.get_branch_config('http://www.example.com')
1328
self.my_location_config._get_config_policy(
1329
'http://www.example.com', 'normal_option'),
1332
def test__get_option_policy_norecurse(self):
1333
self.get_branch_config('http://www.example.com')
1335
self.my_location_config._get_option_policy(
1336
'http://www.example.com', 'norecurse_option'),
1337
config.POLICY_NORECURSE)
1338
# Test old recurse=False setting:
1340
self.my_location_config._get_option_policy(
1341
'http://www.example.com/norecurse', 'normal_option'),
1342
config.POLICY_NORECURSE)
1344
def test__get_option_policy_normal(self):
1345
self.get_branch_config('http://www.example.com')
1347
self.my_location_config._get_option_policy(
1348
'http://www.example.com', 'appendpath_option'),
1349
config.POLICY_APPENDPATH)
1351
def test__get_options_with_policy(self):
1352
self.get_branch_config('/dir/subdir',
1353
location_config="""\
1355
other_url = /other-dir
1356
other_url:policy = appendpath
1358
other_url = /other-subdir
1361
[(u'other_url', u'/other-subdir', u'/dir/subdir', 'locations'),
1362
(u'other_url', u'/other-dir', u'/dir', 'locations'),
1363
(u'other_url:policy', u'appendpath', u'/dir', 'locations')],
1364
self.my_location_config)
1366
def test_location_without_username(self):
1367
self.get_branch_config('http://www.example.com/ignoreparent')
1368
self.assertEqual(u'Erik B\u00e5gfors <erik@bagfors.nu>',
1369
self.my_config.username())
1371
def test_location_not_listed(self):
1372
"""Test that the global username is used when no location matches"""
1373
self.get_branch_config('/home/robertc/sources')
1374
self.assertEqual(u'Erik B\u00e5gfors <erik@bagfors.nu>',
1375
self.my_config.username())
1377
def test_overriding_location(self):
1378
self.get_branch_config('http://www.example.com/foo')
1379
self.assertEqual('Robert Collins <robertc@example.org>',
1380
self.my_config.username())
1382
def test_signatures_not_set(self):
1383
self.get_branch_config('http://www.example.com',
1384
global_config=sample_ignore_signatures)
1385
self.assertEqual(config.CHECK_ALWAYS,
1386
self.my_config.signature_checking())
1387
self.assertEqual(config.SIGN_NEVER,
1388
self.my_config.signing_policy())
1390
def test_signatures_never(self):
1391
self.get_branch_config('/a/c')
1392
self.assertEqual(config.CHECK_NEVER,
1393
self.my_config.signature_checking())
1395
def test_signatures_when_available(self):
1396
self.get_branch_config('/a/', global_config=sample_ignore_signatures)
1397
self.assertEqual(config.CHECK_IF_POSSIBLE,
1398
self.my_config.signature_checking())
1400
def test_signatures_always(self):
1401
self.get_branch_config('/b')
1402
self.assertEqual(config.CHECK_ALWAYS,
1403
self.my_config.signature_checking())
1405
def test_gpg_signing_command(self):
1406
self.get_branch_config('/b')
1407
self.assertEqual("gnome-gpg", self.my_config.gpg_signing_command())
1409
def test_gpg_signing_command_missing(self):
1410
self.get_branch_config('/a')
1411
self.assertEqual("false", self.my_config.gpg_signing_command())
1413
def test_get_user_option_global(self):
1414
self.get_branch_config('/a')
1415
self.assertEqual('something',
1416
self.my_config.get_user_option('user_global_option'))
1418
def test_get_user_option_local(self):
1419
self.get_branch_config('/a')
1420
self.assertEqual('local',
1421
self.my_config.get_user_option('user_local_option'))
1423
def test_get_user_option_appendpath(self):
1424
# returned as is for the base path:
1425
self.get_branch_config('http://www.example.com')
1426
self.assertEqual('append',
1427
self.my_config.get_user_option('appendpath_option'))
1428
# Extra path components get appended:
1429
self.get_branch_config('http://www.example.com/a/b/c')
1430
self.assertEqual('append/a/b/c',
1431
self.my_config.get_user_option('appendpath_option'))
1432
# Overriden for http://www.example.com/dir, where it is a
1434
self.get_branch_config('http://www.example.com/dir/a/b/c')
1435
self.assertEqual('normal',
1436
self.my_config.get_user_option('appendpath_option'))
1438
def test_get_user_option_norecurse(self):
1439
self.get_branch_config('http://www.example.com')
1440
self.assertEqual('norecurse',
1441
self.my_config.get_user_option('norecurse_option'))
1442
self.get_branch_config('http://www.example.com/dir')
1443
self.assertEqual(None,
1444
self.my_config.get_user_option('norecurse_option'))
1445
# http://www.example.com/norecurse is a recurse=False section
1446
# that redefines normal_option. Subdirectories do not pick up
1447
# this redefinition.
1448
self.get_branch_config('http://www.example.com/norecurse')
1449
self.assertEqual('norecurse',
1450
self.my_config.get_user_option('normal_option'))
1451
self.get_branch_config('http://www.example.com/norecurse/subdir')
1452
self.assertEqual('normal',
1453
self.my_config.get_user_option('normal_option'))
1455
def test_set_user_option_norecurse(self):
1456
self.get_branch_config('http://www.example.com')
1457
self.my_config.set_user_option('foo', 'bar',
1458
store=config.STORE_LOCATION_NORECURSE)
1460
self.my_location_config._get_option_policy(
1461
'http://www.example.com', 'foo'),
1462
config.POLICY_NORECURSE)
1464
def test_set_user_option_appendpath(self):
1465
self.get_branch_config('http://www.example.com')
1466
self.my_config.set_user_option('foo', 'bar',
1467
store=config.STORE_LOCATION_APPENDPATH)
1469
self.my_location_config._get_option_policy(
1470
'http://www.example.com', 'foo'),
1471
config.POLICY_APPENDPATH)
1473
def test_set_user_option_change_policy(self):
1474
self.get_branch_config('http://www.example.com')
1475
self.my_config.set_user_option('norecurse_option', 'normal',
1476
store=config.STORE_LOCATION)
1478
self.my_location_config._get_option_policy(
1479
'http://www.example.com', 'norecurse_option'),
1482
def test_set_user_option_recurse_false_section(self):
1483
# The following section has recurse=False set. The test is to
1484
# make sure that a normal option can be added to the section,
1485
# converting recurse=False to the norecurse policy.
1486
self.get_branch_config('http://www.example.com/norecurse')
1487
self.callDeprecated(['The recurse option is deprecated as of 0.14. '
1488
'The section "http://www.example.com/norecurse" '
1489
'has been converted to use policies.'],
1490
self.my_config.set_user_option,
1491
'foo', 'bar', store=config.STORE_LOCATION)
1493
self.my_location_config._get_option_policy(
1494
'http://www.example.com/norecurse', 'foo'),
1496
# The previously existing option is still norecurse:
1498
self.my_location_config._get_option_policy(
1499
'http://www.example.com/norecurse', 'normal_option'),
1500
config.POLICY_NORECURSE)
1502
def test_post_commit_default(self):
1503
self.get_branch_config('/a/c')
1504
self.assertEqual('bzrlib.tests.test_config.post_commit',
1505
self.my_config.post_commit())
1507
def get_branch_config(self, location, global_config=None,
1508
location_config=None):
1509
my_branch = FakeBranch(location)
1510
if global_config is None:
1511
global_config = sample_config_text
1512
if location_config is None:
1513
location_config = sample_branches_text
1515
my_global_config = config.GlobalConfig.from_string(global_config,
1517
my_location_config = config.LocationConfig.from_string(
1518
location_config, my_branch.base, save=True)
1519
my_config = config.BranchConfig(my_branch)
1520
self.my_config = my_config
1521
self.my_location_config = my_config._get_location_config()
1523
def test_set_user_setting_sets_and_saves(self):
1524
self.get_branch_config('/a/c')
1525
record = InstrumentedConfigObj("foo")
1526
self.my_location_config._parser = record
1528
self.callDeprecated(['The recurse option is deprecated as of '
1529
'0.14. The section "/a/c" has been '
1530
'converted to use policies.'],
1531
self.my_config.set_user_option,
1532
'foo', 'bar', store=config.STORE_LOCATION)
1533
self.assertEqual([('reload',),
1534
('__contains__', '/a/c'),
1535
('__contains__', '/a/c/'),
1536
('__setitem__', '/a/c', {}),
1537
('__getitem__', '/a/c'),
1538
('__setitem__', 'foo', 'bar'),
1539
('__getitem__', '/a/c'),
1540
('as_bool', 'recurse'),
1541
('__getitem__', '/a/c'),
1542
('__delitem__', 'recurse'),
1543
('__getitem__', '/a/c'),
1545
('__getitem__', '/a/c'),
1546
('__contains__', 'foo:policy'),
1550
def test_set_user_setting_sets_and_saves2(self):
1551
self.get_branch_config('/a/c')
1552
self.assertIs(self.my_config.get_user_option('foo'), None)
1553
self.my_config.set_user_option('foo', 'bar')
1555
self.my_config.branch.control_files.files['branch.conf'].strip(),
1557
self.assertEqual(self.my_config.get_user_option('foo'), 'bar')
1558
self.my_config.set_user_option('foo', 'baz',
1559
store=config.STORE_LOCATION)
1560
self.assertEqual(self.my_config.get_user_option('foo'), 'baz')
1561
self.my_config.set_user_option('foo', 'qux')
1562
self.assertEqual(self.my_config.get_user_option('foo'), 'baz')
1564
def test_get_bzr_remote_path(self):
1565
my_config = config.LocationConfig('/a/c')
1566
self.assertEqual('bzr', my_config.get_bzr_remote_path())
1567
my_config.set_user_option('bzr_remote_path', '/path-bzr')
1568
self.assertEqual('/path-bzr', my_config.get_bzr_remote_path())
1569
self.overrideEnv('BZR_REMOTE_PATH', '/environ-bzr')
1570
self.assertEqual('/environ-bzr', my_config.get_bzr_remote_path())
1573
precedence_global = 'option = global'
1574
precedence_branch = 'option = branch'
1575
precedence_location = """
1579
[http://example.com/specific]
1583
class TestBranchConfigItems(tests.TestCaseInTempDir):
1585
def get_branch_config(self, global_config=None, location=None,
1586
location_config=None, branch_data_config=None):
1587
my_branch = FakeBranch(location)
1588
if global_config is not None:
1589
my_global_config = config.GlobalConfig.from_string(global_config,
1591
if location_config is not None:
1592
my_location_config = config.LocationConfig.from_string(
1593
location_config, my_branch.base, save=True)
1594
my_config = config.BranchConfig(my_branch)
1595
if branch_data_config is not None:
1596
my_config.branch.control_files.files['branch.conf'] = \
1600
def test_user_id(self):
1601
branch = FakeBranch(user_id='Robert Collins <robertc@example.net>')
1602
my_config = config.BranchConfig(branch)
1603
self.assertEqual("Robert Collins <robertc@example.net>",
1604
my_config.username())
1605
my_config.branch.control_files.files['email'] = "John"
1606
my_config.set_user_option('email',
1607
"Robert Collins <robertc@example.org>")
1608
self.assertEqual("John", my_config.username())
1609
del my_config.branch.control_files.files['email']
1610
self.assertEqual("Robert Collins <robertc@example.org>",
1611
my_config.username())
1613
def test_not_set_in_branch(self):
1614
my_config = self.get_branch_config(global_config=sample_config_text)
1615
self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
1616
my_config._get_user_id())
1617
my_config.branch.control_files.files['email'] = "John"
1618
self.assertEqual("John", my_config._get_user_id())
1620
def test_BZR_EMAIL_OVERRIDES(self):
1621
self.overrideEnv('BZR_EMAIL', "Robert Collins <robertc@example.org>")
1622
branch = FakeBranch()
1623
my_config = config.BranchConfig(branch)
1624
self.assertEqual("Robert Collins <robertc@example.org>",
1625
my_config.username())
1627
def test_signatures_forced(self):
1628
my_config = self.get_branch_config(
1629
global_config=sample_always_signatures)
1630
self.assertEqual(config.CHECK_NEVER, my_config.signature_checking())
1631
self.assertEqual(config.SIGN_ALWAYS, my_config.signing_policy())
1632
self.assertTrue(my_config.signature_needed())
1634
def test_signatures_forced_branch(self):
1635
my_config = self.get_branch_config(
1636
global_config=sample_ignore_signatures,
1637
branch_data_config=sample_always_signatures)
1638
self.assertEqual(config.CHECK_NEVER, my_config.signature_checking())
1639
self.assertEqual(config.SIGN_ALWAYS, my_config.signing_policy())
1640
self.assertTrue(my_config.signature_needed())
1642
def test_gpg_signing_command(self):
1643
my_config = self.get_branch_config(
1644
global_config=sample_config_text,
1645
# branch data cannot set gpg_signing_command
1646
branch_data_config="gpg_signing_command=pgp")
1647
self.assertEqual('gnome-gpg', my_config.gpg_signing_command())
1649
def test_get_user_option_global(self):
1650
my_config = self.get_branch_config(global_config=sample_config_text)
1651
self.assertEqual('something',
1652
my_config.get_user_option('user_global_option'))
1654
def test_post_commit_default(self):
1655
my_config = self.get_branch_config(global_config=sample_config_text,
1657
location_config=sample_branches_text)
1658
self.assertEqual(my_config.branch.base, '/a/c')
1659
self.assertEqual('bzrlib.tests.test_config.post_commit',
1660
my_config.post_commit())
1661
my_config.set_user_option('post_commit', 'rmtree_root')
1662
# post-commit is ignored when present in branch data
1663
self.assertEqual('bzrlib.tests.test_config.post_commit',
1664
my_config.post_commit())
1665
my_config.set_user_option('post_commit', 'rmtree_root',
1666
store=config.STORE_LOCATION)
1667
self.assertEqual('rmtree_root', my_config.post_commit())
1669
def test_config_precedence(self):
1670
# FIXME: eager test, luckily no persitent config file makes it fail
1672
my_config = self.get_branch_config(global_config=precedence_global)
1673
self.assertEqual(my_config.get_user_option('option'), 'global')
1674
my_config = self.get_branch_config(global_config=precedence_global,
1675
branch_data_config=precedence_branch)
1676
self.assertEqual(my_config.get_user_option('option'), 'branch')
1677
my_config = self.get_branch_config(
1678
global_config=precedence_global,
1679
branch_data_config=precedence_branch,
1680
location_config=precedence_location)
1681
self.assertEqual(my_config.get_user_option('option'), 'recurse')
1682
my_config = self.get_branch_config(
1683
global_config=precedence_global,
1684
branch_data_config=precedence_branch,
1685
location_config=precedence_location,
1686
location='http://example.com/specific')
1687
self.assertEqual(my_config.get_user_option('option'), 'exact')
1689
def test_get_mail_client(self):
1690
config = self.get_branch_config()
1691
client = config.get_mail_client()
1692
self.assertIsInstance(client, mail_client.DefaultMail)
1695
config.set_user_option('mail_client', 'evolution')
1696
client = config.get_mail_client()
1697
self.assertIsInstance(client, mail_client.Evolution)
1699
config.set_user_option('mail_client', 'kmail')
1700
client = config.get_mail_client()
1701
self.assertIsInstance(client, mail_client.KMail)
1703
config.set_user_option('mail_client', 'mutt')
1704
client = config.get_mail_client()
1705
self.assertIsInstance(client, mail_client.Mutt)
1707
config.set_user_option('mail_client', 'thunderbird')
1708
client = config.get_mail_client()
1709
self.assertIsInstance(client, mail_client.Thunderbird)
1712
config.set_user_option('mail_client', 'default')
1713
client = config.get_mail_client()
1714
self.assertIsInstance(client, mail_client.DefaultMail)
1716
config.set_user_option('mail_client', 'editor')
1717
client = config.get_mail_client()
1718
self.assertIsInstance(client, mail_client.Editor)
1720
config.set_user_option('mail_client', 'mapi')
1721
client = config.get_mail_client()
1722
self.assertIsInstance(client, mail_client.MAPIClient)
1724
config.set_user_option('mail_client', 'xdg-email')
1725
client = config.get_mail_client()
1726
self.assertIsInstance(client, mail_client.XDGEmail)
1728
config.set_user_option('mail_client', 'firebird')
1729
self.assertRaises(errors.UnknownMailClient, config.get_mail_client)
1732
class TestMailAddressExtraction(tests.TestCase):
1734
def test_extract_email_address(self):
1735
self.assertEqual('jane@test.com',
1736
config.extract_email_address('Jane <jane@test.com>'))
1737
self.assertRaises(errors.NoEmailInUsername,
1738
config.extract_email_address, 'Jane Tester')
1740
def test_parse_username(self):
1741
self.assertEqual(('', 'jdoe@example.com'),
1742
config.parse_username('jdoe@example.com'))
1743
self.assertEqual(('', 'jdoe@example.com'),
1744
config.parse_username('<jdoe@example.com>'))
1745
self.assertEqual(('John Doe', 'jdoe@example.com'),
1746
config.parse_username('John Doe <jdoe@example.com>'))
1747
self.assertEqual(('John Doe', ''),
1748
config.parse_username('John Doe'))
1749
self.assertEqual(('John Doe', 'jdoe@example.com'),
1750
config.parse_username('John Doe jdoe@example.com'))
1752
class TestTreeConfig(tests.TestCaseWithTransport):
1754
def test_get_value(self):
1755
"""Test that retreiving a value from a section is possible"""
1756
branch = self.make_branch('.')
1757
tree_config = config.TreeConfig(branch)
1758
tree_config.set_option('value', 'key', 'SECTION')
1759
tree_config.set_option('value2', 'key2')
1760
tree_config.set_option('value3-top', 'key3')
1761
tree_config.set_option('value3-section', 'key3', 'SECTION')
1762
value = tree_config.get_option('key', 'SECTION')
1763
self.assertEqual(value, 'value')
1764
value = tree_config.get_option('key2')
1765
self.assertEqual(value, 'value2')
1766
self.assertEqual(tree_config.get_option('non-existant'), None)
1767
value = tree_config.get_option('non-existant', 'SECTION')
1768
self.assertEqual(value, None)
1769
value = tree_config.get_option('non-existant', default='default')
1770
self.assertEqual(value, 'default')
1771
self.assertEqual(tree_config.get_option('key2', 'NOSECTION'), None)
1772
value = tree_config.get_option('key2', 'NOSECTION', default='default')
1773
self.assertEqual(value, 'default')
1774
value = tree_config.get_option('key3')
1775
self.assertEqual(value, 'value3-top')
1776
value = tree_config.get_option('key3', 'SECTION')
1777
self.assertEqual(value, 'value3-section')
1780
class TestTransportConfig(tests.TestCaseWithTransport):
1782
def test_get_value(self):
1783
"""Test that retreiving a value from a section is possible"""
1784
bzrdir_config = config.TransportConfig(transport.get_transport('.'),
1786
bzrdir_config.set_option('value', 'key', 'SECTION')
1787
bzrdir_config.set_option('value2', 'key2')
1788
bzrdir_config.set_option('value3-top', 'key3')
1789
bzrdir_config.set_option('value3-section', 'key3', 'SECTION')
1790
value = bzrdir_config.get_option('key', 'SECTION')
1791
self.assertEqual(value, 'value')
1792
value = bzrdir_config.get_option('key2')
1793
self.assertEqual(value, 'value2')
1794
self.assertEqual(bzrdir_config.get_option('non-existant'), None)
1795
value = bzrdir_config.get_option('non-existant', 'SECTION')
1796
self.assertEqual(value, None)
1797
value = bzrdir_config.get_option('non-existant', default='default')
1798
self.assertEqual(value, 'default')
1799
self.assertEqual(bzrdir_config.get_option('key2', 'NOSECTION'), None)
1800
value = bzrdir_config.get_option('key2', 'NOSECTION',
1802
self.assertEqual(value, 'default')
1803
value = bzrdir_config.get_option('key3')
1804
self.assertEqual(value, 'value3-top')
1805
value = bzrdir_config.get_option('key3', 'SECTION')
1806
self.assertEqual(value, 'value3-section')
1808
def test_set_unset_default_stack_on(self):
1809
my_dir = self.make_bzrdir('.')
1810
bzrdir_config = config.BzrDirConfig(my_dir)
1811
self.assertIs(None, bzrdir_config.get_default_stack_on())
1812
bzrdir_config.set_default_stack_on('Foo')
1813
self.assertEqual('Foo', bzrdir_config._config.get_option(
1814
'default_stack_on'))
1815
self.assertEqual('Foo', bzrdir_config.get_default_stack_on())
1816
bzrdir_config.set_default_stack_on(None)
1817
self.assertIs(None, bzrdir_config.get_default_stack_on())
1820
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
1823
super(TestConfigGetOptions, self).setUp()
1824
create_configs(self)
1826
# One variable in none of the above
1827
def test_no_variable(self):
1828
# Using branch should query branch, locations and bazaar
1829
self.assertOptions([], self.branch_config)
1831
def test_option_in_bazaar(self):
1832
self.bazaar_config.set_user_option('file', 'bazaar')
1833
self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
1836
def test_option_in_locations(self):
1837
self.locations_config.set_user_option('file', 'locations')
1839
[('file', 'locations', self.tree.basedir, 'locations')],
1840
self.locations_config)
1842
def test_option_in_branch(self):
1843
self.branch_config.set_user_option('file', 'branch')
1844
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
1847
def test_option_in_bazaar_and_branch(self):
1848
self.bazaar_config.set_user_option('file', 'bazaar')
1849
self.branch_config.set_user_option('file', 'branch')
1850
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
1851
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
1854
def test_option_in_branch_and_locations(self):
1855
# Hmm, locations override branch :-/
1856
self.locations_config.set_user_option('file', 'locations')
1857
self.branch_config.set_user_option('file', 'branch')
1859
[('file', 'locations', self.tree.basedir, 'locations'),
1860
('file', 'branch', 'DEFAULT', 'branch'),],
1863
def test_option_in_bazaar_locations_and_branch(self):
1864
self.bazaar_config.set_user_option('file', 'bazaar')
1865
self.locations_config.set_user_option('file', 'locations')
1866
self.branch_config.set_user_option('file', 'branch')
1868
[('file', 'locations', self.tree.basedir, 'locations'),
1869
('file', 'branch', 'DEFAULT', 'branch'),
1870
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
1874
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
1877
super(TestConfigRemoveOption, self).setUp()
1878
create_configs_with_file_option(self)
1880
def test_remove_in_locations(self):
1881
self.locations_config.remove_user_option('file', self.tree.basedir)
1883
[('file', 'branch', 'DEFAULT', 'branch'),
1884
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
1887
def test_remove_in_branch(self):
1888
self.branch_config.remove_user_option('file')
1890
[('file', 'locations', self.tree.basedir, 'locations'),
1891
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
1894
def test_remove_in_bazaar(self):
1895
self.bazaar_config.remove_user_option('file')
1897
[('file', 'locations', self.tree.basedir, 'locations'),
1898
('file', 'branch', 'DEFAULT', 'branch'),],
1902
class TestConfigGetSections(tests.TestCaseWithTransport):
1905
super(TestConfigGetSections, self).setUp()
1906
create_configs(self)
1908
def assertSectionNames(self, expected, conf, name=None):
1909
"""Check which sections are returned for a given config.
1911
If fallback configurations exist their sections can be included.
1913
:param expected: A list of section names.
1915
:param conf: The configuration that will be queried.
1917
:param name: An optional section name that will be passed to
1920
sections = list(conf._get_sections(name))
1921
self.assertLength(len(expected), sections)
1922
self.assertEqual(expected, [name for name, _, _ in sections])
1924
def test_bazaar_default_section(self):
1925
self.assertSectionNames(['DEFAULT'], self.bazaar_config)
1927
def test_locations_default_section(self):
1928
# No sections are defined in an empty file
1929
self.assertSectionNames([], self.locations_config)
1931
def test_locations_named_section(self):
1932
self.locations_config.set_user_option('file', 'locations')
1933
self.assertSectionNames([self.tree.basedir], self.locations_config)
1935
def test_locations_matching_sections(self):
1936
loc_config = self.locations_config
1937
loc_config.set_user_option('file', 'locations')
1938
# We need to cheat a bit here to create an option in sections above and
1939
# below the 'location' one.
1940
parser = loc_config._get_parser()
1941
# locations.cong deals with '/' ignoring native os.sep
1942
location_names = self.tree.basedir.split('/')
1943
parent = '/'.join(location_names[:-1])
1944
child = '/'.join(location_names + ['child'])
1946
parser[parent]['file'] = 'parent'
1948
parser[child]['file'] = 'child'
1949
self.assertSectionNames([self.tree.basedir, parent], loc_config)
1951
def test_branch_data_default_section(self):
1952
self.assertSectionNames([None],
1953
self.branch_config._get_branch_data_config())
1955
def test_branch_default_sections(self):
1956
# No sections are defined in an empty locations file
1957
self.assertSectionNames([None, 'DEFAULT'],
1959
# Unless we define an option
1960
self.branch_config._get_location_config().set_user_option(
1961
'file', 'locations')
1962
self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
1965
def test_bazaar_named_section(self):
1966
# We need to cheat as the API doesn't give direct access to sections
1967
# other than DEFAULT.
1968
self.bazaar_config.set_alias('bazaar', 'bzr')
1969
self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
1972
class TestAuthenticationConfigFile(tests.TestCase):
1973
"""Test the authentication.conf file matching"""
1975
def _got_user_passwd(self, expected_user, expected_password,
1976
config, *args, **kwargs):
1977
credentials = config.get_credentials(*args, **kwargs)
1978
if credentials is None:
1982
user = credentials['user']
1983
password = credentials['password']
1984
self.assertEquals(expected_user, user)
1985
self.assertEquals(expected_password, password)
1987
def test_empty_config(self):
1988
conf = config.AuthenticationConfig(_file=StringIO())
1989
self.assertEquals({}, conf._get_config())
1990
self._got_user_passwd(None, None, conf, 'http', 'foo.net')
1992
def test_missing_auth_section_header(self):
1993
conf = config.AuthenticationConfig(_file=StringIO('foo = bar'))
1994
self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
1996
def test_auth_section_header_not_closed(self):
1997
conf = config.AuthenticationConfig(_file=StringIO('[DEF'))
1998
self.assertRaises(errors.ParseConfigError, conf._get_config)
2000
def test_auth_value_not_boolean(self):
2001
conf = config.AuthenticationConfig(_file=StringIO(
2005
verify_certificates=askme # Error: Not a boolean
2007
self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
2009
def test_auth_value_not_int(self):
2010
conf = config.AuthenticationConfig(_file=StringIO(
2014
port=port # Error: Not an int
2016
self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
2018
def test_unknown_password_encoding(self):
2019
conf = config.AuthenticationConfig(_file=StringIO(
2023
password_encoding=unknown
2025
self.assertRaises(ValueError, conf.get_password,
2026
'ftp', 'foo.net', 'joe')
2028
def test_credentials_for_scheme_host(self):
2029
conf = config.AuthenticationConfig(_file=StringIO(
2030
"""# Identity on foo.net
2035
password=secret-pass
2038
self._got_user_passwd('joe', 'secret-pass', conf, 'ftp', 'foo.net')
2040
self._got_user_passwd(None, None, conf, 'http', 'foo.net')
2042
self._got_user_passwd(None, None, conf, 'ftp', 'bar.net')
2044
def test_credentials_for_host_port(self):
2045
conf = config.AuthenticationConfig(_file=StringIO(
2046
"""# Identity on foo.net
2052
password=secret-pass
2055
self._got_user_passwd('joe', 'secret-pass',
2056
conf, 'ftp', 'foo.net', port=10021)
2058
self._got_user_passwd(None, None, conf, 'ftp', 'foo.net')
2060
def test_for_matching_host(self):
2061
conf = config.AuthenticationConfig(_file=StringIO(
2062
"""# Identity on foo.net
2068
[sourceforge domain]
2075
self._got_user_passwd('georges', 'bendover',
2076
conf, 'bzr', 'foo.bzr.sf.net')
2078
self._got_user_passwd(None, None,
2079
conf, 'bzr', 'bbzr.sf.net')
2081
def test_for_matching_host_None(self):
2082
conf = config.AuthenticationConfig(_file=StringIO(
2083
"""# Identity on foo.net
2093
self._got_user_passwd('joe', 'joepass',
2094
conf, 'bzr', 'quux.net')
2095
# no host but different scheme
2096
self._got_user_passwd('georges', 'bendover',
2097
conf, 'ftp', 'quux.net')
2099
def test_credentials_for_path(self):
2100
conf = config.AuthenticationConfig(_file=StringIO(
2116
self._got_user_passwd(None, None,
2117
conf, 'http', host='bar.org', path='/dir3')
2119
self._got_user_passwd('georges', 'bendover',
2120
conf, 'http', host='bar.org', path='/dir2')
2122
self._got_user_passwd('jim', 'jimpass',
2123
conf, 'http', host='bar.org',path='/dir1/subdir')
2125
def test_credentials_for_user(self):
2126
conf = config.AuthenticationConfig(_file=StringIO(
2135
self._got_user_passwd('jim', 'jimpass',
2136
conf, 'http', 'bar.org')
2138
self._got_user_passwd('jim', 'jimpass',
2139
conf, 'http', 'bar.org', user='jim')
2140
# Don't get a different user if one is specified
2141
self._got_user_passwd(None, None,
2142
conf, 'http', 'bar.org', user='georges')
2144
def test_credentials_for_user_without_password(self):
2145
conf = config.AuthenticationConfig(_file=StringIO(
2152
# Get user but no password
2153
self._got_user_passwd('jim', None,
2154
conf, 'http', 'bar.org')
2156
def test_verify_certificates(self):
2157
conf = config.AuthenticationConfig(_file=StringIO(
2164
verify_certificates=False
2171
credentials = conf.get_credentials('https', 'bar.org')
2172
self.assertEquals(False, credentials.get('verify_certificates'))
2173
credentials = conf.get_credentials('https', 'foo.net')
2174
self.assertEquals(True, credentials.get('verify_certificates'))
2177
class TestAuthenticationStorage(tests.TestCaseInTempDir):
2179
def test_set_credentials(self):
2180
conf = config.AuthenticationConfig()
2181
conf.set_credentials('name', 'host', 'user', 'scheme', 'password',
2182
99, path='/foo', verify_certificates=False, realm='realm')
2183
credentials = conf.get_credentials(host='host', scheme='scheme',
2184
port=99, path='/foo',
2186
CREDENTIALS = {'name': 'name', 'user': 'user', 'password': 'password',
2187
'verify_certificates': False, 'scheme': 'scheme',
2188
'host': 'host', 'port': 99, 'path': '/foo',
2190
self.assertEqual(CREDENTIALS, credentials)
2191
credentials_from_disk = config.AuthenticationConfig().get_credentials(
2192
host='host', scheme='scheme', port=99, path='/foo', realm='realm')
2193
self.assertEqual(CREDENTIALS, credentials_from_disk)
2195
def test_reset_credentials_different_name(self):
2196
conf = config.AuthenticationConfig()
2197
conf.set_credentials('name', 'host', 'user', 'scheme', 'password'),
2198
conf.set_credentials('name2', 'host', 'user2', 'scheme', 'password'),
2199
self.assertIs(None, conf._get_config().get('name'))
2200
credentials = conf.get_credentials(host='host', scheme='scheme')
2201
CREDENTIALS = {'name': 'name2', 'user': 'user2', 'password':
2202
'password', 'verify_certificates': True,
2203
'scheme': 'scheme', 'host': 'host', 'port': None,
2204
'path': None, 'realm': None}
2205
self.assertEqual(CREDENTIALS, credentials)
2208
class TestAuthenticationConfig(tests.TestCase):
2209
"""Test AuthenticationConfig behaviour"""
2211
def _check_default_password_prompt(self, expected_prompt_format, scheme,
2212
host=None, port=None, realm=None,
2216
user, password = 'jim', 'precious'
2217
expected_prompt = expected_prompt_format % {
2218
'scheme': scheme, 'host': host, 'port': port,
2219
'user': user, 'realm': realm}
2221
stdout = tests.StringIOWrapper()
2222
stderr = tests.StringIOWrapper()
2223
ui.ui_factory = tests.TestUIFactory(stdin=password + '\n',
2224
stdout=stdout, stderr=stderr)
2225
# We use an empty conf so that the user is always prompted
2226
conf = config.AuthenticationConfig()
2227
self.assertEquals(password,
2228
conf.get_password(scheme, host, user, port=port,
2229
realm=realm, path=path))
2230
self.assertEquals(expected_prompt, stderr.getvalue())
2231
self.assertEquals('', stdout.getvalue())
2233
def _check_default_username_prompt(self, expected_prompt_format, scheme,
2234
host=None, port=None, realm=None,
2239
expected_prompt = expected_prompt_format % {
2240
'scheme': scheme, 'host': host, 'port': port,
2242
stdout = tests.StringIOWrapper()
2243
stderr = tests.StringIOWrapper()
2244
ui.ui_factory = tests.TestUIFactory(stdin=username+ '\n',
2245
stdout=stdout, stderr=stderr)
2246
# We use an empty conf so that the user is always prompted
2247
conf = config.AuthenticationConfig()
2248
self.assertEquals(username, conf.get_user(scheme, host, port=port,
2249
realm=realm, path=path, ask=True))
2250
self.assertEquals(expected_prompt, stderr.getvalue())
2251
self.assertEquals('', stdout.getvalue())
2253
def test_username_defaults_prompts(self):
2254
# HTTP prompts can't be tested here, see test_http.py
2255
self._check_default_username_prompt('FTP %(host)s username: ', 'ftp')
2256
self._check_default_username_prompt(
2257
'FTP %(host)s:%(port)d username: ', 'ftp', port=10020)
2258
self._check_default_username_prompt(
2259
'SSH %(host)s:%(port)d username: ', 'ssh', port=12345)
2261
def test_username_default_no_prompt(self):
2262
conf = config.AuthenticationConfig()
2263
self.assertEquals(None,
2264
conf.get_user('ftp', 'example.com'))
2265
self.assertEquals("explicitdefault",
2266
conf.get_user('ftp', 'example.com', default="explicitdefault"))
2268
def test_password_default_prompts(self):
2269
# HTTP prompts can't be tested here, see test_http.py
2270
self._check_default_password_prompt(
2271
'FTP %(user)s@%(host)s password: ', 'ftp')
2272
self._check_default_password_prompt(
2273
'FTP %(user)s@%(host)s:%(port)d password: ', 'ftp', port=10020)
2274
self._check_default_password_prompt(
2275
'SSH %(user)s@%(host)s:%(port)d password: ', 'ssh', port=12345)
2276
# SMTP port handling is a bit special (it's handled if embedded in the
2278
# FIXME: should we: forbid that, extend it to other schemes, leave
2279
# things as they are that's fine thank you ?
2280
self._check_default_password_prompt('SMTP %(user)s@%(host)s password: ',
2282
self._check_default_password_prompt('SMTP %(user)s@%(host)s password: ',
2283
'smtp', host='bar.org:10025')
2284
self._check_default_password_prompt(
2285
'SMTP %(user)s@%(host)s:%(port)d password: ',
2288
def test_ssh_password_emits_warning(self):
2289
conf = config.AuthenticationConfig(_file=StringIO(
2297
entered_password = 'typed-by-hand'
2298
stdout = tests.StringIOWrapper()
2299
stderr = tests.StringIOWrapper()
2300
ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
2301
stdout=stdout, stderr=stderr)
2303
# Since the password defined in the authentication config is ignored,
2304
# the user is prompted
2305
self.assertEquals(entered_password,
2306
conf.get_password('ssh', 'bar.org', user='jim'))
2307
self.assertContainsRe(
2309
'password ignored in section \[ssh with password\]')
2311
def test_ssh_without_password_doesnt_emit_warning(self):
2312
conf = config.AuthenticationConfig(_file=StringIO(
2319
entered_password = 'typed-by-hand'
2320
stdout = tests.StringIOWrapper()
2321
stderr = tests.StringIOWrapper()
2322
ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
2326
# Since the password defined in the authentication config is ignored,
2327
# the user is prompted
2328
self.assertEquals(entered_password,
2329
conf.get_password('ssh', 'bar.org', user='jim'))
2330
# No warning shoud be emitted since there is no password. We are only
2332
self.assertNotContainsRe(
2334
'password ignored in section \[ssh with password\]')
2336
def test_uses_fallback_stores(self):
2337
self.overrideAttr(config, 'credential_store_registry',
2338
config.CredentialStoreRegistry())
2339
store = StubCredentialStore()
2340
store.add_credentials("http", "example.com", "joe", "secret")
2341
config.credential_store_registry.register("stub", store, fallback=True)
2342
conf = config.AuthenticationConfig(_file=StringIO())
2343
creds = conf.get_credentials("http", "example.com")
2344
self.assertEquals("joe", creds["user"])
2345
self.assertEquals("secret", creds["password"])
2348
class StubCredentialStore(config.CredentialStore):
2354
def add_credentials(self, scheme, host, user, password=None):
2355
self._username[(scheme, host)] = user
2356
self._password[(scheme, host)] = password
2358
def get_credentials(self, scheme, host, port=None, user=None,
2359
path=None, realm=None):
2360
key = (scheme, host)
2361
if not key in self._username:
2363
return { "scheme": scheme, "host": host, "port": port,
2364
"user": self._username[key], "password": self._password[key]}
2367
class CountingCredentialStore(config.CredentialStore):
2372
def get_credentials(self, scheme, host, port=None, user=None,
2373
path=None, realm=None):
2378
class TestCredentialStoreRegistry(tests.TestCase):
2380
def _get_cs_registry(self):
2381
return config.credential_store_registry
2383
def test_default_credential_store(self):
2384
r = self._get_cs_registry()
2385
default = r.get_credential_store(None)
2386
self.assertIsInstance(default, config.PlainTextCredentialStore)
2388
def test_unknown_credential_store(self):
2389
r = self._get_cs_registry()
2390
# It's hard to imagine someone creating a credential store named
2391
# 'unknown' so we use that as an never registered key.
2392
self.assertRaises(KeyError, r.get_credential_store, 'unknown')
2394
def test_fallback_none_registered(self):
2395
r = config.CredentialStoreRegistry()
2396
self.assertEquals(None,
2397
r.get_fallback_credentials("http", "example.com"))
2399
def test_register(self):
2400
r = config.CredentialStoreRegistry()
2401
r.register("stub", StubCredentialStore(), fallback=False)
2402
r.register("another", StubCredentialStore(), fallback=True)
2403
self.assertEquals(["another", "stub"], r.keys())
2405
def test_register_lazy(self):
2406
r = config.CredentialStoreRegistry()
2407
r.register_lazy("stub", "bzrlib.tests.test_config",
2408
"StubCredentialStore", fallback=False)
2409
self.assertEquals(["stub"], r.keys())
2410
self.assertIsInstance(r.get_credential_store("stub"),
2411
StubCredentialStore)
2413
def test_is_fallback(self):
2414
r = config.CredentialStoreRegistry()
2415
r.register("stub1", None, fallback=False)
2416
r.register("stub2", None, fallback=True)
2417
self.assertEquals(False, r.is_fallback("stub1"))
2418
self.assertEquals(True, r.is_fallback("stub2"))
2420
def test_no_fallback(self):
2421
r = config.CredentialStoreRegistry()
2422
store = CountingCredentialStore()
2423
r.register("count", store, fallback=False)
2424
self.assertEquals(None,
2425
r.get_fallback_credentials("http", "example.com"))
2426
self.assertEquals(0, store._calls)
2428
def test_fallback_credentials(self):
2429
r = config.CredentialStoreRegistry()
2430
store = StubCredentialStore()
2431
store.add_credentials("http", "example.com",
2432
"somebody", "geheim")
2433
r.register("stub", store, fallback=True)
2434
creds = r.get_fallback_credentials("http", "example.com")
2435
self.assertEquals("somebody", creds["user"])
2436
self.assertEquals("geheim", creds["password"])
2438
def test_fallback_first_wins(self):
2439
r = config.CredentialStoreRegistry()
2440
stub1 = StubCredentialStore()
2441
stub1.add_credentials("http", "example.com",
2442
"somebody", "stub1")
2443
r.register("stub1", stub1, fallback=True)
2444
stub2 = StubCredentialStore()
2445
stub2.add_credentials("http", "example.com",
2446
"somebody", "stub2")
2447
r.register("stub2", stub1, fallback=True)
2448
creds = r.get_fallback_credentials("http", "example.com")
2449
self.assertEquals("somebody", creds["user"])
2450
self.assertEquals("stub1", creds["password"])
2453
class TestPlainTextCredentialStore(tests.TestCase):
2455
def test_decode_password(self):
2456
r = config.credential_store_registry
2457
plain_text = r.get_credential_store()
2458
decoded = plain_text.decode_password(dict(password='secret'))
2459
self.assertEquals('secret', decoded)
2462
# FIXME: Once we have a way to declare authentication to all test servers, we
2463
# can implement generic tests.
2464
# test_user_password_in_url
2465
# test_user_in_url_password_from_config
2466
# test_user_in_url_password_prompted
2467
# test_user_in_config
2468
# test_user_getpass.getuser
2469
# test_user_prompted ?
2470
class TestAuthenticationRing(tests.TestCaseWithTransport):