155
163
build_backing_branch(test, 'branch', transport_class, server_class)
156
164
b = branch.Branch.open(test.get_url('branch'))
157
165
return config.BranchOnlyStack(b)
158
168
config.test_stack_builder_registry.register('branch_only',
159
169
build_branch_only_stack)
161
172
def build_remote_control_stack(test):
162
173
# There is only one permutation (but we won't be able to handle more with
163
174
# this design anyway)
168
179
build_backing_branch(test, 'branch', transport_class, server_class)
169
180
b = branch.Branch.open(test.get_url('branch'))
170
181
return config.RemoteControlStack(b.controldir)
171
184
config.test_stack_builder_registry.register('remote_control',
172
185
build_remote_control_stack)
175
sample_long_alias="log -r-15..-1 --line"
188
sample_long_alias = "log -r-15..-1 --line"
176
189
sample_config_text = u"""
178
191
email=Erik B\u00e5gfors <erik@bagfors.nu>
752
765
# Nope, it's either a string or a list, and the list wins as soon as a
753
766
# ',' appears, so the string concatenation never occur.
754
767
self.assertEqual(['{foo', '}', '{', 'bar}'],
755
conf.get_user_option('hidden', expand=True))
768
conf.get_user_option('hidden', expand=True))
758
771
class TestLocationConfigOptionExpansion(tests.TestCaseInTempDir):
898
913
self.assertEqual('c2', c2.get_user_option('one'))
900
915
def test_read_while_writing(self):
902
# We spawn a thread that will pause *during* the write
903
ready_to_write = threading.Event()
904
do_writing = threading.Event()
905
writing_done = threading.Event()
906
c1_orig = c1._write_config_file
907
def c1_write_config_file():
909
# The lock is held. We wait for the main thread to decide when to
914
c1._write_config_file = c1_write_config_file
916
c1.set_user_option('one', 'c1')
917
t1 = threading.Thread(target=c1_set_option)
918
# Collect the thread after the test
919
self.addCleanup(t1.join)
920
# Be ready to unblock the thread if the test goes wrong
921
self.addCleanup(do_writing.set)
923
# Ensure the thread is ready to write
924
ready_to_write.wait()
925
self.assertTrue(c1._lock.is_held)
926
self.assertEqual('c1', c1.get_user_option('one'))
927
# If we read during the write, we get the old value
928
c2 = self.get_existing_config()
929
self.assertEqual('1', c2.get_user_option('one'))
930
# Let the writing occur and ensure it occurred
933
# Now we get the updated value
934
c3 = self.get_existing_config()
935
self.assertEqual('c1', c3.get_user_option('one'))
917
# We spawn a thread that will pause *during* the write
918
ready_to_write = threading.Event()
919
do_writing = threading.Event()
920
writing_done = threading.Event()
921
c1_orig = c1._write_config_file
923
def c1_write_config_file():
925
# The lock is held. We wait for the main thread to decide when to
930
c1._write_config_file = c1_write_config_file
933
c1.set_user_option('one', 'c1')
934
t1 = threading.Thread(target=c1_set_option)
935
# Collect the thread after the test
936
self.addCleanup(t1.join)
937
# Be ready to unblock the thread if the test goes wrong
938
self.addCleanup(do_writing.set)
940
# Ensure the thread is ready to write
941
ready_to_write.wait()
942
self.assertTrue(c1._lock.is_held)
943
self.assertEqual('c1', c1.get_user_option('one'))
944
# If we read during the write, we get the old value
945
c2 = self.get_existing_config()
946
self.assertEqual('1', c2.get_user_option('one'))
947
# Let the writing occur and ensure it occurred
950
# Now we get the updated value
951
c3 = self.get_existing_config()
952
self.assertEqual('c1', c3.get_user_option('one'))
938
955
class TestGetUserOptionAs(TestIniConfig):
1191
1211
tools = conf.get_merge_tools()
1192
1212
self.log(repr(tools))
1193
1213
self.assertEqual(
1194
{u'funkytool' : u'funkytool "arg with spaces" {this_temp}',
1195
u'sometool' : u'sometool {base} {this} {other} -o {result}',
1196
u'newtool' : u'"newtool with spaces" {this_temp}'},
1214
{u'funkytool': u'funkytool "arg with spaces" {this_temp}',
1215
u'sometool': u'sometool {base} {this} {other} -o {result}',
1216
u'newtool': u'"newtool with spaces" {this_temp}'},
1199
1219
def test_get_merge_tools_empty(self):
1335
1355
self.get_branch_config('http://www.example.com')
1336
1356
self.assertEqual(
1337
1357
self.my_location_config._get_config_policy(
1338
'http://www.example.com', 'normal_option'),
1358
'http://www.example.com', 'normal_option'),
1339
1359
config.POLICY_NONE)
1341
1361
def test__get_option_policy_norecurse(self):
1342
1362
self.get_branch_config('http://www.example.com')
1343
1363
self.assertEqual(
1344
1364
self.my_location_config._get_option_policy(
1345
'http://www.example.com', 'norecurse_option'),
1365
'http://www.example.com', 'norecurse_option'),
1346
1366
config.POLICY_NORECURSE)
1347
1367
# Test old recurse=False setting:
1348
1368
self.assertEqual(
1349
1369
self.my_location_config._get_option_policy(
1350
'http://www.example.com/norecurse', 'normal_option'),
1370
'http://www.example.com/norecurse', 'normal_option'),
1351
1371
config.POLICY_NORECURSE)
1353
1373
def test__get_option_policy_normal(self):
1354
1374
self.get_branch_config('http://www.example.com')
1355
1375
self.assertEqual(
1356
1376
self.my_location_config._get_option_policy(
1357
'http://www.example.com', 'appendpath_option'),
1377
'http://www.example.com', 'appendpath_option'),
1358
1378
config.POLICY_APPENDPATH)
1360
1380
def test__get_options_with_policy(self):
1445
1465
store=config.STORE_LOCATION_APPENDPATH)
1446
1466
self.assertEqual(
1447
1467
self.my_location_config._get_option_policy(
1448
'http://www.example.com', 'foo'),
1468
'http://www.example.com', 'foo'),
1449
1469
config.POLICY_APPENDPATH)
1451
1471
def test_set_user_option_change_policy(self):
1454
1474
store=config.STORE_LOCATION)
1455
1475
self.assertEqual(
1456
1476
self.my_location_config._get_option_policy(
1457
'http://www.example.com', 'norecurse_option'),
1477
'http://www.example.com', 'norecurse_option'),
1458
1478
config.POLICY_NONE)
1460
1480
def get_branch_config(self, location, global_config=None,
1904
1935
def test_load_hook_remote_branch(self):
1905
1936
remote_branch = branch.Branch.open(self.get_url('tree'))
1906
self.assertLoadHook(1, 'file', remote.RemoteBranchConfig, remote_branch)
1937
self.assertLoadHook(
1938
1, 'file', remote.RemoteBranchConfig, remote_branch)
1908
1940
def test_load_hook_remote_bzrdir(self):
1909
1941
remote_bzrdir = controldir.ControlDir.open(self.get_url('tree'))
1914
1946
# caused by the differences in implementations betwen
1915
1947
# SmartServerBzrDirRequestConfigFile (in smart/bzrdir.py) and
1916
1948
# SmartServerBranchGetConfigFile (in smart/branch.py)
1917
self.assertLoadHook(2, 'file', remote.RemoteBzrDirConfig, remote_bzrdir)
1949
self.assertLoadHook(
1950
2, 'file', remote.RemoteBzrDirConfig, remote_bzrdir)
1919
1952
def assertSaveHook(self, conf):
1921
1955
def hook(*args):
1922
1956
calls.append(args)
1923
1957
config.OldConfigHooks.install_named_hook('save', hook, None)
2492
2527
def test_simple_comma(self):
2493
2528
if isinstance(self.store, config.IniFileStore):
2494
2529
# configobj requires that lists are special-cased
2495
self.assertRaises(AssertionError,
2496
self.assertIdempotent, ',')
2530
self.assertRaises(AssertionError,
2531
self.assertIdempotent, ',')
2498
2533
self.assertIdempotent(',')
2499
2534
# When a single comma is required, quoting is also required
2889
2928
stack = config.Stack([store.get_sections], store)
2890
2929
stack.set('foo', ' a b c ')
2892
self.assertFileEqual(b'foo = " a b c "' + os.linesep.encode('ascii'), 'foo.conf')
2931
self.assertFileEqual(b'foo = " a b c "' +
2932
os.linesep.encode('ascii'), 'foo.conf')
2895
2935
class TestTransportIniFileStore(TestStore):
2897
2937
def test_loading_unknown_file_fails(self):
2898
2938
store = config.TransportIniFileStore(self.get_transport(),
2900
2940
self.assertRaises(errors.NoSuchFile, store.load)
2902
2942
def test_invalid_content(self):
3046
3088
self.assertEqual('c2', c2.get('one'))
3048
3090
def test_read_while_writing(self):
3050
# We spawn a thread that will pause *during* the write
3051
ready_to_write = threading.Event()
3052
do_writing = threading.Event()
3053
writing_done = threading.Event()
3054
# We override the _save implementation so we know the store is locked
3055
c1_save_without_locking_orig = c1.store.save_without_locking
3056
def c1_save_without_locking():
3057
ready_to_write.set()
3058
# The lock is held. We wait for the main thread to decide when to
3061
c1_save_without_locking_orig()
3063
c1.store.save_without_locking = c1_save_without_locking
3066
t1 = threading.Thread(target=c1_set)
3067
# Collect the thread after the test
3068
self.addCleanup(t1.join)
3069
# Be ready to unblock the thread if the test goes wrong
3070
self.addCleanup(do_writing.set)
3072
# Ensure the thread is ready to write
3073
ready_to_write.wait()
3074
self.assertEqual('c1', c1.get('one'))
3075
# If we read during the write, we get the old value
3076
c2 = self.get_stack(self)
3077
self.assertEqual('1', c2.get('one'))
3078
# Let the writing occur and ensure it occurred
3081
# Now we get the updated value
3082
c3 = self.get_stack(self)
3083
self.assertEqual('c1', c3.get('one'))
3092
# We spawn a thread that will pause *during* the write
3093
ready_to_write = threading.Event()
3094
do_writing = threading.Event()
3095
writing_done = threading.Event()
3096
# We override the _save implementation so we know the store is locked
3097
c1_save_without_locking_orig = c1.store.save_without_locking
3099
def c1_save_without_locking():
3100
ready_to_write.set()
3101
# The lock is held. We wait for the main thread to decide when to
3104
c1_save_without_locking_orig()
3106
c1.store.save_without_locking = c1_save_without_locking
3110
t1 = threading.Thread(target=c1_set)
3111
# Collect the thread after the test
3112
self.addCleanup(t1.join)
3113
# Be ready to unblock the thread if the test goes wrong
3114
self.addCleanup(do_writing.set)
3116
# Ensure the thread is ready to write
3117
ready_to_write.wait()
3118
self.assertEqual('c1', c1.get('one'))
3119
# If we read during the write, we get the old value
3120
c2 = self.get_stack(self)
3121
self.assertEqual('1', c2.get('one'))
3122
# Let the writing occur and ensure it occurred
3125
# Now we get the updated value
3126
c3 = self.get_stack(self)
3127
self.assertEqual('c1', c3.get('one'))
3085
3129
# FIXME: It may be worth looking into removing the lock dir when it's not
3086
3130
# needed anymore and look at possible fallouts for concurrent lockers. This
3091
3135
class TestSectionMatcher(TestStore):
3093
3137
scenarios = [('location', {'matcher': config.LocationMatcher}),
3094
('id', {'matcher': config.NameMatcher}),]
3138
('id', {'matcher': config.NameMatcher}), ]
3096
3140
def setUp(self):
3097
3141
super(TestSectionMatcher, self).setUp()
3154
3198
section=/quux/quux
3156
3200
self.assertEqual(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
3158
[section.id for _, section in store.get_sections()])
3202
[section.id for _, section in store.get_sections()])
3159
3203
matcher = config.LocationMatcher(store, '/foo/bar/quux')
3160
3204
sections = [section for _, section in matcher.get_sections()]
3161
3205
self.assertEqual(['/foo/bar', '/foo'],
3162
[section.id for section in sections])
3206
[section.id for section in sections])
3163
3207
self.assertEqual(['quux', 'bar/quux'],
3164
[section.extra_path for section in sections])
3208
[section.extra_path for section in sections])
3166
3210
def test_more_specific_sections_first(self):
3167
3211
store = self.get_store(self)
3172
3216
section=/foo/bar
3174
3218
self.assertEqual(['/foo', '/foo/bar'],
3175
[section.id for _, section in store.get_sections()])
3219
[section.id for _, section in store.get_sections()])
3176
3220
matcher = config.LocationMatcher(store, '/foo/bar/baz')
3177
3221
sections = [section for _, section in matcher.get_sections()]
3178
3222
self.assertEqual(['/foo/bar', '/foo'],
3179
[section.id for section in sections])
3223
[section.id for section in sections])
3180
3224
self.assertEqual(['baz', 'bar/baz'],
3181
[section.extra_path for section in sections])
3225
[section.extra_path for section in sections])
3183
3227
def test_appendpath_in_no_name_section(self):
3184
3228
# It's a bit weird to allow appendpath in a no-name section, but
3274
3317
self.assertEqual(['baz', 'bar/baz', '/foo/bar/baz'],
3275
[s.locals['relpath'] for _, s in sections])
3318
[s.locals['relpath'] for _, s in sections])
3277
3320
def test_order_reversed(self):
3278
3321
self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', b'''\
3299
3342
# nothing really is... as far using {relpath} to append it to something
3300
3343
# else, this seems good enough though.
3301
3344
self.assertEqual(['', 'baz', 'bar/baz'],
3302
[s.locals['relpath'] for _, s in sections])
3345
[s.locals['relpath'] for _, s in sections])
3304
3347
def test_respect_order(self):
3305
3348
self.assertSectionIDs(['/foo', '/foo/b*', '/foo/*/baz'],
3750
3794
self.registry.register(
3751
3795
config.ListOption('list'))
3752
3796
self.assertEqual(['start', 'middle', 'end'],
3753
self.conf.get('list', expand=True))
3797
self.conf.get('list', expand=True))
3755
3799
def test_cascading_list(self):
3756
3800
self.conf.store._load_from_string(b'''
3765
3809
# option ('list' here).
3766
3810
self.registry.register(config.ListOption('baz'))
3767
3811
self.assertEqual(['start', 'middle', 'end'],
3768
self.conf.get('list', expand=True))
3812
self.conf.get('list', expand=True))
3770
3814
def test_pathologically_hidden_list(self):
3771
3815
self.conf.store._load_from_string(b'''
3780
3824
# only after all expansions have been performed
3781
3825
self.registry.register(config.ListOption('hidden'))
3782
3826
self.assertEqual(['bin', 'go'],
3783
self.conf.get('hidden', expand=True))
3827
self.conf.get('hidden', expand=True))
3786
3830
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
4017
4062
self.breezy_config.set_user_option('file', 'breezy')
4018
4063
self.branch_config.set_user_option('file', 'branch')
4019
4064
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
4020
('file', 'breezy', 'DEFAULT', 'breezy'),],
4065
('file', 'breezy', 'DEFAULT', 'breezy'), ],
4021
4066
self.branch_config)
4023
4068
def test_option_in_branch_and_locations(self):
4026
4071
self.branch_config.set_user_option('file', 'branch')
4027
4072
self.assertOptions(
4028
4073
[('file', 'locations', self.tree.basedir, 'locations'),
4029
('file', 'branch', 'DEFAULT', 'branch'),],
4074
('file', 'branch', 'DEFAULT', 'branch'), ],
4030
4075
self.branch_config)
4032
4077
def test_option_in_breezy_locations_and_branch(self):
4036
4081
self.assertOptions(
4037
4082
[('file', 'locations', self.tree.basedir, 'locations'),
4038
4083
('file', 'branch', 'DEFAULT', 'branch'),
4039
('file', 'breezy', 'DEFAULT', 'breezy'),],
4084
('file', 'breezy', 'DEFAULT', 'breezy'), ],
4040
4085
self.branch_config)
4050
4095
self.locations_config.remove_user_option('file', self.tree.basedir)
4051
4096
self.assertOptions(
4052
4097
[('file', 'branch', 'DEFAULT', 'branch'),
4053
('file', 'breezy', 'DEFAULT', 'breezy'),],
4098
('file', 'breezy', 'DEFAULT', 'breezy'), ],
4054
4099
self.branch_config)
4056
4101
def test_remove_in_branch(self):
4057
4102
self.branch_config.remove_user_option('file')
4058
4103
self.assertOptions(
4059
4104
[('file', 'locations', self.tree.basedir, 'locations'),
4060
('file', 'breezy', 'DEFAULT', 'breezy'),],
4105
('file', 'breezy', 'DEFAULT', 'breezy'), ],
4061
4106
self.branch_config)
4063
4108
def test_remove_in_breezy(self):
4064
4109
self.breezy_config.remove_user_option('file')
4065
4110
self.assertOptions(
4066
4111
[('file', 'locations', self.tree.basedir, 'locations'),
4067
('file', 'branch', 'DEFAULT', 'branch'),],
4112
('file', 'branch', 'DEFAULT', 'branch'), ],
4068
4113
self.branch_config)
4160
4205
port=port # Error: Not an int
4162
4207
self.overrideAttr(config, 'authentication_config_filename',
4164
4209
osutils.chmod_if_possible(self.path, 0o755)
4166
4211
def test_check_warning(self):
4167
4212
conf = config.AuthenticationConfig()
4168
4213
self.assertEqual(conf._filename, self.path)
4169
4214
self.assertContainsRe(self.get_log(),
4170
'Saved passwords may be accessible by other users.')
4215
'Saved passwords may be accessible by other users.')
4172
4217
def test_check_suppressed_warning(self):
4173
4218
global_config = config.GlobalConfig()
4174
4219
global_config.set_user_option('suppress_warnings',
4175
'insecure_permissions')
4220
'insecure_permissions')
4176
4221
conf = config.AuthenticationConfig()
4177
4222
self.assertEqual(conf._filename, self.path)
4178
4223
self.assertNotContainsRe(self.get_log(),
4179
'Saved passwords may be accessible by other users.')
4224
'Saved passwords may be accessible by other users.')
4182
4227
class TestAuthenticationConfigFile(tests.TestCase):
4389
4434
def test_set_credentials(self):
4390
4435
conf = config.AuthenticationConfig()
4391
4436
conf.set_credentials('name', 'host', 'user', 'scheme', 'password',
4392
99, path='/foo', verify_certificates=False, realm='realm')
4437
99, path='/foo', verify_certificates=False, realm='realm')
4393
4438
credentials = conf.get_credentials(host='host', scheme='scheme',
4394
4439
port=99, path='/foo',
4432
4477
# We use an empty conf so that the user is always prompted
4433
4478
conf = config.AuthenticationConfig()
4434
4479
self.assertEqual(password,
4435
conf.get_password(scheme, host, user, port=port,
4436
realm=realm, path=path))
4480
conf.get_password(scheme, host, user, port=port,
4481
realm=realm, path=path))
4437
4482
self.assertEqual(expected_prompt, ui.ui_factory.stderr.getvalue())
4438
4483
self.assertEqual('', ui.ui_factory.stdout.getvalue())
4446
4491
expected_prompt = expected_prompt_format % {
4447
4492
'scheme': scheme, 'host': host, 'port': port,
4448
4493
'realm': realm}
4449
ui.ui_factory = tests.TestUIFactory(stdin=username+ '\n')
4494
ui.ui_factory = tests.TestUIFactory(stdin=username + '\n')
4450
4495
# We use an empty conf so that the user is always prompted
4451
4496
conf = config.AuthenticationConfig()
4452
4497
self.assertEqual(username, conf.get_user(scheme, host, port=port,
4453
realm=realm, path=path, ask=True))
4498
realm=realm, path=path, ask=True))
4454
4499
self.assertEqual(expected_prompt, ui.ui_factory.stderr.getvalue())
4455
4500
self.assertEqual('', ui.ui_factory.stdout.getvalue())
4465
4510
def test_username_default_no_prompt(self):
4466
4511
conf = config.AuthenticationConfig()
4467
4512
self.assertEqual(None,
4468
conf.get_user('ftp', 'example.com'))
4513
conf.get_user('ftp', 'example.com'))
4469
4514
self.assertEqual("explicitdefault",
4470
conf.get_user('ftp', 'example.com', default="explicitdefault"))
4515
conf.get_user('ftp', 'example.com', default="explicitdefault"))
4472
4517
def test_password_default_prompts(self):
4473
4518
# HTTP prompts can't be tested here, see test_http.py
4502
4547
# Since the password defined in the authentication config is ignored,
4503
4548
# the user is prompted
4504
4549
self.assertEqual(entered_password,
4505
conf.get_password('ssh', 'bar.org', user='jim'))
4550
conf.get_password('ssh', 'bar.org', user='jim'))
4506
4551
self.assertContainsRe(
4507
4552
self.get_log(),
4508
4553
'password ignored in section \\[ssh with password\\]')
4520
4565
# Since the password defined in the authentication config is ignored,
4521
4566
# the user is prompted
4522
4567
self.assertEqual(entered_password,
4523
conf.get_password('ssh', 'bar.org', user='jim'))
4568
conf.get_password('ssh', 'bar.org', user='jim'))
4524
4569
# No warning shoud be emitted since there is no password. We are only
4525
4570
# providing "user".
4526
4571
self.assertNotContainsRe(
4550
4595
self._password[(scheme, host)] = password
4552
4597
def get_credentials(self, scheme, host, port=None, user=None,
4553
path=None, realm=None):
4598
path=None, realm=None):
4554
4599
key = (scheme, host)
4555
4600
if not key in self._username:
4557
return { "scheme": scheme, "host": host, "port": port,
4602
return {"scheme": scheme, "host": host, "port": port,
4558
4603
"user": self._username[key], "password": self._password[key]}
4616
4661
store = CountingCredentialStore()
4617
4662
r.register("count", store, fallback=False)
4618
4663
self.assertEqual(None,
4619
r.get_fallback_credentials("http", "example.com"))
4664
r.get_fallback_credentials("http", "example.com"))
4620
4665
self.assertEqual(0, store._calls)
4622
4667
def test_fallback_credentials(self):