1084
220
my_config = config.BranchConfig(branch)
1085
221
location_config = my_config._get_location_config()
1086
222
self.assertEqual(branch.base, location_config.location)
1087
self.assertIs(location_config, my_config._get_location_config())
1089
def test_get_config(self):
1090
"""The Branch.get_config method works properly"""
1091
b = bzrdir.BzrDir.create_standalone_workingtree('.').branch
1092
my_config = b.get_config()
1093
self.assertIs(my_config.get_user_option('wacky'), None)
1094
my_config.set_user_option('wacky', 'unlikely')
1095
self.assertEqual(my_config.get_user_option('wacky'), 'unlikely')
1097
# Ensure we get the same thing if we start again
1098
b2 = branch.Branch.open('.')
1099
my_config2 = b2.get_config()
1100
self.assertEqual(my_config2.get_user_option('wacky'), 'unlikely')
1102
def test_has_explicit_nickname(self):
1103
b = self.make_branch('.')
1104
self.assertFalse(b.get_config().has_explicit_nickname())
1106
self.assertTrue(b.get_config().has_explicit_nickname())
1108
def test_config_url(self):
1109
"""The Branch.get_config will use section that uses a local url"""
1110
branch = self.make_branch('branch')
1111
self.assertEqual('branch', branch.nick)
1113
local_url = urlutils.local_path_to_url('branch')
1114
conf = config.LocationConfig.from_string(
1115
'[%s]\nnickname = foobar' % (local_url,),
1116
local_url, save=True)
1117
self.assertEqual('foobar', branch.nick)
1119
def test_config_local_path(self):
1120
"""The Branch.get_config will use a local system path"""
1121
branch = self.make_branch('branch')
1122
self.assertEqual('branch', branch.nick)
1124
local_path = osutils.getcwd().encode('utf8')
1125
conf = config.LocationConfig.from_string(
1126
'[%s/branch]\nnickname = barry' % (local_path,),
1127
'branch', save=True)
1128
self.assertEqual('barry', branch.nick)
1130
def test_config_creates_local(self):
1131
"""Creating a new entry in config uses a local path."""
1132
branch = self.make_branch('branch', format='knit')
1133
branch.set_push_location('http://foobar')
1134
local_path = osutils.getcwd().encode('utf8')
1135
# Surprisingly ConfigObj doesn't create a trailing newline
1136
self.check_file_contents(config.locations_config_filename(),
1138
'push_location = http://foobar\n'
1139
'push_location:policy = norecurse\n'
1142
def test_autonick_urlencoded(self):
1143
b = self.make_branch('!repo')
1144
self.assertEqual('!repo', b.get_config().get_nickname())
1146
def test_warn_if_masked(self):
1149
warnings.append(args[0] % args[1:])
1150
self.overrideAttr(trace, 'warning', warning)
1152
def set_option(store, warn_masked=True):
1154
conf.set_user_option('example_option', repr(store), store=store,
1155
warn_masked=warn_masked)
1156
def assertWarning(warning):
1158
self.assertEqual(0, len(warnings))
1160
self.assertEqual(1, len(warnings))
1161
self.assertEqual(warning, warnings[0])
1162
branch = self.make_branch('.')
1163
conf = branch.get_config()
1164
set_option(config.STORE_GLOBAL)
1166
set_option(config.STORE_BRANCH)
1168
set_option(config.STORE_GLOBAL)
1169
assertWarning('Value "4" is masked by "3" from branch.conf')
1170
set_option(config.STORE_GLOBAL, warn_masked=False)
1172
set_option(config.STORE_LOCATION)
1174
set_option(config.STORE_BRANCH)
1175
assertWarning('Value "3" is masked by "0" from locations.conf')
1176
set_option(config.STORE_BRANCH, warn_masked=False)
1180
class TestGlobalConfigItems(tests.TestCaseInTempDir):
223
self.failUnless(location_config is my_config._get_location_config())
226
class TestGlobalConfigItems(TestCase):
1182
228
def test_user_id(self):
1183
my_config = config.GlobalConfig.from_string(sample_config_text)
1184
self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
229
config_file = StringIO(sample_config_text)
230
my_config = config.GlobalConfig()
231
my_config._parser = my_config._get_parser(file=config_file)
232
self.assertEqual("Robert Collins <robertc@example.com>",
1185
233
my_config._get_user_id())
1187
235
def test_absent_user_id(self):
236
config_file = StringIO("")
1188
237
my_config = config.GlobalConfig()
238
my_config._parser = my_config._get_parser(file=config_file)
1189
239
self.assertEqual(None, my_config._get_user_id())
1191
241
def test_configured_editor(self):
1192
my_config = config.GlobalConfig.from_string(sample_config_text)
1193
editor = self.applyDeprecated(
1194
deprecated_in((2, 4, 0)), my_config.get_editor)
1195
self.assertEqual('vim', editor)
242
config_file = StringIO(sample_config_text)
243
my_config = config.GlobalConfig()
244
my_config._parser = my_config._get_parser(file=config_file)
245
self.assertEqual("vim", my_config.get_editor())
1197
247
def test_signatures_always(self):
1198
my_config = config.GlobalConfig.from_string(sample_always_signatures)
1199
self.assertEqual(config.CHECK_NEVER,
248
config_file = StringIO(sample_always_signatures)
249
my_config = config.GlobalConfig()
250
my_config._parser = my_config._get_parser(file=config_file)
251
self.assertEqual(config.CHECK_ALWAYS,
1200
252
my_config.signature_checking())
1201
self.assertEqual(config.SIGN_ALWAYS,
1202
my_config.signing_policy())
1203
253
self.assertEqual(True, my_config.signature_needed())
1205
255
def test_signatures_if_possible(self):
1206
my_config = config.GlobalConfig.from_string(sample_maybe_signatures)
1207
self.assertEqual(config.CHECK_NEVER,
256
config_file = StringIO(sample_maybe_signatures)
257
my_config = config.GlobalConfig()
258
my_config._parser = my_config._get_parser(file=config_file)
259
self.assertEqual(config.CHECK_IF_POSSIBLE,
1208
260
my_config.signature_checking())
1209
self.assertEqual(config.SIGN_WHEN_REQUIRED,
1210
my_config.signing_policy())
1211
261
self.assertEqual(False, my_config.signature_needed())
1213
263
def test_signatures_ignore(self):
1214
my_config = config.GlobalConfig.from_string(sample_ignore_signatures)
1215
self.assertEqual(config.CHECK_ALWAYS,
264
config_file = StringIO(sample_ignore_signatures)
265
my_config = config.GlobalConfig()
266
my_config._parser = my_config._get_parser(file=config_file)
267
self.assertEqual(config.CHECK_NEVER,
1216
268
my_config.signature_checking())
1217
self.assertEqual(config.SIGN_NEVER,
1218
my_config.signing_policy())
1219
269
self.assertEqual(False, my_config.signature_needed())
1221
def _get_sample_config(self):
1222
my_config = config.GlobalConfig.from_string(sample_config_text)
1225
271
def test_gpg_signing_command(self):
1226
my_config = self._get_sample_config()
272
config_file = StringIO(sample_config_text)
273
my_config = config.GlobalConfig()
274
my_config._parser = my_config._get_parser(file=config_file)
1227
275
self.assertEqual("gnome-gpg", my_config.gpg_signing_command())
1228
276
self.assertEqual(False, my_config.signature_needed())
1230
def test_gpg_signing_key(self):
1231
my_config = self._get_sample_config()
1232
self.assertEqual("DD4D5088", my_config.gpg_signing_key())
1234
def _get_empty_config(self):
1235
my_config = config.GlobalConfig()
1238
278
def test_gpg_signing_command_unset(self):
1239
my_config = self._get_empty_config()
279
config_file = StringIO("")
280
my_config = config.GlobalConfig()
281
my_config._parser = my_config._get_parser(file=config_file)
1240
282
self.assertEqual("gpg", my_config.gpg_signing_command())
1242
def test_get_user_option_default(self):
1243
my_config = self._get_empty_config()
1244
self.assertEqual(None, my_config.get_user_option('no_option'))
1246
def test_get_user_option_global(self):
1247
my_config = self._get_sample_config()
1248
self.assertEqual("something",
1249
my_config.get_user_option('user_global_option'))
1251
def test_post_commit_default(self):
1252
my_config = self._get_sample_config()
1253
self.assertEqual(None, my_config.post_commit())
1255
def test_configured_logformat(self):
1256
my_config = self._get_sample_config()
1257
self.assertEqual("short", my_config.log_format())
1259
def test_configured_acceptable_keys(self):
1260
my_config = self._get_sample_config()
1261
self.assertEqual("amy", my_config.acceptable_keys())
1263
def test_configured_validate_signatures_in_log(self):
1264
my_config = self._get_sample_config()
1265
self.assertEqual(True, my_config.validate_signatures_in_log())
1267
def test_get_alias(self):
1268
my_config = self._get_sample_config()
1269
self.assertEqual('help', my_config.get_alias('h'))
1271
def test_get_aliases(self):
1272
my_config = self._get_sample_config()
1273
aliases = my_config.get_aliases()
1274
self.assertEqual(2, len(aliases))
1275
sorted_keys = sorted(aliases)
1276
self.assertEqual('help', aliases[sorted_keys[0]])
1277
self.assertEqual(sample_long_alias, aliases[sorted_keys[1]])
1279
def test_get_no_alias(self):
1280
my_config = self._get_sample_config()
1281
self.assertEqual(None, my_config.get_alias('foo'))
1283
def test_get_long_alias(self):
1284
my_config = self._get_sample_config()
1285
self.assertEqual(sample_long_alias, my_config.get_alias('ll'))
1287
def test_get_change_editor(self):
1288
my_config = self._get_sample_config()
1289
change_editor = my_config.get_change_editor('old', 'new')
1290
self.assertIs(diff.DiffFromTool, change_editor.__class__)
1291
self.assertEqual('vimdiff -of @new_path @old_path',
1292
' '.join(change_editor.command_template))
1294
def test_get_no_change_editor(self):
1295
my_config = self._get_empty_config()
1296
change_editor = my_config.get_change_editor('old', 'new')
1297
self.assertIs(None, change_editor)
1299
def test_get_merge_tools(self):
1300
conf = self._get_sample_config()
1301
tools = conf.get_merge_tools()
1302
self.log(repr(tools))
1304
{u'funkytool' : u'funkytool "arg with spaces" {this_temp}',
1305
u'sometool' : u'sometool {base} {this} {other} -o {result}'},
1308
def test_get_merge_tools_empty(self):
1309
conf = self._get_empty_config()
1310
tools = conf.get_merge_tools()
1311
self.assertEqual({}, tools)
1313
def test_find_merge_tool(self):
1314
conf = self._get_sample_config()
1315
cmdline = conf.find_merge_tool('sometool')
1316
self.assertEqual('sometool {base} {this} {other} -o {result}', cmdline)
1318
def test_find_merge_tool_not_found(self):
1319
conf = self._get_sample_config()
1320
cmdline = conf.find_merge_tool('DOES NOT EXIST')
1321
self.assertIs(cmdline, None)
1323
def test_find_merge_tool_known(self):
1324
conf = self._get_empty_config()
1325
cmdline = conf.find_merge_tool('kdiff3')
1326
self.assertEquals('kdiff3 {base} {this} {other} -o {result}', cmdline)
1328
def test_find_merge_tool_override_known(self):
1329
conf = self._get_empty_config()
1330
conf.set_user_option('bzr.mergetool.kdiff3', 'kdiff3 blah')
1331
cmdline = conf.find_merge_tool('kdiff3')
1332
self.assertEqual('kdiff3 blah', cmdline)
1335
class TestGlobalConfigSavingOptions(tests.TestCaseInTempDir):
1337
def test_empty(self):
1338
my_config = config.GlobalConfig()
1339
self.assertEqual(0, len(my_config.get_aliases()))
1341
def test_set_alias(self):
1342
my_config = config.GlobalConfig()
1343
alias_value = 'commit --strict'
1344
my_config.set_alias('commit', alias_value)
1345
new_config = config.GlobalConfig()
1346
self.assertEqual(alias_value, new_config.get_alias('commit'))
1348
def test_remove_alias(self):
1349
my_config = config.GlobalConfig()
1350
my_config.set_alias('commit', 'commit --strict')
1351
# Now remove the alias again.
1352
my_config.unset_alias('commit')
1353
new_config = config.GlobalConfig()
1354
self.assertIs(None, new_config.get_alias('commit'))
1357
class TestLocationConfig(tests.TestCaseInTempDir, TestOptionsMixin):
285
class TestLocationConfig(TestCase):
1359
287
def test_constructs(self):
1360
288
my_config = config.LocationConfig('http://example.com')
1361
289
self.assertRaises(TypeError, config.LocationConfig)
1363
291
def test_branch_calls_read_filenames(self):
1364
# This is testing the correct file names are provided.
1365
# TODO: consolidate with the test for GlobalConfigs filename checks.
1367
# replace the class that is constructed, to check its parameters
1368
oldparserclass = config.ConfigObj
1369
config.ConfigObj = InstrumentedConfigObj
292
# replace the class that is constructured, to check its parameters
293
oldparserclass = config.ConfigParser
294
config.ConfigParser = InstrumentedConfigParser
295
my_config = config.LocationConfig('http://www.example.com')
1371
my_config = config.LocationConfig('http://www.example.com')
1372
297
parser = my_config._get_parser()
1374
config.ConfigObj = oldparserclass
1375
self.assertIsInstance(parser, InstrumentedConfigObj)
1376
self.assertEqual(parser._calls,
1377
[('__init__', config.locations_config_filename(),
299
config.ConfigParser = oldparserclass
300
self.failUnless(isinstance(parser, InstrumentedConfigParser))
301
self.assertEqual(parser._calls, [('read', [config.branches_config_filename()])])
1380
303
def test_get_global_config(self):
1381
my_config = config.BranchConfig(FakeBranch('http://example.com'))
304
my_config = config.LocationConfig('http://example.com')
1382
305
global_config = my_config._get_global_config()
1383
self.assertIsInstance(global_config, config.GlobalConfig)
1384
self.assertIs(global_config, my_config._get_global_config())
1386
def assertLocationMatching(self, expected):
1387
self.assertEqual(expected,
1388
list(self.my_location_config._get_matching_sections()))
1390
def test__get_matching_sections_no_match(self):
1391
self.get_branch_config('/')
1392
self.assertLocationMatching([])
1394
def test__get_matching_sections_exact(self):
1395
self.get_branch_config('http://www.example.com')
1396
self.assertLocationMatching([('http://www.example.com', '')])
1398
def test__get_matching_sections_suffix_does_not(self):
1399
self.get_branch_config('http://www.example.com-com')
1400
self.assertLocationMatching([])
1402
def test__get_matching_sections_subdir_recursive(self):
1403
self.get_branch_config('http://www.example.com/com')
1404
self.assertLocationMatching([('http://www.example.com', 'com')])
1406
def test__get_matching_sections_ignoreparent(self):
1407
self.get_branch_config('http://www.example.com/ignoreparent')
1408
self.assertLocationMatching([('http://www.example.com/ignoreparent',
1411
def test__get_matching_sections_ignoreparent_subdir(self):
1412
self.get_branch_config(
1413
'http://www.example.com/ignoreparent/childbranch')
1414
self.assertLocationMatching([('http://www.example.com/ignoreparent',
1417
def test__get_matching_sections_subdir_trailing_slash(self):
1418
self.get_branch_config('/b')
1419
self.assertLocationMatching([('/b/', '')])
1421
def test__get_matching_sections_subdir_child(self):
1422
self.get_branch_config('/a/foo')
1423
self.assertLocationMatching([('/a/*', ''), ('/a/', 'foo')])
1425
def test__get_matching_sections_subdir_child_child(self):
1426
self.get_branch_config('/a/foo/bar')
1427
self.assertLocationMatching([('/a/*', 'bar'), ('/a/', 'foo/bar')])
1429
def test__get_matching_sections_trailing_slash_with_children(self):
1430
self.get_branch_config('/a/')
1431
self.assertLocationMatching([('/a/', '')])
1433
def test__get_matching_sections_explicit_over_glob(self):
1434
# XXX: 2006-09-08 jamesh
1435
# This test only passes because ord('c') > ord('*'). If there
1436
# was a config section for '/a/?', it would get precedence
1438
self.get_branch_config('/a/c')
1439
self.assertLocationMatching([('/a/c', ''), ('/a/*', ''), ('/a/', 'c')])
1441
def test__get_option_policy_normal(self):
1442
self.get_branch_config('http://www.example.com')
1444
self.my_location_config._get_config_policy(
1445
'http://www.example.com', 'normal_option'),
1448
def test__get_option_policy_norecurse(self):
1449
self.get_branch_config('http://www.example.com')
1451
self.my_location_config._get_option_policy(
1452
'http://www.example.com', 'norecurse_option'),
1453
config.POLICY_NORECURSE)
1454
# Test old recurse=False setting:
1456
self.my_location_config._get_option_policy(
1457
'http://www.example.com/norecurse', 'normal_option'),
1458
config.POLICY_NORECURSE)
1460
def test__get_option_policy_normal(self):
1461
self.get_branch_config('http://www.example.com')
1463
self.my_location_config._get_option_policy(
1464
'http://www.example.com', 'appendpath_option'),
1465
config.POLICY_APPENDPATH)
1467
def test__get_options_with_policy(self):
1468
self.get_branch_config('/dir/subdir',
1469
location_config="""\
1471
other_url = /other-dir
1472
other_url:policy = appendpath
1474
other_url = /other-subdir
1477
[(u'other_url', u'/other-subdir', u'/dir/subdir', 'locations'),
1478
(u'other_url', u'/other-dir', u'/dir', 'locations'),
1479
(u'other_url:policy', u'appendpath', u'/dir', 'locations')],
1480
self.my_location_config)
306
self.failUnless(isinstance(global_config, config.GlobalConfig))
307
self.failUnless(global_config is my_config._get_global_config())
309
def test__get_section_no_match(self):
310
self.get_location_config('/')
311
self.assertEqual(None, self.my_config._get_section())
313
def test__get_section_exact(self):
314
self.get_location_config('http://www.example.com')
315
self.assertEqual('http://www.example.com',
316
self.my_config._get_section())
318
def test__get_section_suffix_does_not(self):
319
self.get_location_config('http://www.example.com-com')
320
self.assertEqual(None, self.my_config._get_section())
322
def test__get_section_subdir_recursive(self):
323
self.get_location_config('http://www.example.com/com')
324
self.assertEqual('http://www.example.com',
325
self.my_config._get_section())
327
def test__get_section_subdir_matches(self):
328
self.get_location_config('http://www.example.com/useglobal')
329
self.assertEqual('http://www.example.com/useglobal',
330
self.my_config._get_section())
332
def test__get_section_subdir_nonrecursive(self):
333
self.get_location_config(
334
'http://www.example.com/useglobal/childbranch')
335
self.assertEqual('http://www.example.com',
336
self.my_config._get_section())
338
def test__get_section_subdir_trailing_slash(self):
339
self.get_location_config('/b')
340
self.assertEqual('/b/', self.my_config._get_section())
342
def test__get_section_subdir_child(self):
343
self.get_location_config('/a/foo')
344
self.assertEqual('/a/*', self.my_config._get_section())
346
def test__get_section_subdir_child_child(self):
347
self.get_location_config('/a/foo/bar')
348
self.assertEqual('/a/', self.my_config._get_section())
350
def test__get_section_trailing_slash_with_children(self):
351
self.get_location_config('/a/')
352
self.assertEqual('/a/', self.my_config._get_section())
354
def test__get_section_explicit_over_glob(self):
355
self.get_location_config('/a/c')
356
self.assertEqual('/a/c', self.my_config._get_section())
358
def get_location_config(self, location, global_config=None):
359
if global_config is None:
360
global_file = StringIO(sample_config_text)
362
global_file = StringIO(global_config)
363
branches_file = StringIO(sample_branches_text)
364
self.my_config = config.LocationConfig(location)
365
self.my_config._get_parser(branches_file)
366
self.my_config._get_global_config()._get_parser(global_file)
1482
368
def test_location_without_username(self):
1483
self.get_branch_config('http://www.example.com/ignoreparent')
1484
self.assertEqual(u'Erik B\u00e5gfors <erik@bagfors.nu>',
369
self.get_location_config('http://www.example.com/useglobal')
370
self.assertEqual('Robert Collins <robertc@example.com>',
1485
371
self.my_config.username())
1487
373
def test_location_not_listed(self):
1488
"""Test that the global username is used when no location matches"""
1489
self.get_branch_config('/home/robertc/sources')
1490
self.assertEqual(u'Erik B\u00e5gfors <erik@bagfors.nu>',
374
self.get_location_config('/home/robertc/sources')
375
self.assertEqual('Robert Collins <robertc@example.com>',
1491
376
self.my_config.username())
1493
378
def test_overriding_location(self):
1494
self.get_branch_config('http://www.example.com/foo')
379
self.get_location_config('http://www.example.com/foo')
1495
380
self.assertEqual('Robert Collins <robertc@example.org>',
1496
381
self.my_config.username())
1498
383
def test_signatures_not_set(self):
1499
self.get_branch_config('http://www.example.com',
384
self.get_location_config('http://www.example.com',
1500
385
global_config=sample_ignore_signatures)
1501
self.assertEqual(config.CHECK_ALWAYS,
386
self.assertEqual(config.CHECK_NEVER,
1502
387
self.my_config.signature_checking())
1503
self.assertEqual(config.SIGN_NEVER,
1504
self.my_config.signing_policy())
1506
389
def test_signatures_never(self):
1507
self.get_branch_config('/a/c')
390
self.get_location_config('/a/c')
1508
391
self.assertEqual(config.CHECK_NEVER,
1509
392
self.my_config.signature_checking())
1511
394
def test_signatures_when_available(self):
1512
self.get_branch_config('/a/', global_config=sample_ignore_signatures)
395
self.get_location_config('/a/', global_config=sample_ignore_signatures)
1513
396
self.assertEqual(config.CHECK_IF_POSSIBLE,
1514
397
self.my_config.signature_checking())
1516
399
def test_signatures_always(self):
1517
self.get_branch_config('/b')
400
self.get_location_config('/b')
1518
401
self.assertEqual(config.CHECK_ALWAYS,
1519
402
self.my_config.signature_checking())
1521
404
def test_gpg_signing_command(self):
1522
self.get_branch_config('/b')
405
self.get_location_config('/b')
1523
406
self.assertEqual("gnome-gpg", self.my_config.gpg_signing_command())
1525
408
def test_gpg_signing_command_missing(self):
1526
self.get_branch_config('/a')
409
self.get_location_config('/a')
1527
410
self.assertEqual("false", self.my_config.gpg_signing_command())
1529
def test_gpg_signing_key(self):
1530
self.get_branch_config('/b')
1531
self.assertEqual("DD4D5088", self.my_config.gpg_signing_key())
1533
def test_gpg_signing_key_default(self):
1534
self.get_branch_config('/a')
1535
self.assertEqual("erik@bagfors.nu", self.my_config.gpg_signing_key())
1537
def test_get_user_option_global(self):
1538
self.get_branch_config('/a')
1539
self.assertEqual('something',
1540
self.my_config.get_user_option('user_global_option'))
1542
def test_get_user_option_local(self):
1543
self.get_branch_config('/a')
1544
self.assertEqual('local',
1545
self.my_config.get_user_option('user_local_option'))
1547
def test_get_user_option_appendpath(self):
1548
# returned as is for the base path:
1549
self.get_branch_config('http://www.example.com')
1550
self.assertEqual('append',
1551
self.my_config.get_user_option('appendpath_option'))
1552
# Extra path components get appended:
1553
self.get_branch_config('http://www.example.com/a/b/c')
1554
self.assertEqual('append/a/b/c',
1555
self.my_config.get_user_option('appendpath_option'))
1556
# Overriden for http://www.example.com/dir, where it is a
1558
self.get_branch_config('http://www.example.com/dir/a/b/c')
1559
self.assertEqual('normal',
1560
self.my_config.get_user_option('appendpath_option'))
1562
def test_get_user_option_norecurse(self):
1563
self.get_branch_config('http://www.example.com')
1564
self.assertEqual('norecurse',
1565
self.my_config.get_user_option('norecurse_option'))
1566
self.get_branch_config('http://www.example.com/dir')
1567
self.assertEqual(None,
1568
self.my_config.get_user_option('norecurse_option'))
1569
# http://www.example.com/norecurse is a recurse=False section
1570
# that redefines normal_option. Subdirectories do not pick up
1571
# this redefinition.
1572
self.get_branch_config('http://www.example.com/norecurse')
1573
self.assertEqual('norecurse',
1574
self.my_config.get_user_option('normal_option'))
1575
self.get_branch_config('http://www.example.com/norecurse/subdir')
1576
self.assertEqual('normal',
1577
self.my_config.get_user_option('normal_option'))
1579
def test_set_user_option_norecurse(self):
1580
self.get_branch_config('http://www.example.com')
1581
self.my_config.set_user_option('foo', 'bar',
1582
store=config.STORE_LOCATION_NORECURSE)
1584
self.my_location_config._get_option_policy(
1585
'http://www.example.com', 'foo'),
1586
config.POLICY_NORECURSE)
1588
def test_set_user_option_appendpath(self):
1589
self.get_branch_config('http://www.example.com')
1590
self.my_config.set_user_option('foo', 'bar',
1591
store=config.STORE_LOCATION_APPENDPATH)
1593
self.my_location_config._get_option_policy(
1594
'http://www.example.com', 'foo'),
1595
config.POLICY_APPENDPATH)
1597
def test_set_user_option_change_policy(self):
1598
self.get_branch_config('http://www.example.com')
1599
self.my_config.set_user_option('norecurse_option', 'normal',
1600
store=config.STORE_LOCATION)
1602
self.my_location_config._get_option_policy(
1603
'http://www.example.com', 'norecurse_option'),
1606
def test_set_user_option_recurse_false_section(self):
1607
# The following section has recurse=False set. The test is to
1608
# make sure that a normal option can be added to the section,
1609
# converting recurse=False to the norecurse policy.
1610
self.get_branch_config('http://www.example.com/norecurse')
1611
self.callDeprecated(['The recurse option is deprecated as of 0.14. '
1612
'The section "http://www.example.com/norecurse" '
1613
'has been converted to use policies.'],
1614
self.my_config.set_user_option,
1615
'foo', 'bar', store=config.STORE_LOCATION)
1617
self.my_location_config._get_option_policy(
1618
'http://www.example.com/norecurse', 'foo'),
1620
# The previously existing option is still norecurse:
1622
self.my_location_config._get_option_policy(
1623
'http://www.example.com/norecurse', 'normal_option'),
1624
config.POLICY_NORECURSE)
1626
def test_post_commit_default(self):
1627
self.get_branch_config('/a/c')
1628
self.assertEqual('bzrlib.tests.test_config.post_commit',
1629
self.my_config.post_commit())
1631
def get_branch_config(self, location, global_config=None,
1632
location_config=None):
1633
my_branch = FakeBranch(location)
1634
if global_config is None:
1635
global_config = sample_config_text
1636
if location_config is None:
1637
location_config = sample_branches_text
1639
my_global_config = config.GlobalConfig.from_string(global_config,
1641
my_location_config = config.LocationConfig.from_string(
1642
location_config, my_branch.base, save=True)
1643
my_config = config.BranchConfig(my_branch)
1644
self.my_config = my_config
1645
self.my_location_config = my_config._get_location_config()
1647
def test_set_user_setting_sets_and_saves(self):
1648
self.get_branch_config('/a/c')
1649
record = InstrumentedConfigObj("foo")
1650
self.my_location_config._parser = record
1652
self.callDeprecated(['The recurse option is deprecated as of '
1653
'0.14. The section "/a/c" has been '
1654
'converted to use policies.'],
1655
self.my_config.set_user_option,
1656
'foo', 'bar', store=config.STORE_LOCATION)
1657
self.assertEqual([('reload',),
1658
('__contains__', '/a/c'),
1659
('__contains__', '/a/c/'),
1660
('__setitem__', '/a/c', {}),
1661
('__getitem__', '/a/c'),
1662
('__setitem__', 'foo', 'bar'),
1663
('__getitem__', '/a/c'),
1664
('as_bool', 'recurse'),
1665
('__getitem__', '/a/c'),
1666
('__delitem__', 'recurse'),
1667
('__getitem__', '/a/c'),
1669
('__getitem__', '/a/c'),
1670
('__contains__', 'foo:policy'),
1674
def test_set_user_setting_sets_and_saves2(self):
1675
self.get_branch_config('/a/c')
1676
self.assertIs(self.my_config.get_user_option('foo'), None)
1677
self.my_config.set_user_option('foo', 'bar')
1679
self.my_config.branch.control_files.files['branch.conf'].strip(),
1681
self.assertEqual(self.my_config.get_user_option('foo'), 'bar')
1682
self.my_config.set_user_option('foo', 'baz',
1683
store=config.STORE_LOCATION)
1684
self.assertEqual(self.my_config.get_user_option('foo'), 'baz')
1685
self.my_config.set_user_option('foo', 'qux')
1686
self.assertEqual(self.my_config.get_user_option('foo'), 'baz')
1688
def test_get_bzr_remote_path(self):
1689
my_config = config.LocationConfig('/a/c')
1690
self.assertEqual('bzr', my_config.get_bzr_remote_path())
1691
my_config.set_user_option('bzr_remote_path', '/path-bzr')
1692
self.assertEqual('/path-bzr', my_config.get_bzr_remote_path())
1693
self.overrideEnv('BZR_REMOTE_PATH', '/environ-bzr')
1694
self.assertEqual('/environ-bzr', my_config.get_bzr_remote_path())
1697
precedence_global = 'option = global'
1698
precedence_branch = 'option = branch'
1699
precedence_location = """
1703
[http://example.com/specific]
1707
class TestBranchConfigItems(tests.TestCaseInTempDir):
1709
def get_branch_config(self, global_config=None, location=None,
1710
location_config=None, branch_data_config=None):
1711
my_branch = FakeBranch(location)
1712
if global_config is not None:
1713
my_global_config = config.GlobalConfig.from_string(global_config,
1715
if location_config is not None:
1716
my_location_config = config.LocationConfig.from_string(
1717
location_config, my_branch.base, save=True)
1718
my_config = config.BranchConfig(my_branch)
1719
if branch_data_config is not None:
1720
my_config.branch.control_files.files['branch.conf'] = \
413
class TestBranchConfigItems(TestCase):
1724
415
def test_user_id(self):
1725
branch = FakeBranch(user_id='Robert Collins <robertc@example.net>')
416
branch = FakeBranch()
1726
417
my_config = config.BranchConfig(branch)
1727
418
self.assertEqual("Robert Collins <robertc@example.net>",
1728
my_config.username())
1729
my_config.branch.control_files.files['email'] = "John"
1730
my_config.set_user_option('email',
1731
"Robert Collins <robertc@example.org>")
1732
self.assertEqual("John", my_config.username())
1733
del my_config.branch.control_files.files['email']
1734
self.assertEqual("Robert Collins <robertc@example.org>",
1735
my_config.username())
419
my_config._get_user_id())
420
branch.email = "John"
421
self.assertEqual("John", my_config._get_user_id())
1737
423
def test_not_set_in_branch(self):
1738
my_config = self.get_branch_config(global_config=sample_config_text)
1739
self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
424
branch = FakeBranch()
425
my_config = config.BranchConfig(branch)
427
config_file = StringIO(sample_config_text)
428
(my_config._get_location_config().
429
_get_global_config()._get_parser(config_file))
430
self.assertEqual("Robert Collins <robertc@example.com>",
1740
431
my_config._get_user_id())
1741
my_config.branch.control_files.files['email'] = "John"
432
branch.email = "John"
1742
433
self.assertEqual("John", my_config._get_user_id())
1744
def test_BZR_EMAIL_OVERRIDES(self):
1745
self.overrideEnv('BZR_EMAIL', "Robert Collins <robertc@example.org>")
435
def test_BZREMAIL_OVERRIDES(self):
436
os.environ['BZREMAIL'] = "Robert Collins <robertc@example.org>"
1746
437
branch = FakeBranch()
1747
438
my_config = config.BranchConfig(branch)
1748
439
self.assertEqual("Robert Collins <robertc@example.org>",
1749
440
my_config.username())
1751
442
def test_signatures_forced(self):
1752
my_config = self.get_branch_config(
1753
global_config=sample_always_signatures)
1754
self.assertEqual(config.CHECK_NEVER, my_config.signature_checking())
1755
self.assertEqual(config.SIGN_ALWAYS, my_config.signing_policy())
1756
self.assertTrue(my_config.signature_needed())
1758
def test_signatures_forced_branch(self):
1759
my_config = self.get_branch_config(
1760
global_config=sample_ignore_signatures,
1761
branch_data_config=sample_always_signatures)
1762
self.assertEqual(config.CHECK_NEVER, my_config.signature_checking())
1763
self.assertEqual(config.SIGN_ALWAYS, my_config.signing_policy())
1764
self.assertTrue(my_config.signature_needed())
443
branch = FakeBranch()
444
my_config = config.BranchConfig(branch)
445
config_file = StringIO(sample_always_signatures)
446
(my_config._get_location_config().
447
_get_global_config()._get_parser(config_file))
448
self.assertEqual(config.CHECK_ALWAYS, my_config.signature_checking())
1766
450
def test_gpg_signing_command(self):
1767
my_config = self.get_branch_config(
1768
global_config=sample_config_text,
1769
# branch data cannot set gpg_signing_command
1770
branch_data_config="gpg_signing_command=pgp")
451
branch = FakeBranch()
452
my_config = config.BranchConfig(branch)
453
config_file = StringIO(sample_config_text)
454
(my_config._get_location_config().
455
_get_global_config()._get_parser(config_file))
1771
456
self.assertEqual('gnome-gpg', my_config.gpg_signing_command())
1773
def test_get_user_option_global(self):
1774
my_config = self.get_branch_config(global_config=sample_config_text)
1775
self.assertEqual('something',
1776
my_config.get_user_option('user_global_option'))
1778
def test_post_commit_default(self):
1779
my_config = self.get_branch_config(global_config=sample_config_text,
1781
location_config=sample_branches_text)
1782
self.assertEqual(my_config.branch.base, '/a/c')
1783
self.assertEqual('bzrlib.tests.test_config.post_commit',
1784
my_config.post_commit())
1785
my_config.set_user_option('post_commit', 'rmtree_root')
1786
# post-commit is ignored when present in branch data
1787
self.assertEqual('bzrlib.tests.test_config.post_commit',
1788
my_config.post_commit())
1789
my_config.set_user_option('post_commit', 'rmtree_root',
1790
store=config.STORE_LOCATION)
1791
self.assertEqual('rmtree_root', my_config.post_commit())
1793
def test_config_precedence(self):
1794
# FIXME: eager test, luckily no persitent config file makes it fail
1796
my_config = self.get_branch_config(global_config=precedence_global)
1797
self.assertEqual(my_config.get_user_option('option'), 'global')
1798
my_config = self.get_branch_config(global_config=precedence_global,
1799
branch_data_config=precedence_branch)
1800
self.assertEqual(my_config.get_user_option('option'), 'branch')
1801
my_config = self.get_branch_config(
1802
global_config=precedence_global,
1803
branch_data_config=precedence_branch,
1804
location_config=precedence_location)
1805
self.assertEqual(my_config.get_user_option('option'), 'recurse')
1806
my_config = self.get_branch_config(
1807
global_config=precedence_global,
1808
branch_data_config=precedence_branch,
1809
location_config=precedence_location,
1810
location='http://example.com/specific')
1811
self.assertEqual(my_config.get_user_option('option'), 'exact')
1813
def test_get_mail_client(self):
1814
config = self.get_branch_config()
1815
client = config.get_mail_client()
1816
self.assertIsInstance(client, mail_client.DefaultMail)
1819
config.set_user_option('mail_client', 'evolution')
1820
client = config.get_mail_client()
1821
self.assertIsInstance(client, mail_client.Evolution)
1823
config.set_user_option('mail_client', 'kmail')
1824
client = config.get_mail_client()
1825
self.assertIsInstance(client, mail_client.KMail)
1827
config.set_user_option('mail_client', 'mutt')
1828
client = config.get_mail_client()
1829
self.assertIsInstance(client, mail_client.Mutt)
1831
config.set_user_option('mail_client', 'thunderbird')
1832
client = config.get_mail_client()
1833
self.assertIsInstance(client, mail_client.Thunderbird)
1836
config.set_user_option('mail_client', 'default')
1837
client = config.get_mail_client()
1838
self.assertIsInstance(client, mail_client.DefaultMail)
1840
config.set_user_option('mail_client', 'editor')
1841
client = config.get_mail_client()
1842
self.assertIsInstance(client, mail_client.Editor)
1844
config.set_user_option('mail_client', 'mapi')
1845
client = config.get_mail_client()
1846
self.assertIsInstance(client, mail_client.MAPIClient)
1848
config.set_user_option('mail_client', 'xdg-email')
1849
client = config.get_mail_client()
1850
self.assertIsInstance(client, mail_client.XDGEmail)
1852
config.set_user_option('mail_client', 'firebird')
1853
self.assertRaises(errors.UnknownMailClient, config.get_mail_client)
1856
class TestMailAddressExtraction(tests.TestCase):
1858
def test_extract_email_address(self):
1859
self.assertEqual('jane@test.com',
1860
config.extract_email_address('Jane <jane@test.com>'))
1861
self.assertRaises(errors.NoEmailInUsername,
1862
config.extract_email_address, 'Jane Tester')
1864
def test_parse_username(self):
1865
self.assertEqual(('', 'jdoe@example.com'),
1866
config.parse_username('jdoe@example.com'))
1867
self.assertEqual(('', 'jdoe@example.com'),
1868
config.parse_username('<jdoe@example.com>'))
1869
self.assertEqual(('John Doe', 'jdoe@example.com'),
1870
config.parse_username('John Doe <jdoe@example.com>'))
1871
self.assertEqual(('John Doe', ''),
1872
config.parse_username('John Doe'))
1873
self.assertEqual(('John Doe', 'jdoe@example.com'),
1874
config.parse_username('John Doe jdoe@example.com'))
1876
class TestTreeConfig(tests.TestCaseWithTransport):
1878
def test_get_value(self):
1879
"""Test that retreiving a value from a section is possible"""
1880
branch = self.make_branch('.')
1881
tree_config = config.TreeConfig(branch)
1882
tree_config.set_option('value', 'key', 'SECTION')
1883
tree_config.set_option('value2', 'key2')
1884
tree_config.set_option('value3-top', 'key3')
1885
tree_config.set_option('value3-section', 'key3', 'SECTION')
1886
value = tree_config.get_option('key', 'SECTION')
1887
self.assertEqual(value, 'value')
1888
value = tree_config.get_option('key2')
1889
self.assertEqual(value, 'value2')
1890
self.assertEqual(tree_config.get_option('non-existant'), None)
1891
value = tree_config.get_option('non-existant', 'SECTION')
1892
self.assertEqual(value, None)
1893
value = tree_config.get_option('non-existant', default='default')
1894
self.assertEqual(value, 'default')
1895
self.assertEqual(tree_config.get_option('key2', 'NOSECTION'), None)
1896
value = tree_config.get_option('key2', 'NOSECTION', default='default')
1897
self.assertEqual(value, 'default')
1898
value = tree_config.get_option('key3')
1899
self.assertEqual(value, 'value3-top')
1900
value = tree_config.get_option('key3', 'SECTION')
1901
self.assertEqual(value, 'value3-section')
1904
class TestTransportConfig(tests.TestCaseWithTransport):
1906
def test_load_utf8(self):
1907
"""Ensure we can load an utf8-encoded file."""
1908
t = self.get_transport()
1909
unicode_user = u'b\N{Euro Sign}ar'
1910
unicode_content = u'user=%s' % (unicode_user,)
1911
utf8_content = unicode_content.encode('utf8')
1912
# Store the raw content in the config file
1913
t.put_bytes('foo.conf', utf8_content)
1914
conf = config.TransportConfig(t, 'foo.conf')
1915
self.assertEquals(unicode_user, conf.get_option('user'))
1917
def test_load_non_ascii(self):
1918
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
1919
t = self.get_transport()
1920
t.put_bytes('foo.conf', 'user=foo\n#\xff\n')
1921
conf = config.TransportConfig(t, 'foo.conf')
1922
self.assertRaises(errors.ConfigContentError, conf._get_configobj)
1924
def test_load_erroneous_content(self):
1925
"""Ensure we display a proper error on content that can't be parsed."""
1926
t = self.get_transport()
1927
t.put_bytes('foo.conf', '[open_section\n')
1928
conf = config.TransportConfig(t, 'foo.conf')
1929
self.assertRaises(errors.ParseConfigError, conf._get_configobj)
1931
def test_get_value(self):
1932
"""Test that retreiving a value from a section is possible"""
1933
bzrdir_config = config.TransportConfig(self.get_transport('.'),
1935
bzrdir_config.set_option('value', 'key', 'SECTION')
1936
bzrdir_config.set_option('value2', 'key2')
1937
bzrdir_config.set_option('value3-top', 'key3')
1938
bzrdir_config.set_option('value3-section', 'key3', 'SECTION')
1939
value = bzrdir_config.get_option('key', 'SECTION')
1940
self.assertEqual(value, 'value')
1941
value = bzrdir_config.get_option('key2')
1942
self.assertEqual(value, 'value2')
1943
self.assertEqual(bzrdir_config.get_option('non-existant'), None)
1944
value = bzrdir_config.get_option('non-existant', 'SECTION')
1945
self.assertEqual(value, None)
1946
value = bzrdir_config.get_option('non-existant', default='default')
1947
self.assertEqual(value, 'default')
1948
self.assertEqual(bzrdir_config.get_option('key2', 'NOSECTION'), None)
1949
value = bzrdir_config.get_option('key2', 'NOSECTION',
1951
self.assertEqual(value, 'default')
1952
value = bzrdir_config.get_option('key3')
1953
self.assertEqual(value, 'value3-top')
1954
value = bzrdir_config.get_option('key3', 'SECTION')
1955
self.assertEqual(value, 'value3-section')
1957
def test_set_unset_default_stack_on(self):
1958
my_dir = self.make_bzrdir('.')
1959
bzrdir_config = config.BzrDirConfig(my_dir)
1960
self.assertIs(None, bzrdir_config.get_default_stack_on())
1961
bzrdir_config.set_default_stack_on('Foo')
1962
self.assertEqual('Foo', bzrdir_config._config.get_option(
1963
'default_stack_on'))
1964
self.assertEqual('Foo', bzrdir_config.get_default_stack_on())
1965
bzrdir_config.set_default_stack_on(None)
1966
self.assertIs(None, bzrdir_config.get_default_stack_on())
1969
class TestOldConfigHooks(tests.TestCaseWithTransport):
1972
super(TestOldConfigHooks, self).setUp()
1973
create_configs_with_file_option(self)
1975
def assertGetHook(self, conf, name, value):
1979
config.OldConfigHooks.install_named_hook('get', hook, None)
1981
config.OldConfigHooks.uninstall_named_hook, 'get', None)
1982
self.assertLength(0, calls)
1983
actual_value = conf.get_user_option(name)
1984
self.assertEquals(value, actual_value)
1985
self.assertLength(1, calls)
1986
self.assertEquals((conf, name, value), calls[0])
1988
def test_get_hook_bazaar(self):
1989
self.assertGetHook(self.bazaar_config, 'file', 'bazaar')
1991
def test_get_hook_locations(self):
1992
self.assertGetHook(self.locations_config, 'file', 'locations')
1994
def test_get_hook_branch(self):
1995
# Since locations masks branch, we define a different option
1996
self.branch_config.set_user_option('file2', 'branch')
1997
self.assertGetHook(self.branch_config, 'file2', 'branch')
1999
def assertSetHook(self, conf, name, value):
2003
config.OldConfigHooks.install_named_hook('set', hook, None)
2005
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2006
self.assertLength(0, calls)
2007
conf.set_user_option(name, value)
2008
self.assertLength(1, calls)
2009
# We can't assert the conf object below as different configs use
2010
# different means to implement set_user_option and we care only about
2012
self.assertEquals((name, value), calls[0][1:])
2014
def test_set_hook_bazaar(self):
2015
self.assertSetHook(self.bazaar_config, 'foo', 'bazaar')
2017
def test_set_hook_locations(self):
2018
self.assertSetHook(self.locations_config, 'foo', 'locations')
2020
def test_set_hook_branch(self):
2021
self.assertSetHook(self.branch_config, 'foo', 'branch')
2023
def assertRemoveHook(self, conf, name, section_name=None):
2027
config.OldConfigHooks.install_named_hook('remove', hook, None)
2029
config.OldConfigHooks.uninstall_named_hook, 'remove', None)
2030
self.assertLength(0, calls)
2031
conf.remove_user_option(name, section_name)
2032
self.assertLength(1, calls)
2033
# We can't assert the conf object below as different configs use
2034
# different means to implement remove_user_option and we care only about
2036
self.assertEquals((name,), calls[0][1:])
2038
def test_remove_hook_bazaar(self):
2039
self.assertRemoveHook(self.bazaar_config, 'file')
2041
def test_remove_hook_locations(self):
2042
self.assertRemoveHook(self.locations_config, 'file',
2043
self.locations_config.location)
2045
def test_remove_hook_branch(self):
2046
self.assertRemoveHook(self.branch_config, 'file')
2048
def assertLoadHook(self, name, conf_class, *conf_args):
2052
config.OldConfigHooks.install_named_hook('load', hook, None)
2054
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2055
self.assertLength(0, calls)
2057
conf = conf_class(*conf_args)
2058
# Access an option to trigger a load
2059
conf.get_user_option(name)
2060
self.assertLength(1, calls)
2061
# Since we can't assert about conf, we just use the number of calls ;-/
2063
def test_load_hook_bazaar(self):
2064
self.assertLoadHook('file', config.GlobalConfig)
2066
def test_load_hook_locations(self):
2067
self.assertLoadHook('file', config.LocationConfig, self.tree.basedir)
2069
def test_load_hook_branch(self):
2070
self.assertLoadHook('file', config.BranchConfig, self.tree.branch)
2072
def assertSaveHook(self, conf):
2076
config.OldConfigHooks.install_named_hook('save', hook, None)
2078
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2079
self.assertLength(0, calls)
2080
# Setting an option triggers a save
2081
conf.set_user_option('foo', 'bar')
2082
self.assertLength(1, calls)
2083
# Since we can't assert about conf, we just use the number of calls ;-/
2085
def test_save_hook_bazaar(self):
2086
self.assertSaveHook(self.bazaar_config)
2088
def test_save_hook_locations(self):
2089
self.assertSaveHook(self.locations_config)
2091
def test_save_hook_branch(self):
2092
self.assertSaveHook(self.branch_config)
2095
class TestOldConfigHooksForRemote(tests.TestCaseWithTransport):
2096
"""Tests config hooks for remote configs.
2098
No tests for the remove hook as this is not implemented there.
2102
super(TestOldConfigHooksForRemote, self).setUp()
2103
self.transport_server = test_server.SmartTCPServer_for_testing
2104
create_configs_with_file_option(self)
2106
def assertGetHook(self, conf, name, value):
2110
config.OldConfigHooks.install_named_hook('get', hook, None)
2112
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2113
self.assertLength(0, calls)
2114
actual_value = conf.get_option(name)
2115
self.assertEquals(value, actual_value)
2116
self.assertLength(1, calls)
2117
self.assertEquals((conf, name, value), calls[0])
2119
def test_get_hook_remote_branch(self):
2120
remote_branch = branch.Branch.open(self.get_url('tree'))
2121
self.assertGetHook(remote_branch._get_config(), 'file', 'branch')
2123
def test_get_hook_remote_bzrdir(self):
2124
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2125
conf = remote_bzrdir._get_config()
2126
conf.set_option('remotedir', 'file')
2127
self.assertGetHook(conf, 'file', 'remotedir')
2129
def assertSetHook(self, conf, name, value):
2133
config.OldConfigHooks.install_named_hook('set', hook, None)
2135
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2136
self.assertLength(0, calls)
2137
conf.set_option(value, name)
2138
self.assertLength(1, calls)
2139
# We can't assert the conf object below as different configs use
2140
# different means to implement set_user_option and we care only about
2142
self.assertEquals((name, value), calls[0][1:])
2144
def test_set_hook_remote_branch(self):
2145
remote_branch = branch.Branch.open(self.get_url('tree'))
2146
self.addCleanup(remote_branch.lock_write().unlock)
2147
self.assertSetHook(remote_branch._get_config(), 'file', 'remote')
2149
def test_set_hook_remote_bzrdir(self):
2150
remote_branch = branch.Branch.open(self.get_url('tree'))
2151
self.addCleanup(remote_branch.lock_write().unlock)
2152
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2153
self.assertSetHook(remote_bzrdir._get_config(), 'file', 'remotedir')
2155
def assertLoadHook(self, expected_nb_calls, name, conf_class, *conf_args):
2159
config.OldConfigHooks.install_named_hook('load', hook, None)
2161
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2162
self.assertLength(0, calls)
2164
conf = conf_class(*conf_args)
2165
# Access an option to trigger a load
2166
conf.get_option(name)
2167
self.assertLength(expected_nb_calls, calls)
2168
# Since we can't assert about conf, we just use the number of calls ;-/
2170
def test_load_hook_remote_branch(self):
2171
remote_branch = branch.Branch.open(self.get_url('tree'))
2172
self.assertLoadHook(1, 'file', remote.RemoteBranchConfig, remote_branch)
2174
def test_load_hook_remote_bzrdir(self):
2175
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2176
# The config file doesn't exist, set an option to force its creation
2177
conf = remote_bzrdir._get_config()
2178
conf.set_option('remotedir', 'file')
2179
# We get one call for the server and one call for the client, this is
2180
# caused by the differences in implementations betwen
2181
# SmartServerBzrDirRequestConfigFile (in smart/bzrdir.py) and
2182
# SmartServerBranchGetConfigFile (in smart/branch.py)
2183
self.assertLoadHook(2 ,'file', remote.RemoteBzrDirConfig, remote_bzrdir)
2185
def assertSaveHook(self, conf):
2189
config.OldConfigHooks.install_named_hook('save', hook, None)
2191
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2192
self.assertLength(0, calls)
2193
# Setting an option triggers a save
2194
conf.set_option('foo', 'bar')
2195
self.assertLength(1, calls)
2196
# Since we can't assert about conf, we just use the number of calls ;-/
2198
def test_save_hook_remote_branch(self):
2199
remote_branch = branch.Branch.open(self.get_url('tree'))
2200
self.addCleanup(remote_branch.lock_write().unlock)
2201
self.assertSaveHook(remote_branch._get_config())
2203
def test_save_hook_remote_bzrdir(self):
2204
remote_branch = branch.Branch.open(self.get_url('tree'))
2205
self.addCleanup(remote_branch.lock_write().unlock)
2206
remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2207
self.assertSaveHook(remote_bzrdir._get_config())
2210
class TestOption(tests.TestCase):
2212
def test_default_value(self):
2213
opt = config.Option('foo', default='bar')
2214
self.assertEquals('bar', opt.get_default())
2217
class TestOptionRegistry(tests.TestCase):
2220
super(TestOptionRegistry, self).setUp()
2221
# Always start with an empty registry
2222
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2223
self.registry = config.option_registry
2225
def test_register(self):
2226
opt = config.Option('foo')
2227
self.registry.register(opt)
2228
self.assertIs(opt, self.registry.get('foo'))
2230
def test_registered_help(self):
2231
opt = config.Option('foo', help='A simple option')
2232
self.registry.register(opt)
2233
self.assertEquals('A simple option', self.registry.get_help('foo'))
2235
lazy_option = config.Option('lazy_foo', help='Lazy help')
2237
def test_register_lazy(self):
2238
self.registry.register_lazy('lazy_foo', self.__module__,
2239
'TestOptionRegistry.lazy_option')
2240
self.assertIs(self.lazy_option, self.registry.get('lazy_foo'))
2242
def test_registered_lazy_help(self):
2243
self.registry.register_lazy('lazy_foo', self.__module__,
2244
'TestOptionRegistry.lazy_option')
2245
self.assertEquals('Lazy help', self.registry.get_help('lazy_foo'))
2248
class TestRegisteredOptions(tests.TestCase):
2249
"""All registered options should verify some constraints."""
2251
scenarios = [(key, {'option_name': key, 'option': option}) for key, option
2252
in config.option_registry.iteritems()]
2255
super(TestRegisteredOptions, self).setUp()
2256
self.registry = config.option_registry
2258
def test_proper_name(self):
2259
# An option should be registered under its own name, this can't be
2260
# checked at registration time for the lazy ones.
2261
self.assertEquals(self.option_name, self.option.name)
2263
def test_help_is_set(self):
2264
option_help = self.registry.get_help(self.option_name)
2265
self.assertNotEquals(None, option_help)
2266
# Come on, think about the user, he really wants to know what the
2268
self.assertIsNot(None, option_help)
2269
self.assertNotEquals('', option_help)
2272
class TestSection(tests.TestCase):
2274
# FIXME: Parametrize so that all sections produced by Stores run these
2275
# tests -- vila 2011-04-01
2277
def test_get_a_value(self):
2278
a_dict = dict(foo='bar')
2279
section = config.Section('myID', a_dict)
2280
self.assertEquals('bar', section.get('foo'))
2282
def test_get_unknown_option(self):
2284
section = config.Section(None, a_dict)
2285
self.assertEquals('out of thin air',
2286
section.get('foo', 'out of thin air'))
2288
def test_options_is_shared(self):
2290
section = config.Section(None, a_dict)
2291
self.assertIs(a_dict, section.options)
2294
class TestMutableSection(tests.TestCase):
2296
# FIXME: Parametrize so that all sections (including os.environ and the
2297
# ones produced by Stores) run these tests -- vila 2011-04-01
2300
a_dict = dict(foo='bar')
2301
section = config.MutableSection('myID', a_dict)
2302
section.set('foo', 'new_value')
2303
self.assertEquals('new_value', section.get('foo'))
2304
# The change appears in the shared section
2305
self.assertEquals('new_value', a_dict.get('foo'))
2306
# We keep track of the change
2307
self.assertTrue('foo' in section.orig)
2308
self.assertEquals('bar', section.orig.get('foo'))
2310
def test_set_preserve_original_once(self):
2311
a_dict = dict(foo='bar')
2312
section = config.MutableSection('myID', a_dict)
2313
section.set('foo', 'first_value')
2314
section.set('foo', 'second_value')
2315
# We keep track of the original value
2316
self.assertTrue('foo' in section.orig)
2317
self.assertEquals('bar', section.orig.get('foo'))
2319
def test_remove(self):
2320
a_dict = dict(foo='bar')
2321
section = config.MutableSection('myID', a_dict)
2322
section.remove('foo')
2323
# We get None for unknown options via the default value
2324
self.assertEquals(None, section.get('foo'))
2325
# Or we just get the default value
2326
self.assertEquals('unknown', section.get('foo', 'unknown'))
2327
self.assertFalse('foo' in section.options)
2328
# We keep track of the deletion
2329
self.assertTrue('foo' in section.orig)
2330
self.assertEquals('bar', section.orig.get('foo'))
2332
def test_remove_new_option(self):
2334
section = config.MutableSection('myID', a_dict)
2335
section.set('foo', 'bar')
2336
section.remove('foo')
2337
self.assertFalse('foo' in section.options)
2338
# The option didn't exist initially so it we need to keep track of it
2339
# with a special value
2340
self.assertTrue('foo' in section.orig)
2341
self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
2344
class TestStore(tests.TestCaseWithTransport):
2346
def assertSectionContent(self, expected, section):
2347
"""Assert that some options have the proper values in a section."""
2348
expected_name, expected_options = expected
2349
self.assertEquals(expected_name, section.id)
2352
dict([(k, section.get(k)) for k in expected_options.keys()]))
2355
class TestReadonlyStore(TestStore):
2357
scenarios = [(key, {'get_store': builder}) for key, builder
2358
in config.test_store_builder_registry.iteritems()]
2361
super(TestReadonlyStore, self).setUp()
2363
def test_building_delays_load(self):
2364
store = self.get_store(self)
2365
self.assertEquals(False, store.is_loaded())
2366
store._load_from_string('')
2367
self.assertEquals(True, store.is_loaded())
2369
def test_get_no_sections_for_empty(self):
2370
store = self.get_store(self)
2371
store._load_from_string('')
2372
self.assertEquals([], list(store.get_sections()))
2374
def test_get_default_section(self):
2375
store = self.get_store(self)
2376
store._load_from_string('foo=bar')
2377
sections = list(store.get_sections())
2378
self.assertLength(1, sections)
2379
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2381
def test_get_named_section(self):
2382
store = self.get_store(self)
2383
store._load_from_string('[baz]\nfoo=bar')
2384
sections = list(store.get_sections())
2385
self.assertLength(1, sections)
2386
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2388
def test_load_from_string_fails_for_non_empty_store(self):
2389
store = self.get_store(self)
2390
store._load_from_string('foo=bar')
2391
self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2394
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2395
"""Simulate loading a config store without content of various encodings.
2397
All files produced by bzr are in utf8 content.
2399
Users may modify them manually and end up with a file that can't be
2400
loaded. We need to issue proper error messages in this case.
2403
invalid_utf8_char = '\xff'
2405
def test_load_utf8(self):
2406
"""Ensure we can load an utf8-encoded file."""
2407
t = self.get_transport()
2408
# From http://pad.lv/799212
2409
unicode_user = u'b\N{Euro Sign}ar'
2410
unicode_content = u'user=%s' % (unicode_user,)
2411
utf8_content = unicode_content.encode('utf8')
2412
# Store the raw content in the config file
2413
t.put_bytes('foo.conf', utf8_content)
2414
store = config.IniFileStore(t, 'foo.conf')
2416
stack = config.Stack([store.get_sections], store)
2417
self.assertEquals(unicode_user, stack.get('user'))
2419
def test_load_non_ascii(self):
2420
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2421
t = self.get_transport()
2422
t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2423
store = config.IniFileStore(t, 'foo.conf')
2424
self.assertRaises(errors.ConfigContentError, store.load)
2426
def test_load_erroneous_content(self):
2427
"""Ensure we display a proper error on content that can't be parsed."""
2428
t = self.get_transport()
2429
t.put_bytes('foo.conf', '[open_section\n')
2430
store = config.IniFileStore(t, 'foo.conf')
2431
self.assertRaises(errors.ParseConfigError, store.load)
2434
class TestIniConfigContent(tests.TestCaseWithTransport):
2435
"""Simulate loading a IniBasedConfig without content of various encodings.
2437
All files produced by bzr are in utf8 content.
2439
Users may modify them manually and end up with a file that can't be
2440
loaded. We need to issue proper error messages in this case.
2443
invalid_utf8_char = '\xff'
2445
def test_load_utf8(self):
2446
"""Ensure we can load an utf8-encoded file."""
2447
# From http://pad.lv/799212
2448
unicode_user = u'b\N{Euro Sign}ar'
2449
unicode_content = u'user=%s' % (unicode_user,)
2450
utf8_content = unicode_content.encode('utf8')
2451
# Store the raw content in the config file
2452
with open('foo.conf', 'wb') as f:
2453
f.write(utf8_content)
2454
conf = config.IniBasedConfig(file_name='foo.conf')
2455
self.assertEquals(unicode_user, conf.get_user_option('user'))
2457
def test_load_badly_encoded_content(self):
2458
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2459
with open('foo.conf', 'wb') as f:
2460
f.write('user=foo\n#%s\n' % (self.invalid_utf8_char,))
2461
conf = config.IniBasedConfig(file_name='foo.conf')
2462
self.assertRaises(errors.ConfigContentError, conf._get_parser)
2464
def test_load_erroneous_content(self):
2465
"""Ensure we display a proper error on content that can't be parsed."""
2466
with open('foo.conf', 'wb') as f:
2467
f.write('[open_section\n')
2468
conf = config.IniBasedConfig(file_name='foo.conf')
2469
self.assertRaises(errors.ParseConfigError, conf._get_parser)
2472
class TestMutableStore(TestStore):
2474
scenarios = [(key, {'store_id': key, 'get_store': builder}) for key, builder
2475
in config.test_store_builder_registry.iteritems()]
2478
super(TestMutableStore, self).setUp()
2479
self.transport = self.get_transport()
2481
def has_store(self, store):
2482
store_basename = urlutils.relative_url(self.transport.external_url(),
2483
store.external_url())
2484
return self.transport.has(store_basename)
2486
def test_save_empty_creates_no_file(self):
2487
# FIXME: There should be a better way than relying on the test
2488
# parametrization to identify branch.conf -- vila 2011-0526
2489
if self.store_id in ('branch', 'remote_branch'):
2490
raise tests.TestNotApplicable(
2491
'branch.conf is *always* created when a branch is initialized')
2492
store = self.get_store(self)
2494
self.assertEquals(False, self.has_store(store))
2496
def test_save_emptied_succeeds(self):
2497
store = self.get_store(self)
2498
store._load_from_string('foo=bar\n')
2499
section = store.get_mutable_section(None)
2500
section.remove('foo')
2502
self.assertEquals(True, self.has_store(store))
2503
modified_store = self.get_store(self)
2504
sections = list(modified_store.get_sections())
2505
self.assertLength(0, sections)
2507
def test_save_with_content_succeeds(self):
2508
# FIXME: There should be a better way than relying on the test
2509
# parametrization to identify branch.conf -- vila 2011-0526
2510
if self.store_id in ('branch', 'remote_branch'):
2511
raise tests.TestNotApplicable(
2512
'branch.conf is *always* created when a branch is initialized')
2513
store = self.get_store(self)
2514
store._load_from_string('foo=bar\n')
2515
self.assertEquals(False, self.has_store(store))
2517
self.assertEquals(True, self.has_store(store))
2518
modified_store = self.get_store(self)
2519
sections = list(modified_store.get_sections())
2520
self.assertLength(1, sections)
2521
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2523
def test_set_option_in_empty_store(self):
2524
store = self.get_store(self)
2525
section = store.get_mutable_section(None)
2526
section.set('foo', 'bar')
2528
modified_store = self.get_store(self)
2529
sections = list(modified_store.get_sections())
2530
self.assertLength(1, sections)
2531
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2533
def test_set_option_in_default_section(self):
2534
store = self.get_store(self)
2535
store._load_from_string('')
2536
section = store.get_mutable_section(None)
2537
section.set('foo', 'bar')
2539
modified_store = self.get_store(self)
2540
sections = list(modified_store.get_sections())
2541
self.assertLength(1, sections)
2542
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2544
def test_set_option_in_named_section(self):
2545
store = self.get_store(self)
2546
store._load_from_string('')
2547
section = store.get_mutable_section('baz')
2548
section.set('foo', 'bar')
2550
modified_store = self.get_store(self)
2551
sections = list(modified_store.get_sections())
2552
self.assertLength(1, sections)
2553
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2555
def test_load_hook(self):
2556
# We first needs to ensure that the store exists
2557
store = self.get_store(self)
2558
section = store.get_mutable_section('baz')
2559
section.set('foo', 'bar')
2561
# Now we can try to load it
2562
store = self.get_store(self)
2566
config.ConfigHooks.install_named_hook('load', hook, None)
2567
self.assertLength(0, calls)
2569
self.assertLength(1, calls)
2570
self.assertEquals((store,), calls[0])
2572
def test_save_hook(self):
2576
config.ConfigHooks.install_named_hook('save', hook, None)
2577
self.assertLength(0, calls)
2578
store = self.get_store(self)
2579
section = store.get_mutable_section('baz')
2580
section.set('foo', 'bar')
2582
self.assertLength(1, calls)
2583
self.assertEquals((store,), calls[0])
2586
class TestIniFileStore(TestStore):
2588
def test_loading_unknown_file_fails(self):
2589
store = config.IniFileStore(self.get_transport(), 'I-do-not-exist')
2590
self.assertRaises(errors.NoSuchFile, store.load)
2592
def test_invalid_content(self):
2593
store = config.IniFileStore(self.get_transport(), 'foo.conf', )
2594
self.assertEquals(False, store.is_loaded())
2595
exc = self.assertRaises(
2596
errors.ParseConfigError, store._load_from_string,
2597
'this is invalid !')
2598
self.assertEndsWith(exc.filename, 'foo.conf')
2599
# And the load failed
2600
self.assertEquals(False, store.is_loaded())
2602
def test_get_embedded_sections(self):
2603
# A more complicated example (which also shows that section names and
2604
# option names share the same name space...)
2605
# FIXME: This should be fixed by forbidding dicts as values ?
2606
# -- vila 2011-04-05
2607
store = config.IniFileStore(self.get_transport(), 'foo.conf', )
2608
store._load_from_string('''
2612
foo_in_DEFAULT=foo_DEFAULT
2620
sections = list(store.get_sections())
2621
self.assertLength(4, sections)
2622
# The default section has no name.
2623
# List values are provided as lists
2624
self.assertSectionContent((None, {'foo': 'bar', 'l': ['1', '2']}),
2626
self.assertSectionContent(
2627
('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
2628
self.assertSectionContent(
2629
('bar', {'foo_in_bar': 'barbar'}), sections[2])
2630
# sub sections are provided as embedded dicts.
2631
self.assertSectionContent(
2632
('baz', {'foo_in_baz': 'barbaz', 'qux': {'foo_in_qux': 'quux'}}),
2636
class TestLockableIniFileStore(TestStore):
2638
def test_create_store_in_created_dir(self):
2639
self.assertPathDoesNotExist('dir')
2640
t = self.get_transport('dir/subdir')
2641
store = config.LockableIniFileStore(t, 'foo.conf')
2642
store.get_mutable_section(None).set('foo', 'bar')
2644
self.assertPathExists('dir/subdir')
2647
class TestConcurrentStoreUpdates(TestStore):
2648
"""Test that Stores properly handle conccurent updates.
2650
New Store implementation may fail some of these tests but until such
2651
implementations exist it's hard to properly filter them from the scenarios
2652
applied here. If you encounter such a case, contact the bzr devs.
2655
scenarios = [(key, {'get_stack': builder}) for key, builder
2656
in config.test_stack_builder_registry.iteritems()]
2659
super(TestConcurrentStoreUpdates, self).setUp()
2660
self._content = 'one=1\ntwo=2\n'
2661
self.stack = self.get_stack(self)
2662
if not isinstance(self.stack, config._CompatibleStack):
2663
raise tests.TestNotApplicable(
2664
'%s is not meant to be compatible with the old config design'
2666
self.stack.store._load_from_string(self._content)
2668
self.stack.store.save()
2670
def test_simple_read_access(self):
2671
self.assertEquals('1', self.stack.get('one'))
2673
def test_simple_write_access(self):
2674
self.stack.set('one', 'one')
2675
self.assertEquals('one', self.stack.get('one'))
2677
def test_listen_to_the_last_speaker(self):
2679
c2 = self.get_stack(self)
2680
c1.set('one', 'ONE')
2681
c2.set('two', 'TWO')
2682
self.assertEquals('ONE', c1.get('one'))
2683
self.assertEquals('TWO', c2.get('two'))
2684
# The second update respect the first one
2685
self.assertEquals('ONE', c2.get('one'))
2687
def test_last_speaker_wins(self):
2688
# If the same config is not shared, the same variable modified twice
2689
# can only see a single result.
2691
c2 = self.get_stack(self)
2694
self.assertEquals('c2', c2.get('one'))
2695
# The first modification is still available until another refresh
2697
self.assertEquals('c1', c1.get('one'))
2698
c1.set('two', 'done')
2699
self.assertEquals('c2', c1.get('one'))
2701
def test_writes_are_serialized(self):
2703
c2 = self.get_stack(self)
2705
# We spawn a thread that will pause *during* the config saving.
2706
before_writing = threading.Event()
2707
after_writing = threading.Event()
2708
writing_done = threading.Event()
2709
c1_save_without_locking_orig = c1.store.save_without_locking
2710
def c1_save_without_locking():
2711
before_writing.set()
2712
c1_save_without_locking_orig()
2713
# The lock is held. We wait for the main thread to decide when to
2715
after_writing.wait()
2716
c1.store.save_without_locking = c1_save_without_locking
2720
t1 = threading.Thread(target=c1_set)
2721
# Collect the thread after the test
2722
self.addCleanup(t1.join)
2723
# Be ready to unblock the thread if the test goes wrong
2724
self.addCleanup(after_writing.set)
2726
before_writing.wait()
2727
self.assertRaises(errors.LockContention,
2728
c2.set, 'one', 'c2')
2729
self.assertEquals('c1', c1.get('one'))
2730
# Let the lock be released
2734
self.assertEquals('c2', c2.get('one'))
2736
def test_read_while_writing(self):
2738
# We spawn a thread that will pause *during* the write
2739
ready_to_write = threading.Event()
2740
do_writing = threading.Event()
2741
writing_done = threading.Event()
2742
# We override the _save implementation so we know the store is locked
2743
c1_save_without_locking_orig = c1.store.save_without_locking
2744
def c1_save_without_locking():
2745
ready_to_write.set()
2746
# The lock is held. We wait for the main thread to decide when to
2749
c1_save_without_locking_orig()
2751
c1.store.save_without_locking = c1_save_without_locking
2754
t1 = threading.Thread(target=c1_set)
2755
# Collect the thread after the test
2756
self.addCleanup(t1.join)
2757
# Be ready to unblock the thread if the test goes wrong
2758
self.addCleanup(do_writing.set)
2760
# Ensure the thread is ready to write
2761
ready_to_write.wait()
2762
self.assertEquals('c1', c1.get('one'))
2763
# If we read during the write, we get the old value
2764
c2 = self.get_stack(self)
2765
self.assertEquals('1', c2.get('one'))
2766
# Let the writing occur and ensure it occurred
2769
# Now we get the updated value
2770
c3 = self.get_stack(self)
2771
self.assertEquals('c1', c3.get('one'))
2773
# FIXME: It may be worth looking into removing the lock dir when it's not
2774
# needed anymore and look at possible fallouts for concurrent lockers. This
2775
# will matter if/when we use config files outside of bazaar directories
2776
# (.bazaar or .bzr) -- vila 20110-04-11
2779
class TestSectionMatcher(TestStore):
2781
scenarios = [('location', {'matcher': config.LocationMatcher})]
2783
def get_store(self, file_name):
2784
return config.IniFileStore(self.get_readonly_transport(), file_name)
2786
def test_no_matches_for_empty_stores(self):
2787
store = self.get_store('foo.conf')
2788
store._load_from_string('')
2789
matcher = self.matcher(store, '/bar')
2790
self.assertEquals([], list(matcher.get_sections()))
2792
def test_build_doesnt_load_store(self):
2793
store = self.get_store('foo.conf')
2794
matcher = self.matcher(store, '/bar')
2795
self.assertFalse(store.is_loaded())
2798
class TestLocationSection(tests.TestCase):
2800
def get_section(self, options, extra_path):
2801
section = config.Section('foo', options)
2802
# We don't care about the length so we use '0'
2803
return config.LocationSection(section, 0, extra_path)
2805
def test_simple_option(self):
2806
section = self.get_section({'foo': 'bar'}, '')
2807
self.assertEquals('bar', section.get('foo'))
2809
def test_option_with_extra_path(self):
2810
section = self.get_section({'foo': 'bar', 'foo:policy': 'appendpath'},
2812
self.assertEquals('bar/baz', section.get('foo'))
2814
def test_invalid_policy(self):
2815
section = self.get_section({'foo': 'bar', 'foo:policy': 'die'},
2817
# invalid policies are ignored
2818
self.assertEquals('bar', section.get('foo'))
2821
class TestLocationMatcher(TestStore):
2823
def get_store(self, file_name):
2824
return config.IniFileStore(self.get_readonly_transport(), file_name)
2826
def test_more_specific_sections_first(self):
2827
store = self.get_store('foo.conf')
2828
store._load_from_string('''
2834
self.assertEquals(['/foo', '/foo/bar'],
2835
[section.id for section in store.get_sections()])
2836
matcher = config.LocationMatcher(store, '/foo/bar/baz')
2837
sections = list(matcher.get_sections())
2838
self.assertEquals([3, 2],
2839
[section.length for section in sections])
2840
self.assertEquals(['/foo/bar', '/foo'],
2841
[section.id for section in sections])
2842
self.assertEquals(['baz', 'bar/baz'],
2843
[section.extra_path for section in sections])
2845
def test_appendpath_in_no_name_section(self):
2846
# It's a bit weird to allow appendpath in a no-name section, but
2847
# someone may found a use for it
2848
store = self.get_store('foo.conf')
2849
store._load_from_string('''
2851
foo:policy = appendpath
2853
matcher = config.LocationMatcher(store, 'dir/subdir')
2854
sections = list(matcher.get_sections())
2855
self.assertLength(1, sections)
2856
self.assertEquals('bar/dir/subdir', sections[0].get('foo'))
2858
def test_file_urls_are_normalized(self):
2859
store = self.get_store('foo.conf')
2860
if sys.platform == 'win32':
2861
expected_url = 'file:///C:/dir/subdir'
2862
expected_location = 'C:/dir/subdir'
2864
expected_url = 'file:///dir/subdir'
2865
expected_location = '/dir/subdir'
2866
matcher = config.LocationMatcher(store, expected_url)
2867
self.assertEquals(expected_location, matcher.location)
2870
class TestStackGet(tests.TestCase):
2872
# FIXME: This should be parametrized for all known Stack or dedicated
2873
# paramerized tests created to avoid bloating -- vila 2011-03-31
2875
def overrideOptionRegistry(self):
2876
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2878
def test_single_config_get(self):
2879
conf = dict(foo='bar')
2880
conf_stack = config.Stack([conf])
2881
self.assertEquals('bar', conf_stack.get('foo'))
2883
def test_get_with_registered_default_value(self):
2884
conf_stack = config.Stack([dict()])
2885
opt = config.Option('foo', default='bar')
2886
self.overrideOptionRegistry()
2887
config.option_registry.register('foo', opt)
2888
self.assertEquals('bar', conf_stack.get('foo'))
2890
def test_get_without_registered_default_value(self):
2891
conf_stack = config.Stack([dict()])
2892
opt = config.Option('foo')
2893
self.overrideOptionRegistry()
2894
config.option_registry.register('foo', opt)
2895
self.assertEquals(None, conf_stack.get('foo'))
2897
def test_get_without_default_value_for_not_registered(self):
2898
conf_stack = config.Stack([dict()])
2899
opt = config.Option('foo')
2900
self.overrideOptionRegistry()
2901
self.assertEquals(None, conf_stack.get('foo'))
2903
def test_get_first_definition(self):
2904
conf1 = dict(foo='bar')
2905
conf2 = dict(foo='baz')
2906
conf_stack = config.Stack([conf1, conf2])
2907
self.assertEquals('bar', conf_stack.get('foo'))
2909
def test_get_embedded_definition(self):
2910
conf1 = dict(yy='12')
2911
conf2 = config.Stack([dict(xx='42'), dict(foo='baz')])
2912
conf_stack = config.Stack([conf1, conf2])
2913
self.assertEquals('baz', conf_stack.get('foo'))
2915
def test_get_for_empty_section_callable(self):
2916
conf_stack = config.Stack([lambda : []])
2917
self.assertEquals(None, conf_stack.get('foo'))
2919
def test_get_for_broken_callable(self):
2920
# Trying to use and invalid callable raises an exception on first use
2921
conf_stack = config.Stack([lambda : object()])
2922
self.assertRaises(TypeError, conf_stack.get, 'foo')
2925
class TestStackWithTransport(tests.TestCaseWithTransport):
2927
scenarios = [(key, {'get_stack': builder}) for key, builder
2928
in config.test_stack_builder_registry.iteritems()]
2931
class TestConcreteStacks(TestStackWithTransport):
2933
def test_build_stack(self):
2934
# Just a smoke test to help debug builders
2935
stack = self.get_stack(self)
2938
class TestStackGet(TestStackWithTransport):
2941
super(TestStackGet, self).setUp()
2942
self.conf = self.get_stack(self)
2944
def test_get_for_empty_stack(self):
2945
self.assertEquals(None, self.conf.get('foo'))
2947
def test_get_hook(self):
2948
self.conf.store._load_from_string('foo=bar')
2952
config.ConfigHooks.install_named_hook('get', hook, None)
2953
self.assertLength(0, calls)
2954
value = self.conf.get('foo')
2955
self.assertEquals('bar', value)
2956
self.assertLength(1, calls)
2957
self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
2960
class TestStackGetWithConverter(TestStackGet):
2963
super(TestStackGetWithConverter, self).setUp()
2964
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2965
self.registry = config.option_registry
2967
def register_bool_option(self, name, default, invalid=None):
2968
b = config.Option(name, default=default, help='A boolean.',
2969
from_unicode=config.bool_from_store,
2971
self.registry.register(b)
2973
def test_get_with_bool_not_defined_default_true(self):
2974
self.register_bool_option('foo', True)
2975
self.assertEquals(True, self.conf.get('foo'))
2977
def test_get_with_bool_not_defined_default_false(self):
2978
self.register_bool_option('foo', False)
2979
self.assertEquals(False, self.conf.get('foo'))
2981
def test_get_with_bool_converter_not_default(self):
2982
self.register_bool_option('foo', False)
2983
self.conf.store._load_from_string('foo=yes')
2984
self.assertEquals(True, self.conf.get('foo'))
2986
def test_get_with_bool_converter_invalid_string(self):
2987
self.register_bool_option('foo', False)
2988
self.conf.store._load_from_string('foo=not-a-boolean')
2989
self.assertEquals(False, self.conf.get('foo'))
2991
def test_get_with_bool_converter_invalid_list(self):
2992
self.register_bool_option('foo', False)
2993
self.conf.store._load_from_string('foo=not,a,boolean')
2994
self.assertEquals(False, self.conf.get('foo'))
2996
def test_get_invalid_warns(self):
2997
self.register_bool_option('foo', False, invalid='warning')
2998
self.conf.store._load_from_string('foo=not-a-boolean')
3001
warnings.append(args[0] % args[1:])
3002
self.overrideAttr(trace, 'warning', warning)
3003
self.assertEquals(False, self.conf.get('foo'))
3004
self.assertLength(1, warnings)
3005
self.assertEquals('Value "not-a-boolean" is not valid for "foo"',
3008
def test_get_invalid_errors(self):
3009
self.register_bool_option('foo', False, invalid='error')
3010
self.conf.store._load_from_string('foo=not-a-boolean')
3011
self.assertRaises(errors.ConfigOptionValueError, self.conf.get, 'foo')
3013
def register_integer_option(self, name, default):
3014
i = config.Option(name, default=default, help='An integer.',
3015
from_unicode=config.int_from_store)
3016
self.registry.register(i)
3018
def test_get_with_integer_not_defined_returns_default(self):
3019
self.register_integer_option('foo', 42)
3020
self.assertEquals(42, self.conf.get('foo'))
3022
def test_get_with_integer_converter_not_default(self):
3023
self.register_integer_option('foo', 42)
3024
self.conf.store._load_from_string('foo=16')
3025
self.assertEquals(16, self.conf.get('foo'))
3027
def test_get_with_integer_converter_invalid_string(self):
3028
# We don't set a default value
3029
self.register_integer_option('foo', None)
3030
self.conf.store._load_from_string('foo=forty-two')
3031
# No default value, so we should get None
3032
self.assertEquals(None, self.conf.get('foo'))
3034
def test_get_with_integer_converter_invalid_list(self):
3035
# We don't set a default value
3036
self.register_integer_option('foo', None)
3037
self.conf.store._load_from_string('foo=a,list')
3038
# No default value, so we should get None
3039
self.assertEquals(None, self.conf.get('foo'))
3041
def register_list_option(self, name, default):
3042
l = config.Option(name, default=default, help='A list.',
3043
from_unicode=config.list_from_store)
3044
self.registry.register(l)
3046
def test_get_with_list_not_defined_returns_default(self):
3047
self.register_list_option('foo', [])
3048
self.assertEquals([], self.conf.get('foo'))
3050
def test_get_with_list_converter_nothing(self):
3051
self.register_list_option('foo', [1])
3052
self.conf.store._load_from_string('foo=')
3053
self.assertEquals([], self.conf.get('foo'))
3055
def test_get_with_list_converter_no_item(self):
3056
self.register_list_option('foo', [1])
3057
self.conf.store._load_from_string('foo=,')
3058
self.assertEquals([], self.conf.get('foo'))
3060
def test_get_with_list_converter_one_boolean(self):
3061
self.register_list_option('foo', [1])
3062
self.conf.store._load_from_string('foo=True')
3063
# We get a list of one string
3064
self.assertEquals(['True'], self.conf.get('foo'))
3066
def test_get_with_list_converter_one_integer(self):
3067
self.register_list_option('foo', [1])
3068
self.conf.store._load_from_string('foo=2')
3069
# We get a list of one string
3070
self.assertEquals(['2'], self.conf.get('foo'))
3072
def test_get_with_list_converter_one_string(self):
3073
self.register_list_option('foo', ['foo'])
3074
self.conf.store._load_from_string('foo=bar')
3075
# We get a list of one string
3076
self.assertEquals(['bar'], self.conf.get('foo'))
3078
def test_get_with_list_converter_many_items(self):
3079
self.register_list_option('foo', [1])
3080
self.conf.store._load_from_string('foo=m,o,r,e')
3081
self.assertEquals(['m', 'o', 'r', 'e'], self.conf.get('foo'))
3084
class TestStackSet(TestStackWithTransport):
3086
def test_simple_set(self):
3087
conf = self.get_stack(self)
3088
conf.store._load_from_string('foo=bar')
3089
self.assertEquals('bar', conf.get('foo'))
3090
conf.set('foo', 'baz')
3091
# Did we get it back ?
3092
self.assertEquals('baz', conf.get('foo'))
3094
def test_set_creates_a_new_section(self):
3095
conf = self.get_stack(self)
3096
conf.set('foo', 'baz')
3097
self.assertEquals, 'baz', conf.get('foo')
3099
def test_set_hook(self):
3103
config.ConfigHooks.install_named_hook('set', hook, None)
3104
self.assertLength(0, calls)
3105
conf = self.get_stack(self)
3106
conf.set('foo', 'bar')
3107
self.assertLength(1, calls)
3108
self.assertEquals((conf, 'foo', 'bar'), calls[0])
3111
class TestStackRemove(TestStackWithTransport):
3113
def test_remove_existing(self):
3114
conf = self.get_stack(self)
3115
conf.store._load_from_string('foo=bar')
3116
self.assertEquals('bar', conf.get('foo'))
3118
# Did we get it back ?
3119
self.assertEquals(None, conf.get('foo'))
3121
def test_remove_unknown(self):
3122
conf = self.get_stack(self)
3123
self.assertRaises(KeyError, conf.remove, 'I_do_not_exist')
3125
def test_remove_hook(self):
3129
config.ConfigHooks.install_named_hook('remove', hook, None)
3130
self.assertLength(0, calls)
3131
conf = self.get_stack(self)
3132
conf.store._load_from_string('foo=bar')
3134
self.assertLength(1, calls)
3135
self.assertEquals((conf, 'foo'), calls[0])
3138
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
3141
super(TestConfigGetOptions, self).setUp()
3142
create_configs(self)
3144
def test_no_variable(self):
3145
# Using branch should query branch, locations and bazaar
3146
self.assertOptions([], self.branch_config)
3148
def test_option_in_bazaar(self):
3149
self.bazaar_config.set_user_option('file', 'bazaar')
3150
self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
3153
def test_option_in_locations(self):
3154
self.locations_config.set_user_option('file', 'locations')
3156
[('file', 'locations', self.tree.basedir, 'locations')],
3157
self.locations_config)
3159
def test_option_in_branch(self):
3160
self.branch_config.set_user_option('file', 'branch')
3161
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
3164
def test_option_in_bazaar_and_branch(self):
3165
self.bazaar_config.set_user_option('file', 'bazaar')
3166
self.branch_config.set_user_option('file', 'branch')
3167
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
3168
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3171
def test_option_in_branch_and_locations(self):
3172
# Hmm, locations override branch :-/
3173
self.locations_config.set_user_option('file', 'locations')
3174
self.branch_config.set_user_option('file', 'branch')
3176
[('file', 'locations', self.tree.basedir, 'locations'),
3177
('file', 'branch', 'DEFAULT', 'branch'),],
3180
def test_option_in_bazaar_locations_and_branch(self):
3181
self.bazaar_config.set_user_option('file', 'bazaar')
3182
self.locations_config.set_user_option('file', 'locations')
3183
self.branch_config.set_user_option('file', 'branch')
3185
[('file', 'locations', self.tree.basedir, 'locations'),
3186
('file', 'branch', 'DEFAULT', 'branch'),
3187
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3191
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
3194
super(TestConfigRemoveOption, self).setUp()
3195
create_configs_with_file_option(self)
3197
def test_remove_in_locations(self):
3198
self.locations_config.remove_user_option('file', self.tree.basedir)
3200
[('file', 'branch', 'DEFAULT', 'branch'),
3201
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3204
def test_remove_in_branch(self):
3205
self.branch_config.remove_user_option('file')
3207
[('file', 'locations', self.tree.basedir, 'locations'),
3208
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
3211
def test_remove_in_bazaar(self):
3212
self.bazaar_config.remove_user_option('file')
3214
[('file', 'locations', self.tree.basedir, 'locations'),
3215
('file', 'branch', 'DEFAULT', 'branch'),],
3219
class TestConfigGetSections(tests.TestCaseWithTransport):
3222
super(TestConfigGetSections, self).setUp()
3223
create_configs(self)
3225
def assertSectionNames(self, expected, conf, name=None):
3226
"""Check which sections are returned for a given config.
3228
If fallback configurations exist their sections can be included.
3230
:param expected: A list of section names.
3232
:param conf: The configuration that will be queried.
3234
:param name: An optional section name that will be passed to
3237
sections = list(conf._get_sections(name))
3238
self.assertLength(len(expected), sections)
3239
self.assertEqual(expected, [name for name, _, _ in sections])
3241
def test_bazaar_default_section(self):
3242
self.assertSectionNames(['DEFAULT'], self.bazaar_config)
3244
def test_locations_default_section(self):
3245
# No sections are defined in an empty file
3246
self.assertSectionNames([], self.locations_config)
3248
def test_locations_named_section(self):
3249
self.locations_config.set_user_option('file', 'locations')
3250
self.assertSectionNames([self.tree.basedir], self.locations_config)
3252
def test_locations_matching_sections(self):
3253
loc_config = self.locations_config
3254
loc_config.set_user_option('file', 'locations')
3255
# We need to cheat a bit here to create an option in sections above and
3256
# below the 'location' one.
3257
parser = loc_config._get_parser()
3258
# locations.cong deals with '/' ignoring native os.sep
3259
location_names = self.tree.basedir.split('/')
3260
parent = '/'.join(location_names[:-1])
3261
child = '/'.join(location_names + ['child'])
3263
parser[parent]['file'] = 'parent'
3265
parser[child]['file'] = 'child'
3266
self.assertSectionNames([self.tree.basedir, parent], loc_config)
3268
def test_branch_data_default_section(self):
3269
self.assertSectionNames([None],
3270
self.branch_config._get_branch_data_config())
3272
def test_branch_default_sections(self):
3273
# No sections are defined in an empty locations file
3274
self.assertSectionNames([None, 'DEFAULT'],
3276
# Unless we define an option
3277
self.branch_config._get_location_config().set_user_option(
3278
'file', 'locations')
3279
self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
3282
def test_bazaar_named_section(self):
3283
# We need to cheat as the API doesn't give direct access to sections
3284
# other than DEFAULT.
3285
self.bazaar_config.set_alias('bazaar', 'bzr')
3286
self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
3289
class TestAuthenticationConfigFile(tests.TestCase):
3290
"""Test the authentication.conf file matching"""
3292
def _got_user_passwd(self, expected_user, expected_password,
3293
config, *args, **kwargs):
3294
credentials = config.get_credentials(*args, **kwargs)
3295
if credentials is None:
3299
user = credentials['user']
3300
password = credentials['password']
3301
self.assertEquals(expected_user, user)
3302
self.assertEquals(expected_password, password)
3304
def test_empty_config(self):
3305
conf = config.AuthenticationConfig(_file=StringIO())
3306
self.assertEquals({}, conf._get_config())
3307
self._got_user_passwd(None, None, conf, 'http', 'foo.net')
3309
def test_non_utf8_config(self):
3310
conf = config.AuthenticationConfig(_file=StringIO(
3312
self.assertRaises(errors.ConfigContentError, conf._get_config)
3314
def test_missing_auth_section_header(self):
3315
conf = config.AuthenticationConfig(_file=StringIO('foo = bar'))
3316
self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
3318
def test_auth_section_header_not_closed(self):
3319
conf = config.AuthenticationConfig(_file=StringIO('[DEF'))
3320
self.assertRaises(errors.ParseConfigError, conf._get_config)
3322
def test_auth_value_not_boolean(self):
3323
conf = config.AuthenticationConfig(_file=StringIO(
3327
verify_certificates=askme # Error: Not a boolean
3329
self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
3331
def test_auth_value_not_int(self):
3332
conf = config.AuthenticationConfig(_file=StringIO(
3336
port=port # Error: Not an int
3338
self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
3340
def test_unknown_password_encoding(self):
3341
conf = config.AuthenticationConfig(_file=StringIO(
3345
password_encoding=unknown
3347
self.assertRaises(ValueError, conf.get_password,
3348
'ftp', 'foo.net', 'joe')
3350
def test_credentials_for_scheme_host(self):
3351
conf = config.AuthenticationConfig(_file=StringIO(
3352
"""# Identity on foo.net
3357
password=secret-pass
3360
self._got_user_passwd('joe', 'secret-pass', conf, 'ftp', 'foo.net')
3362
self._got_user_passwd(None, None, conf, 'http', 'foo.net')
3364
self._got_user_passwd(None, None, conf, 'ftp', 'bar.net')
3366
def test_credentials_for_host_port(self):
3367
conf = config.AuthenticationConfig(_file=StringIO(
3368
"""# Identity on foo.net
3374
password=secret-pass
3377
self._got_user_passwd('joe', 'secret-pass',
3378
conf, 'ftp', 'foo.net', port=10021)
3380
self._got_user_passwd(None, None, conf, 'ftp', 'foo.net')
3382
def test_for_matching_host(self):
3383
conf = config.AuthenticationConfig(_file=StringIO(
3384
"""# Identity on foo.net
3390
[sourceforge domain]
3397
self._got_user_passwd('georges', 'bendover',
3398
conf, 'bzr', 'foo.bzr.sf.net')
3400
self._got_user_passwd(None, None,
3401
conf, 'bzr', 'bbzr.sf.net')
3403
def test_for_matching_host_None(self):
3404
conf = config.AuthenticationConfig(_file=StringIO(
3405
"""# Identity on foo.net
3415
self._got_user_passwd('joe', 'joepass',
3416
conf, 'bzr', 'quux.net')
3417
# no host but different scheme
3418
self._got_user_passwd('georges', 'bendover',
3419
conf, 'ftp', 'quux.net')
3421
def test_credentials_for_path(self):
3422
conf = config.AuthenticationConfig(_file=StringIO(
3438
self._got_user_passwd(None, None,
3439
conf, 'http', host='bar.org', path='/dir3')
3441
self._got_user_passwd('georges', 'bendover',
3442
conf, 'http', host='bar.org', path='/dir2')
3444
self._got_user_passwd('jim', 'jimpass',
3445
conf, 'http', host='bar.org',path='/dir1/subdir')
3447
def test_credentials_for_user(self):
3448
conf = config.AuthenticationConfig(_file=StringIO(
3457
self._got_user_passwd('jim', 'jimpass',
3458
conf, 'http', 'bar.org')
3460
self._got_user_passwd('jim', 'jimpass',
3461
conf, 'http', 'bar.org', user='jim')
3462
# Don't get a different user if one is specified
3463
self._got_user_passwd(None, None,
3464
conf, 'http', 'bar.org', user='georges')
3466
def test_credentials_for_user_without_password(self):
3467
conf = config.AuthenticationConfig(_file=StringIO(
3474
# Get user but no password
3475
self._got_user_passwd('jim', None,
3476
conf, 'http', 'bar.org')
3478
def test_verify_certificates(self):
3479
conf = config.AuthenticationConfig(_file=StringIO(
3486
verify_certificates=False
3493
credentials = conf.get_credentials('https', 'bar.org')
3494
self.assertEquals(False, credentials.get('verify_certificates'))
3495
credentials = conf.get_credentials('https', 'foo.net')
3496
self.assertEquals(True, credentials.get('verify_certificates'))
3499
class TestAuthenticationStorage(tests.TestCaseInTempDir):
3501
def test_set_credentials(self):
3502
conf = config.AuthenticationConfig()
3503
conf.set_credentials('name', 'host', 'user', 'scheme', 'password',
3504
99, path='/foo', verify_certificates=False, realm='realm')
3505
credentials = conf.get_credentials(host='host', scheme='scheme',
3506
port=99, path='/foo',
3508
CREDENTIALS = {'name': 'name', 'user': 'user', 'password': 'password',
3509
'verify_certificates': False, 'scheme': 'scheme',
3510
'host': 'host', 'port': 99, 'path': '/foo',
3512
self.assertEqual(CREDENTIALS, credentials)
3513
credentials_from_disk = config.AuthenticationConfig().get_credentials(
3514
host='host', scheme='scheme', port=99, path='/foo', realm='realm')
3515
self.assertEqual(CREDENTIALS, credentials_from_disk)
3517
def test_reset_credentials_different_name(self):
3518
conf = config.AuthenticationConfig()
3519
conf.set_credentials('name', 'host', 'user', 'scheme', 'password'),
3520
conf.set_credentials('name2', 'host', 'user2', 'scheme', 'password'),
3521
self.assertIs(None, conf._get_config().get('name'))
3522
credentials = conf.get_credentials(host='host', scheme='scheme')
3523
CREDENTIALS = {'name': 'name2', 'user': 'user2', 'password':
3524
'password', 'verify_certificates': True,
3525
'scheme': 'scheme', 'host': 'host', 'port': None,
3526
'path': None, 'realm': None}
3527
self.assertEqual(CREDENTIALS, credentials)
3530
class TestAuthenticationConfig(tests.TestCase):
3531
"""Test AuthenticationConfig behaviour"""
3533
def _check_default_password_prompt(self, expected_prompt_format, scheme,
3534
host=None, port=None, realm=None,
3538
user, password = 'jim', 'precious'
3539
expected_prompt = expected_prompt_format % {
3540
'scheme': scheme, 'host': host, 'port': port,
3541
'user': user, 'realm': realm}
3543
stdout = tests.StringIOWrapper()
3544
stderr = tests.StringIOWrapper()
3545
ui.ui_factory = tests.TestUIFactory(stdin=password + '\n',
3546
stdout=stdout, stderr=stderr)
3547
# We use an empty conf so that the user is always prompted
3548
conf = config.AuthenticationConfig()
3549
self.assertEquals(password,
3550
conf.get_password(scheme, host, user, port=port,
3551
realm=realm, path=path))
3552
self.assertEquals(expected_prompt, stderr.getvalue())
3553
self.assertEquals('', stdout.getvalue())
3555
def _check_default_username_prompt(self, expected_prompt_format, scheme,
3556
host=None, port=None, realm=None,
3561
expected_prompt = expected_prompt_format % {
3562
'scheme': scheme, 'host': host, 'port': port,
3564
stdout = tests.StringIOWrapper()
3565
stderr = tests.StringIOWrapper()
3566
ui.ui_factory = tests.TestUIFactory(stdin=username+ '\n',
3567
stdout=stdout, stderr=stderr)
3568
# We use an empty conf so that the user is always prompted
3569
conf = config.AuthenticationConfig()
3570
self.assertEquals(username, conf.get_user(scheme, host, port=port,
3571
realm=realm, path=path, ask=True))
3572
self.assertEquals(expected_prompt, stderr.getvalue())
3573
self.assertEquals('', stdout.getvalue())
3575
def test_username_defaults_prompts(self):
3576
# HTTP prompts can't be tested here, see test_http.py
3577
self._check_default_username_prompt(u'FTP %(host)s username: ', 'ftp')
3578
self._check_default_username_prompt(
3579
u'FTP %(host)s:%(port)d username: ', 'ftp', port=10020)
3580
self._check_default_username_prompt(
3581
u'SSH %(host)s:%(port)d username: ', 'ssh', port=12345)
3583
def test_username_default_no_prompt(self):
3584
conf = config.AuthenticationConfig()
3585
self.assertEquals(None,
3586
conf.get_user('ftp', 'example.com'))
3587
self.assertEquals("explicitdefault",
3588
conf.get_user('ftp', 'example.com', default="explicitdefault"))
3590
def test_password_default_prompts(self):
3591
# HTTP prompts can't be tested here, see test_http.py
3592
self._check_default_password_prompt(
3593
u'FTP %(user)s@%(host)s password: ', 'ftp')
3594
self._check_default_password_prompt(
3595
u'FTP %(user)s@%(host)s:%(port)d password: ', 'ftp', port=10020)
3596
self._check_default_password_prompt(
3597
u'SSH %(user)s@%(host)s:%(port)d password: ', 'ssh', port=12345)
3598
# SMTP port handling is a bit special (it's handled if embedded in the
3600
# FIXME: should we: forbid that, extend it to other schemes, leave
3601
# things as they are that's fine thank you ?
3602
self._check_default_password_prompt(
3603
u'SMTP %(user)s@%(host)s password: ', 'smtp')
3604
self._check_default_password_prompt(
3605
u'SMTP %(user)s@%(host)s password: ', 'smtp', host='bar.org:10025')
3606
self._check_default_password_prompt(
3607
u'SMTP %(user)s@%(host)s:%(port)d password: ', 'smtp', port=10025)
3609
def test_ssh_password_emits_warning(self):
3610
conf = config.AuthenticationConfig(_file=StringIO(
3618
entered_password = 'typed-by-hand'
3619
stdout = tests.StringIOWrapper()
3620
stderr = tests.StringIOWrapper()
3621
ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
3622
stdout=stdout, stderr=stderr)
3624
# Since the password defined in the authentication config is ignored,
3625
# the user is prompted
3626
self.assertEquals(entered_password,
3627
conf.get_password('ssh', 'bar.org', user='jim'))
3628
self.assertContainsRe(
3630
'password ignored in section \[ssh with password\]')
3632
def test_ssh_without_password_doesnt_emit_warning(self):
3633
conf = config.AuthenticationConfig(_file=StringIO(
3640
entered_password = 'typed-by-hand'
3641
stdout = tests.StringIOWrapper()
3642
stderr = tests.StringIOWrapper()
3643
ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
3647
# Since the password defined in the authentication config is ignored,
3648
# the user is prompted
3649
self.assertEquals(entered_password,
3650
conf.get_password('ssh', 'bar.org', user='jim'))
3651
# No warning shoud be emitted since there is no password. We are only
3653
self.assertNotContainsRe(
3655
'password ignored in section \[ssh with password\]')
3657
def test_uses_fallback_stores(self):
3658
self.overrideAttr(config, 'credential_store_registry',
3659
config.CredentialStoreRegistry())
3660
store = StubCredentialStore()
3661
store.add_credentials("http", "example.com", "joe", "secret")
3662
config.credential_store_registry.register("stub", store, fallback=True)
3663
conf = config.AuthenticationConfig(_file=StringIO())
3664
creds = conf.get_credentials("http", "example.com")
3665
self.assertEquals("joe", creds["user"])
3666
self.assertEquals("secret", creds["password"])
3669
class StubCredentialStore(config.CredentialStore):
3675
def add_credentials(self, scheme, host, user, password=None):
3676
self._username[(scheme, host)] = user
3677
self._password[(scheme, host)] = password
3679
def get_credentials(self, scheme, host, port=None, user=None,
3680
path=None, realm=None):
3681
key = (scheme, host)
3682
if not key in self._username:
3684
return { "scheme": scheme, "host": host, "port": port,
3685
"user": self._username[key], "password": self._password[key]}
3688
class CountingCredentialStore(config.CredentialStore):
3693
def get_credentials(self, scheme, host, port=None, user=None,
3694
path=None, realm=None):
3699
class TestCredentialStoreRegistry(tests.TestCase):
3701
def _get_cs_registry(self):
3702
return config.credential_store_registry
3704
def test_default_credential_store(self):
3705
r = self._get_cs_registry()
3706
default = r.get_credential_store(None)
3707
self.assertIsInstance(default, config.PlainTextCredentialStore)
3709
def test_unknown_credential_store(self):
3710
r = self._get_cs_registry()
3711
# It's hard to imagine someone creating a credential store named
3712
# 'unknown' so we use that as an never registered key.
3713
self.assertRaises(KeyError, r.get_credential_store, 'unknown')
3715
def test_fallback_none_registered(self):
3716
r = config.CredentialStoreRegistry()
3717
self.assertEquals(None,
3718
r.get_fallback_credentials("http", "example.com"))
3720
def test_register(self):
3721
r = config.CredentialStoreRegistry()
3722
r.register("stub", StubCredentialStore(), fallback=False)
3723
r.register("another", StubCredentialStore(), fallback=True)
3724
self.assertEquals(["another", "stub"], r.keys())
3726
def test_register_lazy(self):
3727
r = config.CredentialStoreRegistry()
3728
r.register_lazy("stub", "bzrlib.tests.test_config",
3729
"StubCredentialStore", fallback=False)
3730
self.assertEquals(["stub"], r.keys())
3731
self.assertIsInstance(r.get_credential_store("stub"),
3732
StubCredentialStore)
3734
def test_is_fallback(self):
3735
r = config.CredentialStoreRegistry()
3736
r.register("stub1", None, fallback=False)
3737
r.register("stub2", None, fallback=True)
3738
self.assertEquals(False, r.is_fallback("stub1"))
3739
self.assertEquals(True, r.is_fallback("stub2"))
3741
def test_no_fallback(self):
3742
r = config.CredentialStoreRegistry()
3743
store = CountingCredentialStore()
3744
r.register("count", store, fallback=False)
3745
self.assertEquals(None,
3746
r.get_fallback_credentials("http", "example.com"))
3747
self.assertEquals(0, store._calls)
3749
def test_fallback_credentials(self):
3750
r = config.CredentialStoreRegistry()
3751
store = StubCredentialStore()
3752
store.add_credentials("http", "example.com",
3753
"somebody", "geheim")
3754
r.register("stub", store, fallback=True)
3755
creds = r.get_fallback_credentials("http", "example.com")
3756
self.assertEquals("somebody", creds["user"])
3757
self.assertEquals("geheim", creds["password"])
3759
def test_fallback_first_wins(self):
3760
r = config.CredentialStoreRegistry()
3761
stub1 = StubCredentialStore()
3762
stub1.add_credentials("http", "example.com",
3763
"somebody", "stub1")
3764
r.register("stub1", stub1, fallback=True)
3765
stub2 = StubCredentialStore()
3766
stub2.add_credentials("http", "example.com",
3767
"somebody", "stub2")
3768
r.register("stub2", stub1, fallback=True)
3769
creds = r.get_fallback_credentials("http", "example.com")
3770
self.assertEquals("somebody", creds["user"])
3771
self.assertEquals("stub1", creds["password"])
3774
class TestPlainTextCredentialStore(tests.TestCase):
3776
def test_decode_password(self):
3777
r = config.credential_store_registry
3778
plain_text = r.get_credential_store()
3779
decoded = plain_text.decode_password(dict(password='secret'))
3780
self.assertEquals('secret', decoded)
3783
# FIXME: Once we have a way to declare authentication to all test servers, we
3784
# can implement generic tests.
3785
# test_user_password_in_url
3786
# test_user_in_url_password_from_config
3787
# test_user_in_url_password_prompted
3788
# test_user_in_config
3789
# test_user_getpass.getuser
3790
# test_user_prompted ?
3791
class TestAuthenticationRing(tests.TestCaseWithTransport):
3795
class TestAutoUserId(tests.TestCase):
3796
"""Test inferring an automatic user name."""
3798
def test_auto_user_id(self):
3799
"""Automatic inference of user name.
3801
This is a bit hard to test in an isolated way, because it depends on
3802
system functions that go direct to /etc or perhaps somewhere else.
3803
But it's reasonable to say that on Unix, with an /etc/mailname, we ought
3804
to be able to choose a user name with no configuration.
3806
if sys.platform == 'win32':
3807
raise tests.TestSkipped(
3808
"User name inference not implemented on win32")
3809
realname, address = config._auto_user_id()
3810
if os.path.exists('/etc/mailname'):
3811
self.assertIsNot(None, realname)
3812
self.assertIsNot(None, address)
3814
self.assertEquals((None, None), (realname, address))