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 (
48
from bzrlib.util.configobj import configobj
51
def lockable_config_scenarios():
54
{'config_class': config.GlobalConfig,
56
'config_section': 'DEFAULT'}),
58
{'config_class': config.LocationConfig,
60
'config_section': '.'}),]
63
load_tests = scenarios.load_tests_apply_scenarios
66
sample_long_alias="log -r-15..-1 --line"
67
sample_config_text = u"""
69
email=Erik B\u00e5gfors <erik@bagfors.nu>
71
change_editor=vimdiff -of @new_path @old_path
72
gpg_signing_command=gnome-gpg
74
user_global_option=something
75
bzr.mergetool.sometool=sometool {base} {this} {other} -o {result}
76
bzr.mergetool.funkytool=funkytool "arg with spaces" {this_temp}
77
bzr.default_mergetool=sometool
80
ll=""" + sample_long_alias + "\n"
83
sample_always_signatures = """
85
check_signatures=ignore
86
create_signatures=always
89
sample_ignore_signatures = """
91
check_signatures=require
92
create_signatures=never
95
sample_maybe_signatures = """
97
check_signatures=ignore
98
create_signatures=when-required
101
sample_branches_text = """
102
[http://www.example.com]
104
email=Robert Collins <robertc@example.org>
105
normal_option = normal
106
appendpath_option = append
107
appendpath_option:policy = appendpath
108
norecurse_option = norecurse
109
norecurse_option:policy = norecurse
110
[http://www.example.com/ignoreparent]
111
# different project: ignore parent dir config
113
[http://www.example.com/norecurse]
114
# configuration items that only apply to this dir
116
normal_option = norecurse
117
[http://www.example.com/dir]
118
appendpath_option = normal
120
check_signatures=require
121
# test trailing / matching with no children
123
check_signatures=check-available
124
gpg_signing_command=false
125
user_local_option=local
126
# test trailing / matching
128
#subdirs will match but not the parent
130
check_signatures=ignore
131
post_commit=bzrlib.tests.test_config.post_commit
132
#testing explicit beats globs
136
def create_configs(test):
137
"""Create configuration files for a given test.
139
This requires creating a tree (and populate the ``test.tree`` attribute)
140
and its associated branch and will populate the following attributes:
142
- branch_config: A BranchConfig for the associated branch.
144
- locations_config : A LocationConfig for the associated branch
146
- bazaar_config: A GlobalConfig.
148
The tree and branch are created in a 'tree' subdirectory so the tests can
149
still use the test directory to stay outside of the branch.
151
tree = test.make_branch_and_tree('tree')
153
test.branch_config = config.BranchConfig(tree.branch)
154
test.locations_config = config.LocationConfig(tree.basedir)
155
test.bazaar_config = config.GlobalConfig()
158
def create_configs_with_file_option(test):
159
"""Create configuration files with a ``file`` option set in each.
161
This builds on ``create_configs`` and add one ``file`` option in each
162
configuration with a value which allows identifying the configuration file.
165
test.bazaar_config.set_user_option('file', 'bazaar')
166
test.locations_config.set_user_option('file', 'locations')
167
test.branch_config.set_user_option('file', 'branch')
170
class TestOptionsMixin:
172
def assertOptions(self, expected, conf):
173
# We don't care about the parser (as it will make tests hard to write
174
# and error-prone anyway)
175
self.assertThat([opt[:4] for opt in conf._get_options()],
176
matchers.Equals(expected))
179
class InstrumentedConfigObj(object):
180
"""A config obj look-enough-alike to record calls made to it."""
182
def __contains__(self, thing):
183
self._calls.append(('__contains__', thing))
186
def __getitem__(self, key):
187
self._calls.append(('__getitem__', key))
190
def __init__(self, input, encoding=None):
191
self._calls = [('__init__', input, encoding)]
193
def __setitem__(self, key, value):
194
self._calls.append(('__setitem__', key, value))
196
def __delitem__(self, key):
197
self._calls.append(('__delitem__', key))
200
self._calls.append(('keys',))
204
self._calls.append(('reload',))
206
def write(self, arg):
207
self._calls.append(('write',))
209
def as_bool(self, value):
210
self._calls.append(('as_bool', value))
213
def get_value(self, section, name):
214
self._calls.append(('get_value', section, name))
218
class FakeBranch(object):
220
def __init__(self, base=None, user_id=None):
222
self.base = "http://example.com/branches/demo"
225
self._transport = self.control_files = \
226
FakeControlFilesAndTransport(user_id=user_id)
228
def _get_config(self):
229
return config.TransportConfig(self._transport, 'branch.conf')
231
def lock_write(self):
238
class FakeControlFilesAndTransport(object):
240
def __init__(self, user_id=None):
243
self.files['email'] = user_id
244
self._transport = self
246
def get_utf8(self, filename):
248
raise AssertionError("get_utf8 should no longer be used")
250
def get(self, filename):
253
return StringIO(self.files[filename])
255
raise errors.NoSuchFile(filename)
257
def get_bytes(self, filename):
260
return self.files[filename]
262
raise errors.NoSuchFile(filename)
264
def put(self, filename, fileobj):
265
self.files[filename] = fileobj.read()
267
def put_file(self, filename, fileobj):
268
return self.put(filename, fileobj)
271
class InstrumentedConfig(config.Config):
272
"""An instrumented config that supplies stubs for template methods."""
275
super(InstrumentedConfig, self).__init__()
277
self._signatures = config.CHECK_NEVER
279
def _get_user_id(self):
280
self._calls.append('_get_user_id')
281
return "Robert Collins <robert.collins@example.org>"
283
def _get_signature_checking(self):
284
self._calls.append('_get_signature_checking')
285
return self._signatures
287
def _get_change_editor(self):
288
self._calls.append('_get_change_editor')
289
return 'vimdiff -fo @new_path @old_path'
292
bool_config = """[DEFAULT]
301
class TestConfigObj(tests.TestCase):
303
def test_get_bool(self):
304
co = config.ConfigObj(StringIO(bool_config))
305
self.assertIs(co.get_bool('DEFAULT', 'active'), True)
306
self.assertIs(co.get_bool('DEFAULT', 'inactive'), False)
307
self.assertIs(co.get_bool('UPPERCASE', 'active'), True)
308
self.assertIs(co.get_bool('UPPERCASE', 'nonactive'), False)
310
def test_hash_sign_in_value(self):
312
Before 4.5.0, ConfigObj did not quote # signs in values, so they'd be
313
treated as comments when read in again. (#86838)
315
co = config.ConfigObj()
316
co['test'] = 'foo#bar'
318
co.write(outfile=outfile)
319
lines = outfile.getvalue().splitlines()
320
self.assertEqual(lines, ['test = "foo#bar"'])
321
co2 = config.ConfigObj(lines)
322
self.assertEqual(co2['test'], 'foo#bar')
324
def test_triple_quotes(self):
325
# Bug #710410: if the value string has triple quotes
326
# then ConfigObj versions up to 4.7.2 will quote them wrong
327
# and won't able to read them back
328
triple_quotes_value = '''spam
329
""" that's my spam """
331
co = config.ConfigObj()
332
co['test'] = triple_quotes_value
333
# While writing this test another bug in ConfigObj has been found:
334
# method co.write() without arguments produces list of lines
335
# one option per line, and multiline values are not split
336
# across multiple lines,
337
# and that breaks the parsing these lines back by ConfigObj.
338
# This issue only affects test, but it's better to avoid
339
# `co.write()` construct at all.
340
# [bialix 20110222] bug report sent to ConfigObj's author
342
co.write(outfile=outfile)
343
output = outfile.getvalue()
344
# now we're trying to read it back
345
co2 = config.ConfigObj(StringIO(output))
346
self.assertEquals(triple_quotes_value, co2['test'])
349
erroneous_config = """[section] # line 1
352
whocares=notme # line 4
356
class TestConfigObjErrors(tests.TestCase):
358
def test_duplicate_section_name_error_line(self):
360
co = configobj.ConfigObj(StringIO(erroneous_config),
362
except config.configobj.DuplicateError, e:
363
self.assertEqual(3, e.line_number)
365
self.fail('Error in config file not detected')
368
class TestConfig(tests.TestCase):
370
def test_constructs(self):
373
def test_no_default_editor(self):
374
self.assertRaises(NotImplementedError, config.Config().get_editor)
376
def test_user_email(self):
377
my_config = InstrumentedConfig()
378
self.assertEqual('robert.collins@example.org', my_config.user_email())
379
self.assertEqual(['_get_user_id'], my_config._calls)
381
def test_username(self):
382
my_config = InstrumentedConfig()
383
self.assertEqual('Robert Collins <robert.collins@example.org>',
384
my_config.username())
385
self.assertEqual(['_get_user_id'], my_config._calls)
387
def test_signatures_default(self):
388
my_config = config.Config()
389
self.assertFalse(my_config.signature_needed())
390
self.assertEqual(config.CHECK_IF_POSSIBLE,
391
my_config.signature_checking())
392
self.assertEqual(config.SIGN_WHEN_REQUIRED,
393
my_config.signing_policy())
395
def test_signatures_template_method(self):
396
my_config = InstrumentedConfig()
397
self.assertEqual(config.CHECK_NEVER, my_config.signature_checking())
398
self.assertEqual(['_get_signature_checking'], my_config._calls)
400
def test_signatures_template_method_none(self):
401
my_config = InstrumentedConfig()
402
my_config._signatures = None
403
self.assertEqual(config.CHECK_IF_POSSIBLE,
404
my_config.signature_checking())
405
self.assertEqual(['_get_signature_checking'], my_config._calls)
407
def test_gpg_signing_command_default(self):
408
my_config = config.Config()
409
self.assertEqual('gpg', my_config.gpg_signing_command())
411
def test_get_user_option_default(self):
412
my_config = config.Config()
413
self.assertEqual(None, my_config.get_user_option('no_option'))
415
def test_post_commit_default(self):
416
my_config = config.Config()
417
self.assertEqual(None, my_config.post_commit())
419
def test_log_format_default(self):
420
my_config = config.Config()
421
self.assertEqual('long', my_config.log_format())
423
def test_get_change_editor(self):
424
my_config = InstrumentedConfig()
425
change_editor = my_config.get_change_editor('old_tree', 'new_tree')
426
self.assertEqual(['_get_change_editor'], my_config._calls)
427
self.assertIs(diff.DiffFromTool, change_editor.__class__)
428
self.assertEqual(['vimdiff', '-fo', '@new_path', '@old_path'],
429
change_editor.command_template)
432
class TestConfigPath(tests.TestCase):
435
super(TestConfigPath, self).setUp()
436
self.overrideEnv('HOME', '/home/bogus')
437
self.overrideEnv('XDG_CACHE_DIR', '')
438
if sys.platform == 'win32':
440
'BZR_HOME', r'C:\Documents and Settings\bogus\Application Data')
442
'C:/Documents and Settings/bogus/Application Data/bazaar/2.0'
444
self.bzr_home = '/home/bogus/.bazaar'
446
def test_config_dir(self):
447
self.assertEqual(config.config_dir(), self.bzr_home)
449
def test_config_filename(self):
450
self.assertEqual(config.config_filename(),
451
self.bzr_home + '/bazaar.conf')
453
def test_locations_config_filename(self):
454
self.assertEqual(config.locations_config_filename(),
455
self.bzr_home + '/locations.conf')
457
def test_authentication_config_filename(self):
458
self.assertEqual(config.authentication_config_filename(),
459
self.bzr_home + '/authentication.conf')
461
def test_xdg_cache_dir(self):
462
self.assertEqual(config.xdg_cache_dir(),
463
'/home/bogus/.cache')
466
class TestXDGConfigDir(tests.TestCaseInTempDir):
467
# must be in temp dir because config tests for the existence of the bazaar
468
# subdirectory of $XDG_CONFIG_HOME
471
if sys.platform in ('darwin', 'win32'):
472
raise tests.TestNotApplicable(
473
'XDG config dir not used on this platform')
474
super(TestXDGConfigDir, self).setUp()
475
self.overrideEnv('HOME', self.test_home_dir)
476
# BZR_HOME overrides everything we want to test so unset it.
477
self.overrideEnv('BZR_HOME', None)
479
def test_xdg_config_dir_exists(self):
480
"""When ~/.config/bazaar exists, use it as the config dir."""
481
newdir = osutils.pathjoin(self.test_home_dir, '.config', 'bazaar')
483
self.assertEqual(config.config_dir(), newdir)
485
def test_xdg_config_home(self):
486
"""When XDG_CONFIG_HOME is set, use it."""
487
xdgconfigdir = osutils.pathjoin(self.test_home_dir, 'xdgconfig')
488
self.overrideEnv('XDG_CONFIG_HOME', xdgconfigdir)
489
newdir = osutils.pathjoin(xdgconfigdir, 'bazaar')
491
self.assertEqual(config.config_dir(), newdir)
494
class TestIniConfig(tests.TestCaseInTempDir):
496
def make_config_parser(self, s):
497
conf = config.IniBasedConfig.from_string(s)
498
return conf, conf._get_parser()
501
class TestIniConfigBuilding(TestIniConfig):
503
def test_contructs(self):
504
my_config = config.IniBasedConfig()
506
def test_from_fp(self):
507
my_config = config.IniBasedConfig.from_string(sample_config_text)
508
self.assertIsInstance(my_config._get_parser(), configobj.ConfigObj)
510
def test_cached(self):
511
my_config = config.IniBasedConfig.from_string(sample_config_text)
512
parser = my_config._get_parser()
513
self.assertTrue(my_config._get_parser() is parser)
515
def _dummy_chown(self, path, uid, gid):
516
self.path, self.uid, self.gid = path, uid, gid
518
def test_ini_config_ownership(self):
519
"""Ensure that chown is happening during _write_config_file"""
520
self.requireFeature(features.chown_feature)
521
self.overrideAttr(os, 'chown', self._dummy_chown)
522
self.path = self.uid = self.gid = None
523
conf = config.IniBasedConfig(file_name='./foo.conf')
524
conf._write_config_file()
525
self.assertEquals(self.path, './foo.conf')
526
self.assertTrue(isinstance(self.uid, int))
527
self.assertTrue(isinstance(self.gid, int))
529
def test_get_filename_parameter_is_deprecated_(self):
530
conf = self.callDeprecated([
531
'IniBasedConfig.__init__(get_filename) was deprecated in 2.3.'
532
' Use file_name instead.'],
533
config.IniBasedConfig, lambda: 'ini.conf')
534
self.assertEqual('ini.conf', conf.file_name)
536
def test_get_parser_file_parameter_is_deprecated_(self):
537
config_file = StringIO(sample_config_text.encode('utf-8'))
538
conf = config.IniBasedConfig.from_string(sample_config_text)
539
conf = self.callDeprecated([
540
'IniBasedConfig._get_parser(file=xxx) was deprecated in 2.3.'
541
' Use IniBasedConfig(_content=xxx) instead.'],
542
conf._get_parser, file=config_file)
545
class TestIniConfigSaving(tests.TestCaseInTempDir):
547
def test_cant_save_without_a_file_name(self):
548
conf = config.IniBasedConfig()
549
self.assertRaises(AssertionError, conf._write_config_file)
551
def test_saved_with_content(self):
552
content = 'foo = bar\n'
553
conf = config.IniBasedConfig.from_string(
554
content, file_name='./test.conf', save=True)
555
self.assertFileEqual(content, 'test.conf')
558
class TestIniConfigOptionExpansionDefaultValue(tests.TestCaseInTempDir):
559
"""What is the default value of expand for config options.
561
This is an opt-in beta feature used to evaluate whether or not option
562
references can appear in dangerous place raising exceptions, disapearing
563
(and as such corrupting data) or if it's safe to activate the option by
566
Note that these tests relies on config._expand_default_value being already
567
overwritten in the parent class setUp.
571
super(TestIniConfigOptionExpansionDefaultValue, self).setUp()
575
self.warnings.append(args[0] % args[1:])
576
self.overrideAttr(trace, 'warning', warning)
578
def get_config(self, expand):
579
c = config.GlobalConfig.from_string('bzr.config.expand=%s' % (expand,),
583
def assertExpandIs(self, expected):
584
actual = config._get_expand_default_value()
585
#self.config.get_user_option_as_bool('bzr.config.expand')
586
self.assertEquals(expected, actual)
588
def test_default_is_None(self):
589
self.assertEquals(None, config._expand_default_value)
591
def test_default_is_False_even_if_None(self):
592
self.config = self.get_config(None)
593
self.assertExpandIs(False)
595
def test_default_is_False_even_if_invalid(self):
596
self.config = self.get_config('<your choice>')
597
self.assertExpandIs(False)
599
# Huh ? My choice is False ? Thanks, always happy to hear that :D
600
# Wait, you've been warned !
601
self.assertLength(1, self.warnings)
603
'Value "<your choice>" is not a boolean for "bzr.config.expand"',
606
def test_default_is_True(self):
607
self.config = self.get_config(True)
608
self.assertExpandIs(True)
610
def test_default_is_False(self):
611
self.config = self.get_config(False)
612
self.assertExpandIs(False)
615
class TestIniConfigOptionExpansion(tests.TestCase):
616
"""Test option expansion from the IniConfig level.
618
What we really want here is to test the Config level, but the class being
619
abstract as far as storing values is concerned, this can't be done
622
# FIXME: This should be rewritten when all configs share a storage
623
# implementation -- vila 2011-02-18
625
def get_config(self, string=None):
628
c = config.IniBasedConfig.from_string(string)
631
def assertExpansion(self, expected, conf, string, env=None):
632
self.assertEquals(expected, conf.expand_options(string, env))
634
def test_no_expansion(self):
635
c = self.get_config('')
636
self.assertExpansion('foo', c, 'foo')
638
def test_env_adding_options(self):
639
c = self.get_config('')
640
self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
642
def test_env_overriding_options(self):
643
c = self.get_config('foo=baz')
644
self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
646
def test_simple_ref(self):
647
c = self.get_config('foo=xxx')
648
self.assertExpansion('xxx', c, '{foo}')
650
def test_unknown_ref(self):
651
c = self.get_config('')
652
self.assertRaises(errors.ExpandingUnknownOption,
653
c.expand_options, '{foo}')
655
def test_indirect_ref(self):
656
c = self.get_config('''
660
self.assertExpansion('xxx', c, '{bar}')
662
def test_embedded_ref(self):
663
c = self.get_config('''
667
self.assertExpansion('xxx', c, '{{bar}}')
669
def test_simple_loop(self):
670
c = self.get_config('foo={foo}')
671
self.assertRaises(errors.OptionExpansionLoop, c.expand_options, '{foo}')
673
def test_indirect_loop(self):
674
c = self.get_config('''
678
e = self.assertRaises(errors.OptionExpansionLoop,
679
c.expand_options, '{foo}')
680
self.assertEquals('foo->bar->baz', e.refs)
681
self.assertEquals('{foo}', e.string)
684
conf = self.get_config('''
688
list={foo},{bar},{baz}
690
self.assertEquals(['start', 'middle', 'end'],
691
conf.get_user_option('list', expand=True))
693
def test_cascading_list(self):
694
conf = self.get_config('''
700
self.assertEquals(['start', 'middle', 'end'],
701
conf.get_user_option('list', expand=True))
703
def test_pathological_hidden_list(self):
704
conf = self.get_config('''
710
hidden={start}{middle}{end}
712
# Nope, it's either a string or a list, and the list wins as soon as a
713
# ',' appears, so the string concatenation never occur.
714
self.assertEquals(['{foo', '}', '{', 'bar}'],
715
conf.get_user_option('hidden', expand=True))
717
class TestLocationConfigOptionExpansion(tests.TestCaseInTempDir):
719
def get_config(self, location, string=None):
722
# Since we don't save the config we won't strictly require to inherit
723
# from TestCaseInTempDir, but an error occurs so quickly...
724
c = config.LocationConfig.from_string(string, location)
727
def test_dont_cross_unrelated_section(self):
728
c = self.get_config('/another/branch/path','''
733
[/another/branch/path]
736
self.assertRaises(errors.ExpandingUnknownOption,
737
c.get_user_option, 'bar', expand=True)
739
def test_cross_related_sections(self):
740
c = self.get_config('/project/branch/path','''
744
[/project/branch/path]
747
self.assertEquals('quux', c.get_user_option('bar', expand=True))
750
class TestIniBaseConfigOnDisk(tests.TestCaseInTempDir):
752
def test_cannot_reload_without_name(self):
753
conf = config.IniBasedConfig.from_string(sample_config_text)
754
self.assertRaises(AssertionError, conf.reload)
756
def test_reload_see_new_value(self):
757
c1 = config.IniBasedConfig.from_string('editor=vim\n',
758
file_name='./test/conf')
759
c1._write_config_file()
760
c2 = config.IniBasedConfig.from_string('editor=emacs\n',
761
file_name='./test/conf')
762
c2._write_config_file()
763
self.assertEqual('vim', c1.get_user_option('editor'))
764
self.assertEqual('emacs', c2.get_user_option('editor'))
765
# Make sure we get the Right value
767
self.assertEqual('emacs', c1.get_user_option('editor'))
770
class TestLockableConfig(tests.TestCaseInTempDir):
772
scenarios = lockable_config_scenarios()
777
config_section = None
780
super(TestLockableConfig, self).setUp()
781
self._content = '[%s]\none=1\ntwo=2\n' % (self.config_section,)
782
self.config = self.create_config(self._content)
784
def get_existing_config(self):
785
return self.config_class(*self.config_args)
787
def create_config(self, content):
788
kwargs = dict(save=True)
789
c = self.config_class.from_string(content, *self.config_args, **kwargs)
792
def test_simple_read_access(self):
793
self.assertEquals('1', self.config.get_user_option('one'))
795
def test_simple_write_access(self):
796
self.config.set_user_option('one', 'one')
797
self.assertEquals('one', self.config.get_user_option('one'))
799
def test_listen_to_the_last_speaker(self):
801
c2 = self.get_existing_config()
802
c1.set_user_option('one', 'ONE')
803
c2.set_user_option('two', 'TWO')
804
self.assertEquals('ONE', c1.get_user_option('one'))
805
self.assertEquals('TWO', c2.get_user_option('two'))
806
# The second update respect the first one
807
self.assertEquals('ONE', c2.get_user_option('one'))
809
def test_last_speaker_wins(self):
810
# If the same config is not shared, the same variable modified twice
811
# can only see a single result.
813
c2 = self.get_existing_config()
814
c1.set_user_option('one', 'c1')
815
c2.set_user_option('one', 'c2')
816
self.assertEquals('c2', c2._get_user_option('one'))
817
# The first modification is still available until another refresh
819
self.assertEquals('c1', c1._get_user_option('one'))
820
c1.set_user_option('two', 'done')
821
self.assertEquals('c2', c1._get_user_option('one'))
823
def test_writes_are_serialized(self):
825
c2 = self.get_existing_config()
827
# We spawn a thread that will pause *during* the write
828
before_writing = threading.Event()
829
after_writing = threading.Event()
830
writing_done = threading.Event()
831
c1_orig = c1._write_config_file
832
def c1_write_config_file():
835
# The lock is held we wait for the main thread to decide when to
838
c1._write_config_file = c1_write_config_file
840
c1.set_user_option('one', 'c1')
842
t1 = threading.Thread(target=c1_set_option)
843
# Collect the thread after the test
844
self.addCleanup(t1.join)
845
# Be ready to unblock the thread if the test goes wrong
846
self.addCleanup(after_writing.set)
848
before_writing.wait()
849
self.assertTrue(c1._lock.is_held)
850
self.assertRaises(errors.LockContention,
851
c2.set_user_option, 'one', 'c2')
852
self.assertEquals('c1', c1.get_user_option('one'))
853
# Let the lock be released
856
c2.set_user_option('one', 'c2')
857
self.assertEquals('c2', c2.get_user_option('one'))
859
def test_read_while_writing(self):
861
# We spawn a thread that will pause *during* the write
862
ready_to_write = threading.Event()
863
do_writing = threading.Event()
864
writing_done = threading.Event()
865
c1_orig = c1._write_config_file
866
def c1_write_config_file():
868
# The lock is held we wait for the main thread to decide when to
873
c1._write_config_file = c1_write_config_file
875
c1.set_user_option('one', 'c1')
876
t1 = threading.Thread(target=c1_set_option)
877
# Collect the thread after the test
878
self.addCleanup(t1.join)
879
# Be ready to unblock the thread if the test goes wrong
880
self.addCleanup(do_writing.set)
882
# Ensure the thread is ready to write
883
ready_to_write.wait()
884
self.assertTrue(c1._lock.is_held)
885
self.assertEquals('c1', c1.get_user_option('one'))
886
# If we read during the write, we get the old value
887
c2 = self.get_existing_config()
888
self.assertEquals('1', c2.get_user_option('one'))
889
# Let the writing occur and ensure it occurred
892
# Now we get the updated value
893
c3 = self.get_existing_config()
894
self.assertEquals('c1', c3.get_user_option('one'))
897
class TestGetUserOptionAs(TestIniConfig):
899
def test_get_user_option_as_bool(self):
900
conf, parser = self.make_config_parser("""
903
an_invalid_bool = maybe
904
a_list = hmm, who knows ? # This is interpreted as a list !
906
get_bool = conf.get_user_option_as_bool
907
self.assertEqual(True, get_bool('a_true_bool'))
908
self.assertEqual(False, get_bool('a_false_bool'))
911
warnings.append(args[0] % args[1:])
912
self.overrideAttr(trace, 'warning', warning)
913
msg = 'Value "%s" is not a boolean for "%s"'
914
self.assertIs(None, get_bool('an_invalid_bool'))
915
self.assertEquals(msg % ('maybe', 'an_invalid_bool'), warnings[0])
917
self.assertIs(None, get_bool('not_defined_in_this_config'))
918
self.assertEquals([], warnings)
920
def test_get_user_option_as_list(self):
921
conf, parser = self.make_config_parser("""
926
get_list = conf.get_user_option_as_list
927
self.assertEqual(['a', 'b', 'c'], get_list('a_list'))
928
self.assertEqual(['1'], get_list('length_1'))
929
self.assertEqual('x', conf.get_user_option('one_item'))
930
# automatically cast to list
931
self.assertEqual(['x'], get_list('one_item'))
934
class TestSupressWarning(TestIniConfig):
936
def make_warnings_config(self, s):
937
conf, parser = self.make_config_parser(s)
938
return conf.suppress_warning
940
def test_suppress_warning_unknown(self):
941
suppress_warning = self.make_warnings_config('')
942
self.assertEqual(False, suppress_warning('unknown_warning'))
944
def test_suppress_warning_known(self):
945
suppress_warning = self.make_warnings_config('suppress_warnings=a,b')
946
self.assertEqual(False, suppress_warning('c'))
947
self.assertEqual(True, suppress_warning('a'))
948
self.assertEqual(True, suppress_warning('b'))
951
class TestGetConfig(tests.TestCase):
953
def test_constructs(self):
954
my_config = config.GlobalConfig()
956
def test_calls_read_filenames(self):
957
# replace the class that is constructed, to check its parameters
958
oldparserclass = config.ConfigObj
959
config.ConfigObj = InstrumentedConfigObj
960
my_config = config.GlobalConfig()
962
parser = my_config._get_parser()
964
config.ConfigObj = oldparserclass
965
self.assertIsInstance(parser, InstrumentedConfigObj)
966
self.assertEqual(parser._calls, [('__init__', config.config_filename(),
970
class TestBranchConfig(tests.TestCaseWithTransport):
972
def test_constructs(self):
973
branch = FakeBranch()
974
my_config = config.BranchConfig(branch)
975
self.assertRaises(TypeError, config.BranchConfig)
977
def test_get_location_config(self):
978
branch = FakeBranch()
979
my_config = config.BranchConfig(branch)
980
location_config = my_config._get_location_config()
981
self.assertEqual(branch.base, location_config.location)
982
self.assertIs(location_config, my_config._get_location_config())
984
def test_get_config(self):
985
"""The Branch.get_config method works properly"""
986
b = bzrdir.BzrDir.create_standalone_workingtree('.').branch
987
my_config = b.get_config()
988
self.assertIs(my_config.get_user_option('wacky'), None)
989
my_config.set_user_option('wacky', 'unlikely')
990
self.assertEqual(my_config.get_user_option('wacky'), 'unlikely')
992
# Ensure we get the same thing if we start again
993
b2 = branch.Branch.open('.')
994
my_config2 = b2.get_config()
995
self.assertEqual(my_config2.get_user_option('wacky'), 'unlikely')
997
def test_has_explicit_nickname(self):
998
b = self.make_branch('.')
999
self.assertFalse(b.get_config().has_explicit_nickname())
1001
self.assertTrue(b.get_config().has_explicit_nickname())
1003
def test_config_url(self):
1004
"""The Branch.get_config will use section that uses a local url"""
1005
branch = self.make_branch('branch')
1006
self.assertEqual('branch', branch.nick)
1008
local_url = urlutils.local_path_to_url('branch')
1009
conf = config.LocationConfig.from_string(
1010
'[%s]\nnickname = foobar' % (local_url,),
1011
local_url, save=True)
1012
self.assertEqual('foobar', branch.nick)
1014
def test_config_local_path(self):
1015
"""The Branch.get_config will use a local system path"""
1016
branch = self.make_branch('branch')
1017
self.assertEqual('branch', branch.nick)
1019
local_path = osutils.getcwd().encode('utf8')
1020
conf = config.LocationConfig.from_string(
1021
'[%s/branch]\nnickname = barry' % (local_path,),
1022
'branch', save=True)
1023
self.assertEqual('barry', branch.nick)
1025
def test_config_creates_local(self):
1026
"""Creating a new entry in config uses a local path."""
1027
branch = self.make_branch('branch', format='knit')
1028
branch.set_push_location('http://foobar')
1029
local_path = osutils.getcwd().encode('utf8')
1030
# Surprisingly ConfigObj doesn't create a trailing newline
1031
self.check_file_contents(config.locations_config_filename(),
1033
'push_location = http://foobar\n'
1034
'push_location:policy = norecurse\n'
1037
def test_autonick_urlencoded(self):
1038
b = self.make_branch('!repo')
1039
self.assertEqual('!repo', b.get_config().get_nickname())
1041
def test_warn_if_masked(self):
1044
warnings.append(args[0] % args[1:])
1045
self.overrideAttr(trace, 'warning', warning)
1047
def set_option(store, warn_masked=True):
1049
conf.set_user_option('example_option', repr(store), store=store,
1050
warn_masked=warn_masked)
1051
def assertWarning(warning):
1053
self.assertEqual(0, len(warnings))
1055
self.assertEqual(1, len(warnings))
1056
self.assertEqual(warning, warnings[0])
1057
branch = self.make_branch('.')
1058
conf = branch.get_config()
1059
set_option(config.STORE_GLOBAL)
1061
set_option(config.STORE_BRANCH)
1063
set_option(config.STORE_GLOBAL)
1064
assertWarning('Value "4" is masked by "3" from branch.conf')
1065
set_option(config.STORE_GLOBAL, warn_masked=False)
1067
set_option(config.STORE_LOCATION)
1069
set_option(config.STORE_BRANCH)
1070
assertWarning('Value "3" is masked by "0" from locations.conf')
1071
set_option(config.STORE_BRANCH, warn_masked=False)
1075
class TestGlobalConfigItems(tests.TestCaseInTempDir):
1077
def test_user_id(self):
1078
my_config = config.GlobalConfig.from_string(sample_config_text)
1079
self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
1080
my_config._get_user_id())
1082
def test_absent_user_id(self):
1083
my_config = config.GlobalConfig()
1084
self.assertEqual(None, my_config._get_user_id())
1086
def test_configured_editor(self):
1087
my_config = config.GlobalConfig.from_string(sample_config_text)
1088
self.assertEqual("vim", my_config.get_editor())
1090
def test_signatures_always(self):
1091
my_config = config.GlobalConfig.from_string(sample_always_signatures)
1092
self.assertEqual(config.CHECK_NEVER,
1093
my_config.signature_checking())
1094
self.assertEqual(config.SIGN_ALWAYS,
1095
my_config.signing_policy())
1096
self.assertEqual(True, my_config.signature_needed())
1098
def test_signatures_if_possible(self):
1099
my_config = config.GlobalConfig.from_string(sample_maybe_signatures)
1100
self.assertEqual(config.CHECK_NEVER,
1101
my_config.signature_checking())
1102
self.assertEqual(config.SIGN_WHEN_REQUIRED,
1103
my_config.signing_policy())
1104
self.assertEqual(False, my_config.signature_needed())
1106
def test_signatures_ignore(self):
1107
my_config = config.GlobalConfig.from_string(sample_ignore_signatures)
1108
self.assertEqual(config.CHECK_ALWAYS,
1109
my_config.signature_checking())
1110
self.assertEqual(config.SIGN_NEVER,
1111
my_config.signing_policy())
1112
self.assertEqual(False, my_config.signature_needed())
1114
def _get_sample_config(self):
1115
my_config = config.GlobalConfig.from_string(sample_config_text)
1118
def test_gpg_signing_command(self):
1119
my_config = self._get_sample_config()
1120
self.assertEqual("gnome-gpg", my_config.gpg_signing_command())
1121
self.assertEqual(False, my_config.signature_needed())
1123
def _get_empty_config(self):
1124
my_config = config.GlobalConfig()
1127
def test_gpg_signing_command_unset(self):
1128
my_config = self._get_empty_config()
1129
self.assertEqual("gpg", my_config.gpg_signing_command())
1131
def test_get_user_option_default(self):
1132
my_config = self._get_empty_config()
1133
self.assertEqual(None, my_config.get_user_option('no_option'))
1135
def test_get_user_option_global(self):
1136
my_config = self._get_sample_config()
1137
self.assertEqual("something",
1138
my_config.get_user_option('user_global_option'))
1140
def test_post_commit_default(self):
1141
my_config = self._get_sample_config()
1142
self.assertEqual(None, my_config.post_commit())
1144
def test_configured_logformat(self):
1145
my_config = self._get_sample_config()
1146
self.assertEqual("short", my_config.log_format())
1148
def test_get_alias(self):
1149
my_config = self._get_sample_config()
1150
self.assertEqual('help', my_config.get_alias('h'))
1152
def test_get_aliases(self):
1153
my_config = self._get_sample_config()
1154
aliases = my_config.get_aliases()
1155
self.assertEqual(2, len(aliases))
1156
sorted_keys = sorted(aliases)
1157
self.assertEqual('help', aliases[sorted_keys[0]])
1158
self.assertEqual(sample_long_alias, aliases[sorted_keys[1]])
1160
def test_get_no_alias(self):
1161
my_config = self._get_sample_config()
1162
self.assertEqual(None, my_config.get_alias('foo'))
1164
def test_get_long_alias(self):
1165
my_config = self._get_sample_config()
1166
self.assertEqual(sample_long_alias, my_config.get_alias('ll'))
1168
def test_get_change_editor(self):
1169
my_config = self._get_sample_config()
1170
change_editor = my_config.get_change_editor('old', 'new')
1171
self.assertIs(diff.DiffFromTool, change_editor.__class__)
1172
self.assertEqual('vimdiff -of @new_path @old_path',
1173
' '.join(change_editor.command_template))
1175
def test_get_no_change_editor(self):
1176
my_config = self._get_empty_config()
1177
change_editor = my_config.get_change_editor('old', 'new')
1178
self.assertIs(None, change_editor)
1180
def test_get_merge_tools(self):
1181
conf = self._get_sample_config()
1182
tools = conf.get_merge_tools()
1183
self.log(repr(tools))
1185
{u'funkytool' : u'funkytool "arg with spaces" {this_temp}',
1186
u'sometool' : u'sometool {base} {this} {other} -o {result}'},
1189
def test_get_merge_tools_empty(self):
1190
conf = self._get_empty_config()
1191
tools = conf.get_merge_tools()
1192
self.assertEqual({}, tools)
1194
def test_find_merge_tool(self):
1195
conf = self._get_sample_config()
1196
cmdline = conf.find_merge_tool('sometool')
1197
self.assertEqual('sometool {base} {this} {other} -o {result}', cmdline)
1199
def test_find_merge_tool_not_found(self):
1200
conf = self._get_sample_config()
1201
cmdline = conf.find_merge_tool('DOES NOT EXIST')
1202
self.assertIs(cmdline, None)
1204
def test_find_merge_tool_known(self):
1205
conf = self._get_empty_config()
1206
cmdline = conf.find_merge_tool('kdiff3')
1207
self.assertEquals('kdiff3 {base} {this} {other} -o {result}', cmdline)
1209
def test_find_merge_tool_override_known(self):
1210
conf = self._get_empty_config()
1211
conf.set_user_option('bzr.mergetool.kdiff3', 'kdiff3 blah')
1212
cmdline = conf.find_merge_tool('kdiff3')
1213
self.assertEqual('kdiff3 blah', cmdline)
1216
class TestGlobalConfigSavingOptions(tests.TestCaseInTempDir):
1218
def test_empty(self):
1219
my_config = config.GlobalConfig()
1220
self.assertEqual(0, len(my_config.get_aliases()))
1222
def test_set_alias(self):
1223
my_config = config.GlobalConfig()
1224
alias_value = 'commit --strict'
1225
my_config.set_alias('commit', alias_value)
1226
new_config = config.GlobalConfig()
1227
self.assertEqual(alias_value, new_config.get_alias('commit'))
1229
def test_remove_alias(self):
1230
my_config = config.GlobalConfig()
1231
my_config.set_alias('commit', 'commit --strict')
1232
# Now remove the alias again.
1233
my_config.unset_alias('commit')
1234
new_config = config.GlobalConfig()
1235
self.assertIs(None, new_config.get_alias('commit'))
1238
class TestLocationConfig(tests.TestCaseInTempDir, TestOptionsMixin):
1240
def test_constructs(self):
1241
my_config = config.LocationConfig('http://example.com')
1242
self.assertRaises(TypeError, config.LocationConfig)
1244
def test_branch_calls_read_filenames(self):
1245
# This is testing the correct file names are provided.
1246
# TODO: consolidate with the test for GlobalConfigs filename checks.
1248
# replace the class that is constructed, to check its parameters
1249
oldparserclass = config.ConfigObj
1250
config.ConfigObj = InstrumentedConfigObj
1252
my_config = config.LocationConfig('http://www.example.com')
1253
parser = my_config._get_parser()
1255
config.ConfigObj = oldparserclass
1256
self.assertIsInstance(parser, InstrumentedConfigObj)
1257
self.assertEqual(parser._calls,
1258
[('__init__', config.locations_config_filename(),
1261
def test_get_global_config(self):
1262
my_config = config.BranchConfig(FakeBranch('http://example.com'))
1263
global_config = my_config._get_global_config()
1264
self.assertIsInstance(global_config, config.GlobalConfig)
1265
self.assertIs(global_config, my_config._get_global_config())
1267
def assertLocationMatching(self, expected):
1268
self.assertEqual(expected,
1269
list(self.my_location_config._get_matching_sections()))
1271
def test__get_matching_sections_no_match(self):
1272
self.get_branch_config('/')
1273
self.assertLocationMatching([])
1275
def test__get_matching_sections_exact(self):
1276
self.get_branch_config('http://www.example.com')
1277
self.assertLocationMatching([('http://www.example.com', '')])
1279
def test__get_matching_sections_suffix_does_not(self):
1280
self.get_branch_config('http://www.example.com-com')
1281
self.assertLocationMatching([])
1283
def test__get_matching_sections_subdir_recursive(self):
1284
self.get_branch_config('http://www.example.com/com')
1285
self.assertLocationMatching([('http://www.example.com', 'com')])
1287
def test__get_matching_sections_ignoreparent(self):
1288
self.get_branch_config('http://www.example.com/ignoreparent')
1289
self.assertLocationMatching([('http://www.example.com/ignoreparent',
1292
def test__get_matching_sections_ignoreparent_subdir(self):
1293
self.get_branch_config(
1294
'http://www.example.com/ignoreparent/childbranch')
1295
self.assertLocationMatching([('http://www.example.com/ignoreparent',
1298
def test__get_matching_sections_subdir_trailing_slash(self):
1299
self.get_branch_config('/b')
1300
self.assertLocationMatching([('/b/', '')])
1302
def test__get_matching_sections_subdir_child(self):
1303
self.get_branch_config('/a/foo')
1304
self.assertLocationMatching([('/a/*', ''), ('/a/', 'foo')])
1306
def test__get_matching_sections_subdir_child_child(self):
1307
self.get_branch_config('/a/foo/bar')
1308
self.assertLocationMatching([('/a/*', 'bar'), ('/a/', 'foo/bar')])
1310
def test__get_matching_sections_trailing_slash_with_children(self):
1311
self.get_branch_config('/a/')
1312
self.assertLocationMatching([('/a/', '')])
1314
def test__get_matching_sections_explicit_over_glob(self):
1315
# XXX: 2006-09-08 jamesh
1316
# This test only passes because ord('c') > ord('*'). If there
1317
# was a config section for '/a/?', it would get precedence
1319
self.get_branch_config('/a/c')
1320
self.assertLocationMatching([('/a/c', ''), ('/a/*', ''), ('/a/', 'c')])
1322
def test__get_option_policy_normal(self):
1323
self.get_branch_config('http://www.example.com')
1325
self.my_location_config._get_config_policy(
1326
'http://www.example.com', 'normal_option'),
1329
def test__get_option_policy_norecurse(self):
1330
self.get_branch_config('http://www.example.com')
1332
self.my_location_config._get_option_policy(
1333
'http://www.example.com', 'norecurse_option'),
1334
config.POLICY_NORECURSE)
1335
# Test old recurse=False setting:
1337
self.my_location_config._get_option_policy(
1338
'http://www.example.com/norecurse', 'normal_option'),
1339
config.POLICY_NORECURSE)
1341
def test__get_option_policy_normal(self):
1342
self.get_branch_config('http://www.example.com')
1344
self.my_location_config._get_option_policy(
1345
'http://www.example.com', 'appendpath_option'),
1346
config.POLICY_APPENDPATH)
1348
def test__get_options_with_policy(self):
1349
self.get_branch_config('/dir/subdir',
1350
location_config="""\
1352
other_url = /other-dir
1353
other_url:policy = appendpath
1355
other_url = /other-subdir
1358
[(u'other_url', u'/other-subdir', u'/dir/subdir', 'locations'),
1359
(u'other_url', u'/other-dir', u'/dir', 'locations'),
1360
(u'other_url:policy', u'appendpath', u'/dir', 'locations')],
1361
self.my_location_config)
1363
def test_location_without_username(self):
1364
self.get_branch_config('http://www.example.com/ignoreparent')
1365
self.assertEqual(u'Erik B\u00e5gfors <erik@bagfors.nu>',
1366
self.my_config.username())
1368
def test_location_not_listed(self):
1369
"""Test that the global username is used when no location matches"""
1370
self.get_branch_config('/home/robertc/sources')
1371
self.assertEqual(u'Erik B\u00e5gfors <erik@bagfors.nu>',
1372
self.my_config.username())
1374
def test_overriding_location(self):
1375
self.get_branch_config('http://www.example.com/foo')
1376
self.assertEqual('Robert Collins <robertc@example.org>',
1377
self.my_config.username())
1379
def test_signatures_not_set(self):
1380
self.get_branch_config('http://www.example.com',
1381
global_config=sample_ignore_signatures)
1382
self.assertEqual(config.CHECK_ALWAYS,
1383
self.my_config.signature_checking())
1384
self.assertEqual(config.SIGN_NEVER,
1385
self.my_config.signing_policy())
1387
def test_signatures_never(self):
1388
self.get_branch_config('/a/c')
1389
self.assertEqual(config.CHECK_NEVER,
1390
self.my_config.signature_checking())
1392
def test_signatures_when_available(self):
1393
self.get_branch_config('/a/', global_config=sample_ignore_signatures)
1394
self.assertEqual(config.CHECK_IF_POSSIBLE,
1395
self.my_config.signature_checking())
1397
def test_signatures_always(self):
1398
self.get_branch_config('/b')
1399
self.assertEqual(config.CHECK_ALWAYS,
1400
self.my_config.signature_checking())
1402
def test_gpg_signing_command(self):
1403
self.get_branch_config('/b')
1404
self.assertEqual("gnome-gpg", self.my_config.gpg_signing_command())
1406
def test_gpg_signing_command_missing(self):
1407
self.get_branch_config('/a')
1408
self.assertEqual("false", self.my_config.gpg_signing_command())
1410
def test_get_user_option_global(self):
1411
self.get_branch_config('/a')
1412
self.assertEqual('something',
1413
self.my_config.get_user_option('user_global_option'))
1415
def test_get_user_option_local(self):
1416
self.get_branch_config('/a')
1417
self.assertEqual('local',
1418
self.my_config.get_user_option('user_local_option'))
1420
def test_get_user_option_appendpath(self):
1421
# returned as is for the base path:
1422
self.get_branch_config('http://www.example.com')
1423
self.assertEqual('append',
1424
self.my_config.get_user_option('appendpath_option'))
1425
# Extra path components get appended:
1426
self.get_branch_config('http://www.example.com/a/b/c')
1427
self.assertEqual('append/a/b/c',
1428
self.my_config.get_user_option('appendpath_option'))
1429
# Overriden for http://www.example.com/dir, where it is a
1431
self.get_branch_config('http://www.example.com/dir/a/b/c')
1432
self.assertEqual('normal',
1433
self.my_config.get_user_option('appendpath_option'))
1435
def test_get_user_option_norecurse(self):
1436
self.get_branch_config('http://www.example.com')
1437
self.assertEqual('norecurse',
1438
self.my_config.get_user_option('norecurse_option'))
1439
self.get_branch_config('http://www.example.com/dir')
1440
self.assertEqual(None,
1441
self.my_config.get_user_option('norecurse_option'))
1442
# http://www.example.com/norecurse is a recurse=False section
1443
# that redefines normal_option. Subdirectories do not pick up
1444
# this redefinition.
1445
self.get_branch_config('http://www.example.com/norecurse')
1446
self.assertEqual('norecurse',
1447
self.my_config.get_user_option('normal_option'))
1448
self.get_branch_config('http://www.example.com/norecurse/subdir')
1449
self.assertEqual('normal',
1450
self.my_config.get_user_option('normal_option'))
1452
def test_set_user_option_norecurse(self):
1453
self.get_branch_config('http://www.example.com')
1454
self.my_config.set_user_option('foo', 'bar',
1455
store=config.STORE_LOCATION_NORECURSE)
1457
self.my_location_config._get_option_policy(
1458
'http://www.example.com', 'foo'),
1459
config.POLICY_NORECURSE)
1461
def test_set_user_option_appendpath(self):
1462
self.get_branch_config('http://www.example.com')
1463
self.my_config.set_user_option('foo', 'bar',
1464
store=config.STORE_LOCATION_APPENDPATH)
1466
self.my_location_config._get_option_policy(
1467
'http://www.example.com', 'foo'),
1468
config.POLICY_APPENDPATH)
1470
def test_set_user_option_change_policy(self):
1471
self.get_branch_config('http://www.example.com')
1472
self.my_config.set_user_option('norecurse_option', 'normal',
1473
store=config.STORE_LOCATION)
1475
self.my_location_config._get_option_policy(
1476
'http://www.example.com', 'norecurse_option'),
1479
def test_set_user_option_recurse_false_section(self):
1480
# The following section has recurse=False set. The test is to
1481
# make sure that a normal option can be added to the section,
1482
# converting recurse=False to the norecurse policy.
1483
self.get_branch_config('http://www.example.com/norecurse')
1484
self.callDeprecated(['The recurse option is deprecated as of 0.14. '
1485
'The section "http://www.example.com/norecurse" '
1486
'has been converted to use policies.'],
1487
self.my_config.set_user_option,
1488
'foo', 'bar', store=config.STORE_LOCATION)
1490
self.my_location_config._get_option_policy(
1491
'http://www.example.com/norecurse', 'foo'),
1493
# The previously existing option is still norecurse:
1495
self.my_location_config._get_option_policy(
1496
'http://www.example.com/norecurse', 'normal_option'),
1497
config.POLICY_NORECURSE)
1499
def test_post_commit_default(self):
1500
self.get_branch_config('/a/c')
1501
self.assertEqual('bzrlib.tests.test_config.post_commit',
1502
self.my_config.post_commit())
1504
def get_branch_config(self, location, global_config=None,
1505
location_config=None):
1506
my_branch = FakeBranch(location)
1507
if global_config is None:
1508
global_config = sample_config_text
1509
if location_config is None:
1510
location_config = sample_branches_text
1512
my_global_config = config.GlobalConfig.from_string(global_config,
1514
my_location_config = config.LocationConfig.from_string(
1515
location_config, my_branch.base, save=True)
1516
my_config = config.BranchConfig(my_branch)
1517
self.my_config = my_config
1518
self.my_location_config = my_config._get_location_config()
1520
def test_set_user_setting_sets_and_saves(self):
1521
self.get_branch_config('/a/c')
1522
record = InstrumentedConfigObj("foo")
1523
self.my_location_config._parser = record
1525
self.callDeprecated(['The recurse option is deprecated as of '
1526
'0.14. The section "/a/c" has been '
1527
'converted to use policies.'],
1528
self.my_config.set_user_option,
1529
'foo', 'bar', store=config.STORE_LOCATION)
1530
self.assertEqual([('reload',),
1531
('__contains__', '/a/c'),
1532
('__contains__', '/a/c/'),
1533
('__setitem__', '/a/c', {}),
1534
('__getitem__', '/a/c'),
1535
('__setitem__', 'foo', 'bar'),
1536
('__getitem__', '/a/c'),
1537
('as_bool', 'recurse'),
1538
('__getitem__', '/a/c'),
1539
('__delitem__', 'recurse'),
1540
('__getitem__', '/a/c'),
1542
('__getitem__', '/a/c'),
1543
('__contains__', 'foo:policy'),
1547
def test_set_user_setting_sets_and_saves2(self):
1548
self.get_branch_config('/a/c')
1549
self.assertIs(self.my_config.get_user_option('foo'), None)
1550
self.my_config.set_user_option('foo', 'bar')
1552
self.my_config.branch.control_files.files['branch.conf'].strip(),
1554
self.assertEqual(self.my_config.get_user_option('foo'), 'bar')
1555
self.my_config.set_user_option('foo', 'baz',
1556
store=config.STORE_LOCATION)
1557
self.assertEqual(self.my_config.get_user_option('foo'), 'baz')
1558
self.my_config.set_user_option('foo', 'qux')
1559
self.assertEqual(self.my_config.get_user_option('foo'), 'baz')
1561
def test_get_bzr_remote_path(self):
1562
my_config = config.LocationConfig('/a/c')
1563
self.assertEqual('bzr', my_config.get_bzr_remote_path())
1564
my_config.set_user_option('bzr_remote_path', '/path-bzr')
1565
self.assertEqual('/path-bzr', my_config.get_bzr_remote_path())
1566
self.overrideEnv('BZR_REMOTE_PATH', '/environ-bzr')
1567
self.assertEqual('/environ-bzr', my_config.get_bzr_remote_path())
1570
precedence_global = 'option = global'
1571
precedence_branch = 'option = branch'
1572
precedence_location = """
1576
[http://example.com/specific]
1580
class TestBranchConfigItems(tests.TestCaseInTempDir):
1582
def get_branch_config(self, global_config=None, location=None,
1583
location_config=None, branch_data_config=None):
1584
my_branch = FakeBranch(location)
1585
if global_config is not None:
1586
my_global_config = config.GlobalConfig.from_string(global_config,
1588
if location_config is not None:
1589
my_location_config = config.LocationConfig.from_string(
1590
location_config, my_branch.base, save=True)
1591
my_config = config.BranchConfig(my_branch)
1592
if branch_data_config is not None:
1593
my_config.branch.control_files.files['branch.conf'] = \
1597
def test_user_id(self):
1598
branch = FakeBranch(user_id='Robert Collins <robertc@example.net>')
1599
my_config = config.BranchConfig(branch)
1600
self.assertEqual("Robert Collins <robertc@example.net>",
1601
my_config.username())
1602
my_config.branch.control_files.files['email'] = "John"
1603
my_config.set_user_option('email',
1604
"Robert Collins <robertc@example.org>")
1605
self.assertEqual("John", my_config.username())
1606
del my_config.branch.control_files.files['email']
1607
self.assertEqual("Robert Collins <robertc@example.org>",
1608
my_config.username())
1610
def test_not_set_in_branch(self):
1611
my_config = self.get_branch_config(global_config=sample_config_text)
1612
self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
1613
my_config._get_user_id())
1614
my_config.branch.control_files.files['email'] = "John"
1615
self.assertEqual("John", my_config._get_user_id())
1617
def test_BZR_EMAIL_OVERRIDES(self):
1618
self.overrideEnv('BZR_EMAIL', "Robert Collins <robertc@example.org>")
1619
branch = FakeBranch()
1620
my_config = config.BranchConfig(branch)
1621
self.assertEqual("Robert Collins <robertc@example.org>",
1622
my_config.username())
1624
def test_signatures_forced(self):
1625
my_config = self.get_branch_config(
1626
global_config=sample_always_signatures)
1627
self.assertEqual(config.CHECK_NEVER, my_config.signature_checking())
1628
self.assertEqual(config.SIGN_ALWAYS, my_config.signing_policy())
1629
self.assertTrue(my_config.signature_needed())
1631
def test_signatures_forced_branch(self):
1632
my_config = self.get_branch_config(
1633
global_config=sample_ignore_signatures,
1634
branch_data_config=sample_always_signatures)
1635
self.assertEqual(config.CHECK_NEVER, my_config.signature_checking())
1636
self.assertEqual(config.SIGN_ALWAYS, my_config.signing_policy())
1637
self.assertTrue(my_config.signature_needed())
1639
def test_gpg_signing_command(self):
1640
my_config = self.get_branch_config(
1641
global_config=sample_config_text,
1642
# branch data cannot set gpg_signing_command
1643
branch_data_config="gpg_signing_command=pgp")
1644
self.assertEqual('gnome-gpg', my_config.gpg_signing_command())
1646
def test_get_user_option_global(self):
1647
my_config = self.get_branch_config(global_config=sample_config_text)
1648
self.assertEqual('something',
1649
my_config.get_user_option('user_global_option'))
1651
def test_post_commit_default(self):
1652
my_config = self.get_branch_config(global_config=sample_config_text,
1654
location_config=sample_branches_text)
1655
self.assertEqual(my_config.branch.base, '/a/c')
1656
self.assertEqual('bzrlib.tests.test_config.post_commit',
1657
my_config.post_commit())
1658
my_config.set_user_option('post_commit', 'rmtree_root')
1659
# post-commit is ignored when present in branch data
1660
self.assertEqual('bzrlib.tests.test_config.post_commit',
1661
my_config.post_commit())
1662
my_config.set_user_option('post_commit', 'rmtree_root',
1663
store=config.STORE_LOCATION)
1664
self.assertEqual('rmtree_root', my_config.post_commit())
1666
def test_config_precedence(self):
1667
# FIXME: eager test, luckily no persitent config file makes it fail
1669
my_config = self.get_branch_config(global_config=precedence_global)
1670
self.assertEqual(my_config.get_user_option('option'), 'global')
1671
my_config = self.get_branch_config(global_config=precedence_global,
1672
branch_data_config=precedence_branch)
1673
self.assertEqual(my_config.get_user_option('option'), 'branch')
1674
my_config = self.get_branch_config(
1675
global_config=precedence_global,
1676
branch_data_config=precedence_branch,
1677
location_config=precedence_location)
1678
self.assertEqual(my_config.get_user_option('option'), 'recurse')
1679
my_config = self.get_branch_config(
1680
global_config=precedence_global,
1681
branch_data_config=precedence_branch,
1682
location_config=precedence_location,
1683
location='http://example.com/specific')
1684
self.assertEqual(my_config.get_user_option('option'), 'exact')
1686
def test_get_mail_client(self):
1687
config = self.get_branch_config()
1688
client = config.get_mail_client()
1689
self.assertIsInstance(client, mail_client.DefaultMail)
1692
config.set_user_option('mail_client', 'evolution')
1693
client = config.get_mail_client()
1694
self.assertIsInstance(client, mail_client.Evolution)
1696
config.set_user_option('mail_client', 'kmail')
1697
client = config.get_mail_client()
1698
self.assertIsInstance(client, mail_client.KMail)
1700
config.set_user_option('mail_client', 'mutt')
1701
client = config.get_mail_client()
1702
self.assertIsInstance(client, mail_client.Mutt)
1704
config.set_user_option('mail_client', 'thunderbird')
1705
client = config.get_mail_client()
1706
self.assertIsInstance(client, mail_client.Thunderbird)
1709
config.set_user_option('mail_client', 'default')
1710
client = config.get_mail_client()
1711
self.assertIsInstance(client, mail_client.DefaultMail)
1713
config.set_user_option('mail_client', 'editor')
1714
client = config.get_mail_client()
1715
self.assertIsInstance(client, mail_client.Editor)
1717
config.set_user_option('mail_client', 'mapi')
1718
client = config.get_mail_client()
1719
self.assertIsInstance(client, mail_client.MAPIClient)
1721
config.set_user_option('mail_client', 'xdg-email')
1722
client = config.get_mail_client()
1723
self.assertIsInstance(client, mail_client.XDGEmail)
1725
config.set_user_option('mail_client', 'firebird')
1726
self.assertRaises(errors.UnknownMailClient, config.get_mail_client)
1729
class TestMailAddressExtraction(tests.TestCase):
1731
def test_extract_email_address(self):
1732
self.assertEqual('jane@test.com',
1733
config.extract_email_address('Jane <jane@test.com>'))
1734
self.assertRaises(errors.NoEmailInUsername,
1735
config.extract_email_address, 'Jane Tester')
1737
def test_parse_username(self):
1738
self.assertEqual(('', 'jdoe@example.com'),
1739
config.parse_username('jdoe@example.com'))
1740
self.assertEqual(('', 'jdoe@example.com'),
1741
config.parse_username('<jdoe@example.com>'))
1742
self.assertEqual(('John Doe', 'jdoe@example.com'),
1743
config.parse_username('John Doe <jdoe@example.com>'))
1744
self.assertEqual(('John Doe', ''),
1745
config.parse_username('John Doe'))
1746
self.assertEqual(('John Doe', 'jdoe@example.com'),
1747
config.parse_username('John Doe jdoe@example.com'))
1749
class TestTreeConfig(tests.TestCaseWithTransport):
1751
def test_get_value(self):
1752
"""Test that retreiving a value from a section is possible"""
1753
branch = self.make_branch('.')
1754
tree_config = config.TreeConfig(branch)
1755
tree_config.set_option('value', 'key', 'SECTION')
1756
tree_config.set_option('value2', 'key2')
1757
tree_config.set_option('value3-top', 'key3')
1758
tree_config.set_option('value3-section', 'key3', 'SECTION')
1759
value = tree_config.get_option('key', 'SECTION')
1760
self.assertEqual(value, 'value')
1761
value = tree_config.get_option('key2')
1762
self.assertEqual(value, 'value2')
1763
self.assertEqual(tree_config.get_option('non-existant'), None)
1764
value = tree_config.get_option('non-existant', 'SECTION')
1765
self.assertEqual(value, None)
1766
value = tree_config.get_option('non-existant', default='default')
1767
self.assertEqual(value, 'default')
1768
self.assertEqual(tree_config.get_option('key2', 'NOSECTION'), None)
1769
value = tree_config.get_option('key2', 'NOSECTION', default='default')
1770
self.assertEqual(value, 'default')
1771
value = tree_config.get_option('key3')
1772
self.assertEqual(value, 'value3-top')
1773
value = tree_config.get_option('key3', 'SECTION')
1774
self.assertEqual(value, 'value3-section')
1777
class TestTransportConfig(tests.TestCaseWithTransport):
1779
def test_get_value(self):
1780
"""Test that retreiving a value from a section is possible"""
1781
bzrdir_config = config.TransportConfig(transport.get_transport('.'),
1783
bzrdir_config.set_option('value', 'key', 'SECTION')
1784
bzrdir_config.set_option('value2', 'key2')
1785
bzrdir_config.set_option('value3-top', 'key3')
1786
bzrdir_config.set_option('value3-section', 'key3', 'SECTION')
1787
value = bzrdir_config.get_option('key', 'SECTION')
1788
self.assertEqual(value, 'value')
1789
value = bzrdir_config.get_option('key2')
1790
self.assertEqual(value, 'value2')
1791
self.assertEqual(bzrdir_config.get_option('non-existant'), None)
1792
value = bzrdir_config.get_option('non-existant', 'SECTION')
1793
self.assertEqual(value, None)
1794
value = bzrdir_config.get_option('non-existant', default='default')
1795
self.assertEqual(value, 'default')
1796
self.assertEqual(bzrdir_config.get_option('key2', 'NOSECTION'), None)
1797
value = bzrdir_config.get_option('key2', 'NOSECTION',
1799
self.assertEqual(value, 'default')
1800
value = bzrdir_config.get_option('key3')
1801
self.assertEqual(value, 'value3-top')
1802
value = bzrdir_config.get_option('key3', 'SECTION')
1803
self.assertEqual(value, 'value3-section')
1805
def test_set_unset_default_stack_on(self):
1806
my_dir = self.make_bzrdir('.')
1807
bzrdir_config = config.BzrDirConfig(my_dir)
1808
self.assertIs(None, bzrdir_config.get_default_stack_on())
1809
bzrdir_config.set_default_stack_on('Foo')
1810
self.assertEqual('Foo', bzrdir_config._config.get_option(
1811
'default_stack_on'))
1812
self.assertEqual('Foo', bzrdir_config.get_default_stack_on())
1813
bzrdir_config.set_default_stack_on(None)
1814
self.assertIs(None, bzrdir_config.get_default_stack_on())
1817
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
1820
super(TestConfigGetOptions, self).setUp()
1821
create_configs(self)
1823
# One variable in none of the above
1824
def test_no_variable(self):
1825
# Using branch should query branch, locations and bazaar
1826
self.assertOptions([], self.branch_config)
1828
def test_option_in_bazaar(self):
1829
self.bazaar_config.set_user_option('file', 'bazaar')
1830
self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
1833
def test_option_in_locations(self):
1834
self.locations_config.set_user_option('file', 'locations')
1836
[('file', 'locations', self.tree.basedir, 'locations')],
1837
self.locations_config)
1839
def test_option_in_branch(self):
1840
self.branch_config.set_user_option('file', 'branch')
1841
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
1844
def test_option_in_bazaar_and_branch(self):
1845
self.bazaar_config.set_user_option('file', 'bazaar')
1846
self.branch_config.set_user_option('file', 'branch')
1847
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
1848
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
1851
def test_option_in_branch_and_locations(self):
1852
# Hmm, locations override branch :-/
1853
self.locations_config.set_user_option('file', 'locations')
1854
self.branch_config.set_user_option('file', 'branch')
1856
[('file', 'locations', self.tree.basedir, 'locations'),
1857
('file', 'branch', 'DEFAULT', 'branch'),],
1860
def test_option_in_bazaar_locations_and_branch(self):
1861
self.bazaar_config.set_user_option('file', 'bazaar')
1862
self.locations_config.set_user_option('file', 'locations')
1863
self.branch_config.set_user_option('file', 'branch')
1865
[('file', 'locations', self.tree.basedir, 'locations'),
1866
('file', 'branch', 'DEFAULT', 'branch'),
1867
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
1871
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
1874
super(TestConfigRemoveOption, self).setUp()
1875
create_configs_with_file_option(self)
1877
def test_remove_in_locations(self):
1878
self.locations_config.remove_user_option('file', self.tree.basedir)
1880
[('file', 'branch', 'DEFAULT', 'branch'),
1881
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
1884
def test_remove_in_branch(self):
1885
self.branch_config.remove_user_option('file')
1887
[('file', 'locations', self.tree.basedir, 'locations'),
1888
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
1891
def test_remove_in_bazaar(self):
1892
self.bazaar_config.remove_user_option('file')
1894
[('file', 'locations', self.tree.basedir, 'locations'),
1895
('file', 'branch', 'DEFAULT', 'branch'),],
1899
class TestConfigGetSections(tests.TestCaseWithTransport):
1902
super(TestConfigGetSections, self).setUp()
1903
create_configs(self)
1905
def assertSectionNames(self, expected, conf, name=None):
1906
"""Check which sections are returned for a given config.
1908
If fallback configurations exist their sections can be included.
1910
:param expected: A list of section names.
1912
:param conf: The configuration that will be queried.
1914
:param name: An optional section name that will be passed to
1917
sections = list(conf._get_sections(name))
1918
self.assertLength(len(expected), sections)
1919
self.assertEqual(expected, [name for name, _, _ in sections])
1921
def test_bazaar_default_section(self):
1922
self.assertSectionNames(['DEFAULT'], self.bazaar_config)
1924
def test_locations_default_section(self):
1925
# No sections are defined in an empty file
1926
self.assertSectionNames([], self.locations_config)
1928
def test_locations_named_section(self):
1929
self.locations_config.set_user_option('file', 'locations')
1930
self.assertSectionNames([self.tree.basedir], self.locations_config)
1932
def test_locations_matching_sections(self):
1933
loc_config = self.locations_config
1934
loc_config.set_user_option('file', 'locations')
1935
# We need to cheat a bit here to create an option in sections above and
1936
# below the 'location' one.
1937
parser = loc_config._get_parser()
1938
# locations.cong deals with '/' ignoring native os.sep
1939
location_names = self.tree.basedir.split('/')
1940
parent = '/'.join(location_names[:-1])
1941
child = '/'.join(location_names + ['child'])
1943
parser[parent]['file'] = 'parent'
1945
parser[child]['file'] = 'child'
1946
self.assertSectionNames([self.tree.basedir, parent], loc_config)
1948
def test_branch_data_default_section(self):
1949
self.assertSectionNames([None],
1950
self.branch_config._get_branch_data_config())
1952
def test_branch_default_sections(self):
1953
# No sections are defined in an empty locations file
1954
self.assertSectionNames([None, 'DEFAULT'],
1956
# Unless we define an option
1957
self.branch_config._get_location_config().set_user_option(
1958
'file', 'locations')
1959
self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
1962
def test_bazaar_named_section(self):
1963
# We need to cheat as the API doesn't give direct access to sections
1964
# other than DEFAULT.
1965
self.bazaar_config.set_alias('bazaar', 'bzr')
1966
self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
1969
class TestAuthenticationConfigFile(tests.TestCase):
1970
"""Test the authentication.conf file matching"""
1972
def _got_user_passwd(self, expected_user, expected_password,
1973
config, *args, **kwargs):
1974
credentials = config.get_credentials(*args, **kwargs)
1975
if credentials is None:
1979
user = credentials['user']
1980
password = credentials['password']
1981
self.assertEquals(expected_user, user)
1982
self.assertEquals(expected_password, password)
1984
def test_empty_config(self):
1985
conf = config.AuthenticationConfig(_file=StringIO())
1986
self.assertEquals({}, conf._get_config())
1987
self._got_user_passwd(None, None, conf, 'http', 'foo.net')
1989
def test_missing_auth_section_header(self):
1990
conf = config.AuthenticationConfig(_file=StringIO('foo = bar'))
1991
self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
1993
def test_auth_section_header_not_closed(self):
1994
conf = config.AuthenticationConfig(_file=StringIO('[DEF'))
1995
self.assertRaises(errors.ParseConfigError, conf._get_config)
1997
def test_auth_value_not_boolean(self):
1998
conf = config.AuthenticationConfig(_file=StringIO(
2002
verify_certificates=askme # Error: Not a boolean
2004
self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
2006
def test_auth_value_not_int(self):
2007
conf = config.AuthenticationConfig(_file=StringIO(
2011
port=port # Error: Not an int
2013
self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
2015
def test_unknown_password_encoding(self):
2016
conf = config.AuthenticationConfig(_file=StringIO(
2020
password_encoding=unknown
2022
self.assertRaises(ValueError, conf.get_password,
2023
'ftp', 'foo.net', 'joe')
2025
def test_credentials_for_scheme_host(self):
2026
conf = config.AuthenticationConfig(_file=StringIO(
2027
"""# Identity on foo.net
2032
password=secret-pass
2035
self._got_user_passwd('joe', 'secret-pass', conf, 'ftp', 'foo.net')
2037
self._got_user_passwd(None, None, conf, 'http', 'foo.net')
2039
self._got_user_passwd(None, None, conf, 'ftp', 'bar.net')
2041
def test_credentials_for_host_port(self):
2042
conf = config.AuthenticationConfig(_file=StringIO(
2043
"""# Identity on foo.net
2049
password=secret-pass
2052
self._got_user_passwd('joe', 'secret-pass',
2053
conf, 'ftp', 'foo.net', port=10021)
2055
self._got_user_passwd(None, None, conf, 'ftp', 'foo.net')
2057
def test_for_matching_host(self):
2058
conf = config.AuthenticationConfig(_file=StringIO(
2059
"""# Identity on foo.net
2065
[sourceforge domain]
2072
self._got_user_passwd('georges', 'bendover',
2073
conf, 'bzr', 'foo.bzr.sf.net')
2075
self._got_user_passwd(None, None,
2076
conf, 'bzr', 'bbzr.sf.net')
2078
def test_for_matching_host_None(self):
2079
conf = config.AuthenticationConfig(_file=StringIO(
2080
"""# Identity on foo.net
2090
self._got_user_passwd('joe', 'joepass',
2091
conf, 'bzr', 'quux.net')
2092
# no host but different scheme
2093
self._got_user_passwd('georges', 'bendover',
2094
conf, 'ftp', 'quux.net')
2096
def test_credentials_for_path(self):
2097
conf = config.AuthenticationConfig(_file=StringIO(
2113
self._got_user_passwd(None, None,
2114
conf, 'http', host='bar.org', path='/dir3')
2116
self._got_user_passwd('georges', 'bendover',
2117
conf, 'http', host='bar.org', path='/dir2')
2119
self._got_user_passwd('jim', 'jimpass',
2120
conf, 'http', host='bar.org',path='/dir1/subdir')
2122
def test_credentials_for_user(self):
2123
conf = config.AuthenticationConfig(_file=StringIO(
2132
self._got_user_passwd('jim', 'jimpass',
2133
conf, 'http', 'bar.org')
2135
self._got_user_passwd('jim', 'jimpass',
2136
conf, 'http', 'bar.org', user='jim')
2137
# Don't get a different user if one is specified
2138
self._got_user_passwd(None, None,
2139
conf, 'http', 'bar.org', user='georges')
2141
def test_credentials_for_user_without_password(self):
2142
conf = config.AuthenticationConfig(_file=StringIO(
2149
# Get user but no password
2150
self._got_user_passwd('jim', None,
2151
conf, 'http', 'bar.org')
2153
def test_verify_certificates(self):
2154
conf = config.AuthenticationConfig(_file=StringIO(
2161
verify_certificates=False
2168
credentials = conf.get_credentials('https', 'bar.org')
2169
self.assertEquals(False, credentials.get('verify_certificates'))
2170
credentials = conf.get_credentials('https', 'foo.net')
2171
self.assertEquals(True, credentials.get('verify_certificates'))
2174
class TestAuthenticationStorage(tests.TestCaseInTempDir):
2176
def test_set_credentials(self):
2177
conf = config.AuthenticationConfig()
2178
conf.set_credentials('name', 'host', 'user', 'scheme', 'password',
2179
99, path='/foo', verify_certificates=False, realm='realm')
2180
credentials = conf.get_credentials(host='host', scheme='scheme',
2181
port=99, path='/foo',
2183
CREDENTIALS = {'name': 'name', 'user': 'user', 'password': 'password',
2184
'verify_certificates': False, 'scheme': 'scheme',
2185
'host': 'host', 'port': 99, 'path': '/foo',
2187
self.assertEqual(CREDENTIALS, credentials)
2188
credentials_from_disk = config.AuthenticationConfig().get_credentials(
2189
host='host', scheme='scheme', port=99, path='/foo', realm='realm')
2190
self.assertEqual(CREDENTIALS, credentials_from_disk)
2192
def test_reset_credentials_different_name(self):
2193
conf = config.AuthenticationConfig()
2194
conf.set_credentials('name', 'host', 'user', 'scheme', 'password'),
2195
conf.set_credentials('name2', 'host', 'user2', 'scheme', 'password'),
2196
self.assertIs(None, conf._get_config().get('name'))
2197
credentials = conf.get_credentials(host='host', scheme='scheme')
2198
CREDENTIALS = {'name': 'name2', 'user': 'user2', 'password':
2199
'password', 'verify_certificates': True,
2200
'scheme': 'scheme', 'host': 'host', 'port': None,
2201
'path': None, 'realm': None}
2202
self.assertEqual(CREDENTIALS, credentials)
2205
class TestAuthenticationConfig(tests.TestCase):
2206
"""Test AuthenticationConfig behaviour"""
2208
def _check_default_password_prompt(self, expected_prompt_format, scheme,
2209
host=None, port=None, realm=None,
2213
user, password = 'jim', 'precious'
2214
expected_prompt = expected_prompt_format % {
2215
'scheme': scheme, 'host': host, 'port': port,
2216
'user': user, 'realm': realm}
2218
stdout = tests.StringIOWrapper()
2219
stderr = tests.StringIOWrapper()
2220
ui.ui_factory = tests.TestUIFactory(stdin=password + '\n',
2221
stdout=stdout, stderr=stderr)
2222
# We use an empty conf so that the user is always prompted
2223
conf = config.AuthenticationConfig()
2224
self.assertEquals(password,
2225
conf.get_password(scheme, host, user, port=port,
2226
realm=realm, path=path))
2227
self.assertEquals(expected_prompt, stderr.getvalue())
2228
self.assertEquals('', stdout.getvalue())
2230
def _check_default_username_prompt(self, expected_prompt_format, scheme,
2231
host=None, port=None, realm=None,
2236
expected_prompt = expected_prompt_format % {
2237
'scheme': scheme, 'host': host, 'port': port,
2239
stdout = tests.StringIOWrapper()
2240
stderr = tests.StringIOWrapper()
2241
ui.ui_factory = tests.TestUIFactory(stdin=username+ '\n',
2242
stdout=stdout, stderr=stderr)
2243
# We use an empty conf so that the user is always prompted
2244
conf = config.AuthenticationConfig()
2245
self.assertEquals(username, conf.get_user(scheme, host, port=port,
2246
realm=realm, path=path, ask=True))
2247
self.assertEquals(expected_prompt, stderr.getvalue())
2248
self.assertEquals('', stdout.getvalue())
2250
def test_username_defaults_prompts(self):
2251
# HTTP prompts can't be tested here, see test_http.py
2252
self._check_default_username_prompt('FTP %(host)s username: ', 'ftp')
2253
self._check_default_username_prompt(
2254
'FTP %(host)s:%(port)d username: ', 'ftp', port=10020)
2255
self._check_default_username_prompt(
2256
'SSH %(host)s:%(port)d username: ', 'ssh', port=12345)
2258
def test_username_default_no_prompt(self):
2259
conf = config.AuthenticationConfig()
2260
self.assertEquals(None,
2261
conf.get_user('ftp', 'example.com'))
2262
self.assertEquals("explicitdefault",
2263
conf.get_user('ftp', 'example.com', default="explicitdefault"))
2265
def test_password_default_prompts(self):
2266
# HTTP prompts can't be tested here, see test_http.py
2267
self._check_default_password_prompt(
2268
'FTP %(user)s@%(host)s password: ', 'ftp')
2269
self._check_default_password_prompt(
2270
'FTP %(user)s@%(host)s:%(port)d password: ', 'ftp', port=10020)
2271
self._check_default_password_prompt(
2272
'SSH %(user)s@%(host)s:%(port)d password: ', 'ssh', port=12345)
2273
# SMTP port handling is a bit special (it's handled if embedded in the
2275
# FIXME: should we: forbid that, extend it to other schemes, leave
2276
# things as they are that's fine thank you ?
2277
self._check_default_password_prompt('SMTP %(user)s@%(host)s password: ',
2279
self._check_default_password_prompt('SMTP %(user)s@%(host)s password: ',
2280
'smtp', host='bar.org:10025')
2281
self._check_default_password_prompt(
2282
'SMTP %(user)s@%(host)s:%(port)d password: ',
2285
def test_ssh_password_emits_warning(self):
2286
conf = config.AuthenticationConfig(_file=StringIO(
2294
entered_password = 'typed-by-hand'
2295
stdout = tests.StringIOWrapper()
2296
stderr = tests.StringIOWrapper()
2297
ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
2298
stdout=stdout, stderr=stderr)
2300
# Since the password defined in the authentication config is ignored,
2301
# the user is prompted
2302
self.assertEquals(entered_password,
2303
conf.get_password('ssh', 'bar.org', user='jim'))
2304
self.assertContainsRe(
2306
'password ignored in section \[ssh with password\]')
2308
def test_ssh_without_password_doesnt_emit_warning(self):
2309
conf = config.AuthenticationConfig(_file=StringIO(
2316
entered_password = 'typed-by-hand'
2317
stdout = tests.StringIOWrapper()
2318
stderr = tests.StringIOWrapper()
2319
ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
2323
# Since the password defined in the authentication config is ignored,
2324
# the user is prompted
2325
self.assertEquals(entered_password,
2326
conf.get_password('ssh', 'bar.org', user='jim'))
2327
# No warning shoud be emitted since there is no password. We are only
2329
self.assertNotContainsRe(
2331
'password ignored in section \[ssh with password\]')
2333
def test_uses_fallback_stores(self):
2334
self.overrideAttr(config, 'credential_store_registry',
2335
config.CredentialStoreRegistry())
2336
store = StubCredentialStore()
2337
store.add_credentials("http", "example.com", "joe", "secret")
2338
config.credential_store_registry.register("stub", store, fallback=True)
2339
conf = config.AuthenticationConfig(_file=StringIO())
2340
creds = conf.get_credentials("http", "example.com")
2341
self.assertEquals("joe", creds["user"])
2342
self.assertEquals("secret", creds["password"])
2345
class StubCredentialStore(config.CredentialStore):
2351
def add_credentials(self, scheme, host, user, password=None):
2352
self._username[(scheme, host)] = user
2353
self._password[(scheme, host)] = password
2355
def get_credentials(self, scheme, host, port=None, user=None,
2356
path=None, realm=None):
2357
key = (scheme, host)
2358
if not key in self._username:
2360
return { "scheme": scheme, "host": host, "port": port,
2361
"user": self._username[key], "password": self._password[key]}
2364
class CountingCredentialStore(config.CredentialStore):
2369
def get_credentials(self, scheme, host, port=None, user=None,
2370
path=None, realm=None):
2375
class TestCredentialStoreRegistry(tests.TestCase):
2377
def _get_cs_registry(self):
2378
return config.credential_store_registry
2380
def test_default_credential_store(self):
2381
r = self._get_cs_registry()
2382
default = r.get_credential_store(None)
2383
self.assertIsInstance(default, config.PlainTextCredentialStore)
2385
def test_unknown_credential_store(self):
2386
r = self._get_cs_registry()
2387
# It's hard to imagine someone creating a credential store named
2388
# 'unknown' so we use that as an never registered key.
2389
self.assertRaises(KeyError, r.get_credential_store, 'unknown')
2391
def test_fallback_none_registered(self):
2392
r = config.CredentialStoreRegistry()
2393
self.assertEquals(None,
2394
r.get_fallback_credentials("http", "example.com"))
2396
def test_register(self):
2397
r = config.CredentialStoreRegistry()
2398
r.register("stub", StubCredentialStore(), fallback=False)
2399
r.register("another", StubCredentialStore(), fallback=True)
2400
self.assertEquals(["another", "stub"], r.keys())
2402
def test_register_lazy(self):
2403
r = config.CredentialStoreRegistry()
2404
r.register_lazy("stub", "bzrlib.tests.test_config",
2405
"StubCredentialStore", fallback=False)
2406
self.assertEquals(["stub"], r.keys())
2407
self.assertIsInstance(r.get_credential_store("stub"),
2408
StubCredentialStore)
2410
def test_is_fallback(self):
2411
r = config.CredentialStoreRegistry()
2412
r.register("stub1", None, fallback=False)
2413
r.register("stub2", None, fallback=True)
2414
self.assertEquals(False, r.is_fallback("stub1"))
2415
self.assertEquals(True, r.is_fallback("stub2"))
2417
def test_no_fallback(self):
2418
r = config.CredentialStoreRegistry()
2419
store = CountingCredentialStore()
2420
r.register("count", store, fallback=False)
2421
self.assertEquals(None,
2422
r.get_fallback_credentials("http", "example.com"))
2423
self.assertEquals(0, store._calls)
2425
def test_fallback_credentials(self):
2426
r = config.CredentialStoreRegistry()
2427
store = StubCredentialStore()
2428
store.add_credentials("http", "example.com",
2429
"somebody", "geheim")
2430
r.register("stub", store, fallback=True)
2431
creds = r.get_fallback_credentials("http", "example.com")
2432
self.assertEquals("somebody", creds["user"])
2433
self.assertEquals("geheim", creds["password"])
2435
def test_fallback_first_wins(self):
2436
r = config.CredentialStoreRegistry()
2437
stub1 = StubCredentialStore()
2438
stub1.add_credentials("http", "example.com",
2439
"somebody", "stub1")
2440
r.register("stub1", stub1, fallback=True)
2441
stub2 = StubCredentialStore()
2442
stub2.add_credentials("http", "example.com",
2443
"somebody", "stub2")
2444
r.register("stub2", stub1, fallback=True)
2445
creds = r.get_fallback_credentials("http", "example.com")
2446
self.assertEquals("somebody", creds["user"])
2447
self.assertEquals("stub1", creds["password"])
2450
class TestPlainTextCredentialStore(tests.TestCase):
2452
def test_decode_password(self):
2453
r = config.credential_store_registry
2454
plain_text = r.get_credential_store()
2455
decoded = plain_text.decode_password(dict(password='secret'))
2456
self.assertEquals('secret', decoded)
2459
# FIXME: Once we have a way to declare authentication to all test servers, we
2460
# can implement generic tests.
2461
# test_user_password_in_url
2462
# test_user_in_url_password_from_config
2463
# test_user_in_url_password_prompted
2464
# test_user_in_config
2465
# test_user_getpass.getuser
2466
# test_user_prompted ?
2467
class TestAuthenticationRing(tests.TestCaseWithTransport):
2471
class TestAutoUserId(tests.TestCase):
2472
"""Test inferring an automatic user name."""
2474
def test_auto_user_id(self):
2475
"""Automatic inference of user name.
2477
This is a bit hard to test in an isolated way, because it depends on
2478
system functions that go direct to /etc or perhaps somewhere else.
2479
But it's reasonable to say that on Unix, with an /etc/mailname, we ought
2480
to be able to choose a user name with no configuration.
2482
if sys.platform == 'win32':
2483
raise TestSkipped("User name inference not implemented on win32")
2484
realname, address = config._auto_user_id()
2485
if os.path.exists('/etc/mailname'):
2486
self.assertTrue(realname)
2487
self.assertTrue(address)
2489
self.assertEquals((None, None), (realname, address))