347
1257
my_config = self._get_sample_config()
348
1258
self.assertEqual("something",
349
1259
my_config.get_user_option('user_global_option'))
351
1261
def test_post_commit_default(self):
352
1262
my_config = self._get_sample_config()
353
self.assertEqual(None, my_config.post_commit())
356
class TestLocationConfig(TestCase):
358
def test_constructs(self):
359
my_config = config.LocationConfig('http://example.com')
1263
self.assertEqual(None,
1264
self.applyDeprecated(deprecated_in((2, 5, 0)),
1265
my_config.post_commit))
1267
def test_configured_logformat(self):
1268
my_config = self._get_sample_config()
1269
self.assertEqual("short",
1270
self.applyDeprecated(deprecated_in((2, 5, 0)),
1271
my_config.log_format))
1273
def test_configured_acceptable_keys(self):
1274
my_config = self._get_sample_config()
1275
self.assertEqual("amy",
1276
self.applyDeprecated(deprecated_in((2, 5, 0)),
1277
my_config.acceptable_keys))
1279
def test_configured_validate_signatures_in_log(self):
1280
my_config = self._get_sample_config()
1281
self.assertEqual(True, my_config.validate_signatures_in_log())
1283
def test_get_alias(self):
1284
my_config = self._get_sample_config()
1285
self.assertEqual('help', my_config.get_alias('h'))
1287
def test_get_aliases(self):
1288
my_config = self._get_sample_config()
1289
aliases = my_config.get_aliases()
1290
self.assertEqual(2, len(aliases))
1291
sorted_keys = sorted(aliases)
1292
self.assertEqual('help', aliases[sorted_keys[0]])
1293
self.assertEqual(sample_long_alias, aliases[sorted_keys[1]])
1295
def test_get_no_alias(self):
1296
my_config = self._get_sample_config()
1297
self.assertEqual(None, my_config.get_alias('foo'))
1299
def test_get_long_alias(self):
1300
my_config = self._get_sample_config()
1301
self.assertEqual(sample_long_alias, my_config.get_alias('ll'))
1303
def test_get_change_editor(self):
1304
my_config = self._get_sample_config()
1305
change_editor = my_config.get_change_editor('old', 'new')
1306
self.assertIs(diff.DiffFromTool, change_editor.__class__)
1307
self.assertEqual('vimdiff -of @new_path @old_path',
1308
' '.join(change_editor.command_template))
1310
def test_get_no_change_editor(self):
1311
my_config = self._get_empty_config()
1312
change_editor = my_config.get_change_editor('old', 'new')
1313
self.assertIs(None, change_editor)
1315
def test_get_merge_tools(self):
1316
conf = self._get_sample_config()
1317
tools = conf.get_merge_tools()
1318
self.log(repr(tools))
1320
{u'funkytool' : u'funkytool "arg with spaces" {this_temp}',
1321
u'sometool' : u'sometool {base} {this} {other} -o {result}',
1322
u'newtool' : u'"newtool with spaces" {this_temp}'},
1325
def test_get_merge_tools_empty(self):
1326
conf = self._get_empty_config()
1327
tools = conf.get_merge_tools()
1328
self.assertEqual({}, tools)
1330
def test_find_merge_tool(self):
1331
conf = self._get_sample_config()
1332
cmdline = conf.find_merge_tool('sometool')
1333
self.assertEqual('sometool {base} {this} {other} -o {result}', cmdline)
1335
def test_find_merge_tool_not_found(self):
1336
conf = self._get_sample_config()
1337
cmdline = conf.find_merge_tool('DOES NOT EXIST')
1338
self.assertIs(cmdline, None)
1340
def test_find_merge_tool_known(self):
1341
conf = self._get_empty_config()
1342
cmdline = conf.find_merge_tool('kdiff3')
1343
self.assertEquals('kdiff3 {base} {this} {other} -o {result}', cmdline)
1345
def test_find_merge_tool_override_known(self):
1346
conf = self._get_empty_config()
1347
conf.set_user_option('bzr.mergetool.kdiff3', 'kdiff3 blah')
1348
cmdline = conf.find_merge_tool('kdiff3')
1349
self.assertEqual('kdiff3 blah', cmdline)
1352
class TestGlobalConfigSavingOptions(tests.TestCaseInTempDir):
1354
def test_empty(self):
1355
my_config = config.GlobalConfig()
1356
self.assertEqual(0, len(my_config.get_aliases()))
1358
def test_set_alias(self):
1359
my_config = config.GlobalConfig()
1360
alias_value = 'commit --strict'
1361
my_config.set_alias('commit', alias_value)
1362
new_config = config.GlobalConfig()
1363
self.assertEqual(alias_value, new_config.get_alias('commit'))
1365
def test_remove_alias(self):
1366
my_config = config.GlobalConfig()
1367
my_config.set_alias('commit', 'commit --strict')
1368
# Now remove the alias again.
1369
my_config.unset_alias('commit')
1370
new_config = config.GlobalConfig()
1371
self.assertIs(None, new_config.get_alias('commit'))
1374
class TestLocationConfig(tests.TestCaseInTempDir, TestOptionsMixin):
1376
def test_constructs_valid(self):
1377
config.LocationConfig('http://example.com')
1379
def test_constructs_error(self):
360
1380
self.assertRaises(TypeError, config.LocationConfig)
362
1382
def test_branch_calls_read_filenames(self):
363
1383
# This is testing the correct file names are provided.
364
1384
# TODO: consolidate with the test for GlobalConfigs filename checks.
366
# replace the class that is constructured, to check its parameters
1386
# replace the class that is constructed, to check its parameters
367
1387
oldparserclass = config.ConfigObj
368
1388
config.ConfigObj = InstrumentedConfigObj
369
my_config = config.LocationConfig('http://www.example.com')
1390
my_config = config.LocationConfig('http://www.example.com')
371
1391
parser = my_config._get_parser()
373
1393
config.ConfigObj = oldparserclass
374
self.failUnless(isinstance(parser, InstrumentedConfigObj))
1394
self.assertIsInstance(parser, InstrumentedConfigObj)
375
1395
self.assertEqual(parser._calls,
376
[('__init__', config.branches_config_filename())])
1396
[('__init__', config.locations_config_filename(),
378
1399
def test_get_global_config(self):
379
my_config = config.LocationConfig('http://example.com')
1400
my_config = config.BranchConfig(FakeBranch('http://example.com'))
380
1401
global_config = my_config._get_global_config()
381
self.failUnless(isinstance(global_config, config.GlobalConfig))
382
self.failUnless(global_config is my_config._get_global_config())
384
def test__get_section_no_match(self):
385
self.get_location_config('/')
386
self.assertEqual(None, self.my_config._get_section())
388
def test__get_section_exact(self):
389
self.get_location_config('http://www.example.com')
390
self.assertEqual('http://www.example.com',
391
self.my_config._get_section())
393
def test__get_section_suffix_does_not(self):
394
self.get_location_config('http://www.example.com-com')
395
self.assertEqual(None, self.my_config._get_section())
397
def test__get_section_subdir_recursive(self):
398
self.get_location_config('http://www.example.com/com')
399
self.assertEqual('http://www.example.com',
400
self.my_config._get_section())
402
def test__get_section_subdir_matches(self):
403
self.get_location_config('http://www.example.com/useglobal')
404
self.assertEqual('http://www.example.com/useglobal',
405
self.my_config._get_section())
407
def test__get_section_subdir_nonrecursive(self):
408
self.get_location_config(
409
'http://www.example.com/useglobal/childbranch')
410
self.assertEqual('http://www.example.com',
411
self.my_config._get_section())
413
def test__get_section_subdir_trailing_slash(self):
414
self.get_location_config('/b')
415
self.assertEqual('/b/', self.my_config._get_section())
417
def test__get_section_subdir_child(self):
418
self.get_location_config('/a/foo')
419
self.assertEqual('/a/*', self.my_config._get_section())
421
def test__get_section_subdir_child_child(self):
422
self.get_location_config('/a/foo/bar')
423
self.assertEqual('/a/', self.my_config._get_section())
425
def test__get_section_trailing_slash_with_children(self):
426
self.get_location_config('/a/')
427
self.assertEqual('/a/', self.my_config._get_section())
429
def test__get_section_explicit_over_glob(self):
430
self.get_location_config('/a/c')
431
self.assertEqual('/a/c', self.my_config._get_section())
433
def get_location_config(self, location, global_config=None):
434
if global_config is None:
435
global_file = StringIO(sample_config_text)
437
global_file = StringIO(global_config)
438
branches_file = StringIO(sample_branches_text)
439
self.my_config = config.LocationConfig(location)
440
self.my_config._get_parser(branches_file)
441
self.my_config._get_global_config()._get_parser(global_file)
1402
self.assertIsInstance(global_config, config.GlobalConfig)
1403
self.assertIs(global_config, my_config._get_global_config())
1405
def assertLocationMatching(self, expected):
1406
self.assertEqual(expected,
1407
list(self.my_location_config._get_matching_sections()))
1409
def test__get_matching_sections_no_match(self):
1410
self.get_branch_config('/')
1411
self.assertLocationMatching([])
1413
def test__get_matching_sections_exact(self):
1414
self.get_branch_config('http://www.example.com')
1415
self.assertLocationMatching([('http://www.example.com', '')])
1417
def test__get_matching_sections_suffix_does_not(self):
1418
self.get_branch_config('http://www.example.com-com')
1419
self.assertLocationMatching([])
1421
def test__get_matching_sections_subdir_recursive(self):
1422
self.get_branch_config('http://www.example.com/com')
1423
self.assertLocationMatching([('http://www.example.com', 'com')])
1425
def test__get_matching_sections_ignoreparent(self):
1426
self.get_branch_config('http://www.example.com/ignoreparent')
1427
self.assertLocationMatching([('http://www.example.com/ignoreparent',
1430
def test__get_matching_sections_ignoreparent_subdir(self):
1431
self.get_branch_config(
1432
'http://www.example.com/ignoreparent/childbranch')
1433
self.assertLocationMatching([('http://www.example.com/ignoreparent',
1436
def test__get_matching_sections_subdir_trailing_slash(self):
1437
self.get_branch_config('/b')
1438
self.assertLocationMatching([('/b/', '')])
1440
def test__get_matching_sections_subdir_child(self):
1441
self.get_branch_config('/a/foo')
1442
self.assertLocationMatching([('/a/*', ''), ('/a/', 'foo')])
1444
def test__get_matching_sections_subdir_child_child(self):
1445
self.get_branch_config('/a/foo/bar')
1446
self.assertLocationMatching([('/a/*', 'bar'), ('/a/', 'foo/bar')])
1448
def test__get_matching_sections_trailing_slash_with_children(self):
1449
self.get_branch_config('/a/')
1450
self.assertLocationMatching([('/a/', '')])
1452
def test__get_matching_sections_explicit_over_glob(self):
1453
# XXX: 2006-09-08 jamesh
1454
# This test only passes because ord('c') > ord('*'). If there
1455
# was a config section for '/a/?', it would get precedence
1457
self.get_branch_config('/a/c')
1458
self.assertLocationMatching([('/a/c', ''), ('/a/*', ''), ('/a/', 'c')])
1460
def test__get_option_policy_normal(self):
1461
self.get_branch_config('http://www.example.com')
1463
self.my_location_config._get_config_policy(
1464
'http://www.example.com', 'normal_option'),
1467
def test__get_option_policy_norecurse(self):
1468
self.get_branch_config('http://www.example.com')
1470
self.my_location_config._get_option_policy(
1471
'http://www.example.com', 'norecurse_option'),
1472
config.POLICY_NORECURSE)
1473
# Test old recurse=False setting:
1475
self.my_location_config._get_option_policy(
1476
'http://www.example.com/norecurse', 'normal_option'),
1477
config.POLICY_NORECURSE)
1479
def test__get_option_policy_normal(self):
1480
self.get_branch_config('http://www.example.com')
1482
self.my_location_config._get_option_policy(
1483
'http://www.example.com', 'appendpath_option'),
1484
config.POLICY_APPENDPATH)
1486
def test__get_options_with_policy(self):
1487
self.get_branch_config('/dir/subdir',
1488
location_config="""\
1490
other_url = /other-dir
1491
other_url:policy = appendpath
1493
other_url = /other-subdir
1496
[(u'other_url', u'/other-subdir', u'/dir/subdir', 'locations'),
1497
(u'other_url', u'/other-dir', u'/dir', 'locations'),
1498
(u'other_url:policy', u'appendpath', u'/dir', 'locations')],
1499
self.my_location_config)
443
1501
def test_location_without_username(self):
444
self.get_location_config('http://www.example.com/useglobal')
445
self.assertEqual('Robert Collins <robertc@example.com>',
1502
self.get_branch_config('http://www.example.com/ignoreparent')
1503
self.assertEqual(u'Erik B\u00e5gfors <erik@bagfors.nu>',
446
1504
self.my_config.username())
448
1506
def test_location_not_listed(self):
449
self.get_location_config('/home/robertc/sources')
450
self.assertEqual('Robert Collins <robertc@example.com>',
1507
"""Test that the global username is used when no location matches"""
1508
self.get_branch_config('/home/robertc/sources')
1509
self.assertEqual(u'Erik B\u00e5gfors <erik@bagfors.nu>',
451
1510
self.my_config.username())
453
1512
def test_overriding_location(self):
454
self.get_location_config('http://www.example.com/foo')
1513
self.get_branch_config('http://www.example.com/foo')
455
1514
self.assertEqual('Robert Collins <robertc@example.org>',
456
1515
self.my_config.username())
458
1517
def test_signatures_not_set(self):
459
self.get_location_config('http://www.example.com',
1518
self.get_branch_config('http://www.example.com',
460
1519
global_config=sample_ignore_signatures)
461
self.assertEqual(config.CHECK_NEVER,
462
self.my_config.signature_checking())
1520
self.assertEqual(config.CHECK_ALWAYS,
1521
self.applyDeprecated(deprecated_in((2, 5, 0)),
1522
self.my_config.signature_checking))
1523
self.assertEqual(config.SIGN_NEVER,
1524
self.applyDeprecated(deprecated_in((2, 5, 0)),
1525
self.my_config.signing_policy))
464
1527
def test_signatures_never(self):
465
self.get_location_config('/a/c')
1528
self.get_branch_config('/a/c')
466
1529
self.assertEqual(config.CHECK_NEVER,
467
self.my_config.signature_checking())
1530
self.applyDeprecated(deprecated_in((2, 5, 0)),
1531
self.my_config.signature_checking))
469
1533
def test_signatures_when_available(self):
470
self.get_location_config('/a/', global_config=sample_ignore_signatures)
1534
self.get_branch_config('/a/', global_config=sample_ignore_signatures)
471
1535
self.assertEqual(config.CHECK_IF_POSSIBLE,
472
self.my_config.signature_checking())
1536
self.applyDeprecated(deprecated_in((2, 5, 0)),
1537
self.my_config.signature_checking))
474
1539
def test_signatures_always(self):
475
self.get_location_config('/b')
1540
self.get_branch_config('/b')
476
1541
self.assertEqual(config.CHECK_ALWAYS,
477
self.my_config.signature_checking())
1542
self.applyDeprecated(deprecated_in((2, 5, 0)),
1543
self.my_config.signature_checking))
479
1545
def test_gpg_signing_command(self):
480
self.get_location_config('/b')
481
self.assertEqual("gnome-gpg", self.my_config.gpg_signing_command())
1546
self.get_branch_config('/b')
1547
self.assertEqual("gnome-gpg",
1548
self.applyDeprecated(deprecated_in((2, 5, 0)),
1549
self.my_config.gpg_signing_command))
483
1551
def test_gpg_signing_command_missing(self):
484
self.get_location_config('/a')
485
self.assertEqual("false", self.my_config.gpg_signing_command())
1552
self.get_branch_config('/a')
1553
self.assertEqual("false",
1554
self.applyDeprecated(deprecated_in((2, 5, 0)),
1555
self.my_config.gpg_signing_command))
1557
def test_gpg_signing_key(self):
1558
self.get_branch_config('/b')
1559
self.assertEqual("DD4D5088", self.applyDeprecated(deprecated_in((2, 5, 0)),
1560
self.my_config.gpg_signing_key))
1562
def test_gpg_signing_key_default(self):
1563
self.get_branch_config('/a')
1564
self.assertEqual("erik@bagfors.nu",
1565
self.applyDeprecated(deprecated_in((2, 5, 0)),
1566
self.my_config.gpg_signing_key))
487
1568
def test_get_user_option_global(self):
488
self.get_location_config('/a')
1569
self.get_branch_config('/a')
489
1570
self.assertEqual('something',
490
1571
self.my_config.get_user_option('user_global_option'))
492
1573
def test_get_user_option_local(self):
493
self.get_location_config('/a')
1574
self.get_branch_config('/a')
494
1575
self.assertEqual('local',
495
1576
self.my_config.get_user_option('user_local_option'))
1578
def test_get_user_option_appendpath(self):
1579
# returned as is for the base path:
1580
self.get_branch_config('http://www.example.com')
1581
self.assertEqual('append',
1582
self.my_config.get_user_option('appendpath_option'))
1583
# Extra path components get appended:
1584
self.get_branch_config('http://www.example.com/a/b/c')
1585
self.assertEqual('append/a/b/c',
1586
self.my_config.get_user_option('appendpath_option'))
1587
# Overriden for http://www.example.com/dir, where it is a
1589
self.get_branch_config('http://www.example.com/dir/a/b/c')
1590
self.assertEqual('normal',
1591
self.my_config.get_user_option('appendpath_option'))
1593
def test_get_user_option_norecurse(self):
1594
self.get_branch_config('http://www.example.com')
1595
self.assertEqual('norecurse',
1596
self.my_config.get_user_option('norecurse_option'))
1597
self.get_branch_config('http://www.example.com/dir')
1598
self.assertEqual(None,
1599
self.my_config.get_user_option('norecurse_option'))
1600
# http://www.example.com/norecurse is a recurse=False section
1601
# that redefines normal_option. Subdirectories do not pick up
1602
# this redefinition.
1603
self.get_branch_config('http://www.example.com/norecurse')
1604
self.assertEqual('norecurse',
1605
self.my_config.get_user_option('normal_option'))
1606
self.get_branch_config('http://www.example.com/norecurse/subdir')
1607
self.assertEqual('normal',
1608
self.my_config.get_user_option('normal_option'))
1610
def test_set_user_option_norecurse(self):
1611
self.get_branch_config('http://www.example.com')
1612
self.my_config.set_user_option('foo', 'bar',
1613
store=config.STORE_LOCATION_NORECURSE)
1615
self.my_location_config._get_option_policy(
1616
'http://www.example.com', 'foo'),
1617
config.POLICY_NORECURSE)
1619
def test_set_user_option_appendpath(self):
1620
self.get_branch_config('http://www.example.com')
1621
self.my_config.set_user_option('foo', 'bar',
1622
store=config.STORE_LOCATION_APPENDPATH)
1624
self.my_location_config._get_option_policy(
1625
'http://www.example.com', 'foo'),
1626
config.POLICY_APPENDPATH)
1628
def test_set_user_option_change_policy(self):
1629
self.get_branch_config('http://www.example.com')
1630
self.my_config.set_user_option('norecurse_option', 'normal',
1631
store=config.STORE_LOCATION)
1633
self.my_location_config._get_option_policy(
1634
'http://www.example.com', 'norecurse_option'),
1637
def test_set_user_option_recurse_false_section(self):
1638
# The following section has recurse=False set. The test is to
1639
# make sure that a normal option can be added to the section,
1640
# converting recurse=False to the norecurse policy.
1641
self.get_branch_config('http://www.example.com/norecurse')
1642
self.callDeprecated(['The recurse option is deprecated as of 0.14. '
1643
'The section "http://www.example.com/norecurse" '
1644
'has been converted to use policies.'],
1645
self.my_config.set_user_option,
1646
'foo', 'bar', store=config.STORE_LOCATION)
1648
self.my_location_config._get_option_policy(
1649
'http://www.example.com/norecurse', 'foo'),
1651
# The previously existing option is still norecurse:
1653
self.my_location_config._get_option_policy(
1654
'http://www.example.com/norecurse', 'normal_option'),
1655
config.POLICY_NORECURSE)
497
1657
def test_post_commit_default(self):
498
self.get_location_config('/a/c')
1658
self.get_branch_config('/a/c')
499
1659
self.assertEqual('bzrlib.tests.test_config.post_commit',
500
self.my_config.post_commit())
503
class TestLocationConfig(TestCaseInTempDir):
505
def get_location_config(self, location, global_config=None):
1660
self.applyDeprecated(deprecated_in((2, 5, 0)),
1661
self.my_config.post_commit))
1663
def get_branch_config(self, location, global_config=None,
1664
location_config=None):
1665
my_branch = FakeBranch(location)
506
1666
if global_config is None:
507
global_file = StringIO(sample_config_text)
509
global_file = StringIO(global_config)
510
branches_file = StringIO(sample_branches_text)
511
self.my_config = config.LocationConfig(location)
512
self.my_config._get_parser(branches_file)
513
self.my_config._get_global_config()._get_parser(global_file)
1667
global_config = sample_config_text
1668
if location_config is None:
1669
location_config = sample_branches_text
1671
config.GlobalConfig.from_string(global_config, save=True)
1672
config.LocationConfig.from_string(location_config, my_branch.base,
1674
my_config = config.BranchConfig(my_branch)
1675
self.my_config = my_config
1676
self.my_location_config = my_config._get_location_config()
515
1678
def test_set_user_setting_sets_and_saves(self):
516
self.get_location_config('/a/c')
1679
self.get_branch_config('/a/c')
517
1680
record = InstrumentedConfigObj("foo")
518
self.my_config._parser = record
520
real_mkdir = os.mkdir
522
def checked_mkdir(path, mode=0777):
523
self.log('making directory: %s', path)
524
real_mkdir(path, mode)
527
os.mkdir = checked_mkdir
529
self.my_config.set_user_option('foo', 'bar')
531
os.mkdir = real_mkdir
533
self.failUnless(self.created, 'Failed to create ~/.bazaar')
534
self.assertEqual([('__contains__', '/a/c'),
1681
self.my_location_config._parser = record
1683
self.callDeprecated(['The recurse option is deprecated as of '
1684
'0.14. The section "/a/c" has been '
1685
'converted to use policies.'],
1686
self.my_config.set_user_option,
1687
'foo', 'bar', store=config.STORE_LOCATION)
1688
self.assertEqual([('reload',),
1689
('__contains__', '/a/c'),
535
1690
('__contains__', '/a/c/'),
536
1691
('__setitem__', '/a/c', {}),
537
1692
('__getitem__', '/a/c'),
538
1693
('__setitem__', 'foo', 'bar'),
1694
('__getitem__', '/a/c'),
1695
('as_bool', 'recurse'),
1696
('__getitem__', '/a/c'),
1697
('__delitem__', 'recurse'),
1698
('__getitem__', '/a/c'),
1700
('__getitem__', '/a/c'),
1701
('__contains__', 'foo:policy'),
540
1703
record._calls[1:])
543
class TestBranchConfigItems(TestCase):
1705
def test_set_user_setting_sets_and_saves2(self):
1706
self.get_branch_config('/a/c')
1707
self.assertIs(self.my_config.get_user_option('foo'), None)
1708
self.my_config.set_user_option('foo', 'bar')
1710
self.my_config.branch.control_files.files['branch.conf'].strip(),
1712
self.assertEqual(self.my_config.get_user_option('foo'), 'bar')
1713
self.my_config.set_user_option('foo', 'baz',
1714
store=config.STORE_LOCATION)
1715
self.assertEqual(self.my_config.get_user_option('foo'), 'baz')
1716
self.my_config.set_user_option('foo', 'qux')
1717
self.assertEqual(self.my_config.get_user_option('foo'), 'baz')
1719
def test_get_bzr_remote_path(self):
1720
my_config = config.LocationConfig('/a/c')
1721
self.assertEqual('bzr', my_config.get_bzr_remote_path())
1722
my_config.set_user_option('bzr_remote_path', '/path-bzr')
1723
self.assertEqual('/path-bzr', my_config.get_bzr_remote_path())
1724
self.overrideEnv('BZR_REMOTE_PATH', '/environ-bzr')
1725
self.assertEqual('/environ-bzr', my_config.get_bzr_remote_path())
1728
precedence_global = 'option = global'
1729
precedence_branch = 'option = branch'
1730
precedence_location = """
1734
[http://example.com/specific]
1738
class TestBranchConfigItems(tests.TestCaseInTempDir):
1740
def get_branch_config(self, global_config=None, location=None,
1741
location_config=None, branch_data_config=None):
1742
my_branch = FakeBranch(location)
1743
if global_config is not None:
1744
config.GlobalConfig.from_string(global_config, save=True)
1745
if location_config is not None:
1746
config.LocationConfig.from_string(location_config, my_branch.base,
1748
my_config = config.BranchConfig(my_branch)
1749
if branch_data_config is not None:
1750
my_config.branch.control_files.files['branch.conf'] = \
545
1754
def test_user_id(self):
546
1755
branch = FakeBranch()
547
1756
my_config = config.BranchConfig(branch)
548
self.assertEqual("Robert Collins <robertc@example.net>",
549
my_config._get_user_id())
550
branch.control_files.email = "John"
551
self.assertEqual("John", my_config._get_user_id())
553
def test_not_set_in_branch(self):
554
branch = FakeBranch()
555
my_config = config.BranchConfig(branch)
556
branch.control_files.email = None
557
config_file = StringIO(sample_config_text)
558
(my_config._get_location_config().
559
_get_global_config()._get_parser(config_file))
560
self.assertEqual("Robert Collins <robertc@example.com>",
561
my_config._get_user_id())
562
branch.control_files.email = "John"
563
self.assertEqual("John", my_config._get_user_id())
565
def test_BZREMAIL_OVERRIDES(self):
566
os.environ['BZREMAIL'] = "Robert Collins <robertc@example.org>"
1757
self.assertIsNot(None, my_config.username())
1758
my_config.branch.control_files.files['email'] = "John"
1759
my_config.set_user_option('email',
1760
"Robert Collins <robertc@example.org>")
1761
self.assertEqual("Robert Collins <robertc@example.org>",
1762
my_config.username())
1764
def test_BZR_EMAIL_OVERRIDES(self):
1765
self.overrideEnv('BZR_EMAIL', "Robert Collins <robertc@example.org>")
567
1766
branch = FakeBranch()
568
1767
my_config = config.BranchConfig(branch)
569
1768
self.assertEqual("Robert Collins <robertc@example.org>",
570
1769
my_config.username())
572
1771
def test_signatures_forced(self):
573
branch = FakeBranch()
574
my_config = config.BranchConfig(branch)
575
config_file = StringIO(sample_always_signatures)
576
(my_config._get_location_config().
577
_get_global_config()._get_parser(config_file))
578
self.assertEqual(config.CHECK_ALWAYS, my_config.signature_checking())
1772
my_config = self.get_branch_config(
1773
global_config=sample_always_signatures)
1774
self.assertEqual(config.CHECK_NEVER,
1775
self.applyDeprecated(deprecated_in((2, 5, 0)),
1776
my_config.signature_checking))
1777
self.assertEqual(config.SIGN_ALWAYS,
1778
self.applyDeprecated(deprecated_in((2, 5, 0)),
1779
my_config.signing_policy))
1780
self.assertTrue(self.applyDeprecated(deprecated_in((2, 5, 0)),
1781
my_config.signature_needed))
1783
def test_signatures_forced_branch(self):
1784
my_config = self.get_branch_config(
1785
global_config=sample_ignore_signatures,
1786
branch_data_config=sample_always_signatures)
1787
self.assertEqual(config.CHECK_NEVER,
1788
self.applyDeprecated(deprecated_in((2, 5, 0)),
1789
my_config.signature_checking))
1790
self.assertEqual(config.SIGN_ALWAYS,
1791
self.applyDeprecated(deprecated_in((2, 5, 0)),
1792
my_config.signing_policy))
1793
self.assertTrue(self.applyDeprecated(deprecated_in((2, 5, 0)),
1794
my_config.signature_needed))
580
1796
def test_gpg_signing_command(self):
581
branch = FakeBranch()
582
my_config = config.BranchConfig(branch)
583
config_file = StringIO(sample_config_text)
584
(my_config._get_location_config().
585
_get_global_config()._get_parser(config_file))
586
self.assertEqual('gnome-gpg', my_config.gpg_signing_command())
1797
my_config = self.get_branch_config(
1798
global_config=sample_config_text,
1799
# branch data cannot set gpg_signing_command
1800
branch_data_config="gpg_signing_command=pgp")
1801
self.assertEqual('gnome-gpg',
1802
self.applyDeprecated(deprecated_in((2, 5, 0)),
1803
my_config.gpg_signing_command))
588
1805
def test_get_user_option_global(self):
589
branch = FakeBranch()
590
my_config = config.BranchConfig(branch)
591
config_file = StringIO(sample_config_text)
592
(my_config._get_location_config().
593
_get_global_config()._get_parser(config_file))
1806
my_config = self.get_branch_config(global_config=sample_config_text)
594
1807
self.assertEqual('something',
595
1808
my_config.get_user_option('user_global_option'))
597
1810
def test_post_commit_default(self):
598
branch = FakeBranch()
600
my_config = config.BranchConfig(branch)
601
config_file = StringIO(sample_config_text)
602
(my_config._get_location_config().
603
_get_global_config()._get_parser(config_file))
604
branch_file = StringIO(sample_branches_text)
605
my_config._get_location_config()._get_parser(branch_file)
606
self.assertEqual('bzrlib.tests.test_config.post_commit',
607
my_config.post_commit())
610
class TestMailAddressExtraction(TestCase):
1811
my_config = self.get_branch_config(global_config=sample_config_text,
1813
location_config=sample_branches_text)
1814
self.assertEqual(my_config.branch.base, '/a/c')
1815
self.assertEqual('bzrlib.tests.test_config.post_commit',
1816
self.applyDeprecated(deprecated_in((2, 5, 0)),
1817
my_config.post_commit))
1818
my_config.set_user_option('post_commit', 'rmtree_root')
1819
# post-commit is ignored when present in branch data
1820
self.assertEqual('bzrlib.tests.test_config.post_commit',
1821
self.applyDeprecated(deprecated_in((2, 5, 0)),
1822
my_config.post_commit))
1823
my_config.set_user_option('post_commit', 'rmtree_root',
1824
store=config.STORE_LOCATION)
1825
self.assertEqual('rmtree_root',
1826
self.applyDeprecated(deprecated_in((2, 5, 0)),
1827
my_config.post_commit))
1829
def test_config_precedence(self):
1830
# FIXME: eager test, luckily no persitent config file makes it fail
1832
my_config = self.get_branch_config(global_config=precedence_global)
1833
self.assertEqual(my_config.get_user_option('option'), 'global')
1834
my_config = self.get_branch_config(global_config=precedence_global,
1835
branch_data_config=precedence_branch)
1836
self.assertEqual(my_config.get_user_option('option'), 'branch')
1837
my_config = self.get_branch_config(
1838
global_config=precedence_global,
1839
branch_data_config=precedence_branch,
1840
location_config=precedence_location)
1841
self.assertEqual(my_config.get_user_option('option'), 'recurse')
1842
my_config = self.get_branch_config(
1843
global_config=precedence_global,
1844
branch_data_config=precedence_branch,
1845
location_config=precedence_location,
1846
location='http://example.com/specific')
1847
self.assertEqual(my_config.get_user_option('option'), 'exact')
1850
class TestMailAddressExtraction(tests.TestCase):
612
1852
def test_extract_email_address(self):
613
1853
self.assertEqual('jane@test.com',
614
1854
config.extract_email_address('Jane <jane@test.com>'))
615
self.assertRaises(errors.BzrError,
1855
self.assertRaises(errors.NoEmailInUsername,
616
1856
config.extract_email_address, 'Jane Tester')
1858
def test_parse_username(self):
1859
self.assertEqual(('', 'jdoe@example.com'),
1860
config.parse_username('jdoe@example.com'))
1861
self.assertEqual(('', 'jdoe@example.com'),
1862
config.parse_username('<jdoe@example.com>'))
1863
self.assertEqual(('John Doe', 'jdoe@example.com'),
1864
config.parse_username('John Doe <jdoe@example.com>'))
1865
self.assertEqual(('John Doe', ''),
1866
config.parse_username('John Doe'))
1867
self.assertEqual(('John Doe', 'jdoe@example.com'),
1868
config.parse_username('John Doe jdoe@example.com'))
1870
class TestTreeConfig(tests.TestCaseWithTransport):
1872
def test_get_value(self):
1873
"""Test that retreiving a value from a section is possible"""
1874
branch = self.make_branch('.')
1875
tree_config = config.TreeConfig(branch)
1876
tree_config.set_option('value', 'key', 'SECTION')
1877
tree_config.set_option('value2', 'key2')
1878
tree_config.set_option('value3-top', 'key3')
1879
tree_config.set_option('value3-section', 'key3', 'SECTION')
1880
value = tree_config.get_option('key', 'SECTION')
1881
self.assertEqual(value, 'value')
1882
value = tree_config.get_option('key2')
1883
self.assertEqual(value, 'value2')
1884
self.assertEqual(tree_config.get_option('non-existant'), None)
1885
value = tree_config.get_option('non-existant', 'SECTION')
1886
self.assertEqual(value, None)
1887
value = tree_config.get_option('non-existant', default='default')
1888
self.assertEqual(value, 'default')
1889
self.assertEqual(tree_config.get_option('key2', 'NOSECTION'), None)
1890
value = tree_config.get_option('key2', 'NOSECTION', default='default')
1891
self.assertEqual(value, 'default')
1892
value = tree_config.get_option('key3')
1893
self.assertEqual(value, 'value3-top')
1894
value = tree_config.get_option('key3', 'SECTION')
1895
self.assertEqual(value, 'value3-section')
1898
class TestTransportConfig(tests.TestCaseWithTransport):
1900
def test_load_utf8(self):
1901
"""Ensure we can load an utf8-encoded file."""
1902
t = self.get_transport()
1903
unicode_user = u'b\N{Euro Sign}ar'
1904
unicode_content = u'user=%s' % (unicode_user,)
1905
utf8_content = unicode_content.encode('utf8')
1906
# Store the raw content in the config file
1907
t.put_bytes('foo.conf', utf8_content)
1908
conf = config.TransportConfig(t, 'foo.conf')
1909
self.assertEquals(unicode_user, conf.get_option('user'))
1911
def test_load_non_ascii(self):
1912
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
1913
t = self.get_transport()
1914
t.put_bytes('foo.conf', 'user=foo\n#\xff\n')
1915
conf = config.TransportConfig(t, 'foo.conf')
1916
self.assertRaises(errors.ConfigContentError, conf._get_configobj)
1918
def test_load_erroneous_content(self):
1919
"""Ensure we display a proper error on content that can't be parsed."""
1920
t = self.get_transport()
1921
t.put_bytes('foo.conf', '[open_section\n')
1922
conf = config.TransportConfig(t, 'foo.conf')
1923
self.assertRaises(errors.ParseConfigError, conf._get_configobj)
1925
def test_load_permission_denied(self):
1926
"""Ensure we get an empty config file if the file is inaccessible."""
1929
warnings.append(args[0] % args[1:])
1930
self.overrideAttr(trace, 'warning', warning)
1932
class DenyingTransport(object):
1934
def __init__(self, base):
1937
def get_bytes(self, relpath):
1938
raise errors.PermissionDenied(relpath, "")
1940
cfg = config.TransportConfig(
1941
DenyingTransport("nonexisting://"), 'control.conf')
1942
self.assertIs(None, cfg.get_option('non-existant', 'SECTION'))
1945
[u'Permission denied while trying to open configuration file '
1946
u'nonexisting:///control.conf.'])
1948
def test_get_value(self):
1949
"""Test that retreiving a value from a section is possible"""
1950
bzrdir_config = config.TransportConfig(self.get_transport('.'),
1952
bzrdir_config.set_option('value', 'key', 'SECTION')
1953
bzrdir_config.set_option('value2', 'key2')
1954
bzrdir_config.set_option('value3-top', 'key3')
1955
bzrdir_config.set_option('value3-section', 'key3', 'SECTION')
1956
value = bzrdir_config.get_option('key', 'SECTION')
1957
self.assertEqual(value, 'value')
1958
value = bzrdir_config.get_option('key2')
1959
self.assertEqual(value, 'value2')
1960
self.assertEqual(bzrdir_config.get_option('non-existant'), None)
1961
value = bzrdir_config.get_option('non-existant', 'SECTION')
1962
self.assertEqual(value, None)
1963
value = bzrdir_config.get_option('non-existant', default='default')
1964
self.assertEqual(value, 'default')
1965
self.assertEqual(bzrdir_config.get_option('key2', 'NOSECTION'), None)
1966
value = bzrdir_config.get_option('key2', 'NOSECTION',
1968
self.assertEqual(value, 'default')
1969
value = bzrdir_config.get_option('key3')
1970
self.assertEqual(value, 'value3-top')
1971
value = bzrdir_config.get_option('key3', 'SECTION')
1972
self.assertEqual(value, 'value3-section')
1974
def test_set_unset_default_stack_on(self):
1975
my_dir = self.make_bzrdir('.')
1976
bzrdir_config = config.BzrDirConfig(my_dir)
1977
self.assertIs(None, bzrdir_config.get_default_stack_on())
1978
bzrdir_config.set_default_stack_on('Foo')
1979
self.assertEqual('Foo', bzrdir_config._config.get_option(
1980
'default_stack_on'))
1981
self.assertEqual('Foo', bzrdir_config.get_default_stack_on())
1982
bzrdir_config.set_default_stack_on(None)
1983
self.assertIs(None, bzrdir_config.get_default_stack_on())
1986
class TestOldConfigHooks(tests.TestCaseWithTransport):
1989
super(TestOldConfigHooks, self).setUp()
1990
create_configs_with_file_option(self)
1992
def assertGetHook(self, conf, name, value):
1996
config.OldConfigHooks.install_named_hook('get', hook, None)
1998
config.OldConfigHooks.uninstall_named_hook, 'get', None)
1999
self.assertLength(0, calls)
2000
actual_value = conf.get_user_option(name)
2001
self.assertEquals(value, actual_value)
2002
self.assertLength(1, calls)
2003
self.assertEquals((conf, name, value), calls[0])
2005
def test_get_hook_bazaar(self):
2006
self.assertGetHook(self.bazaar_config, 'file', 'bazaar')
2008
def test_get_hook_locations(self):
2009
self.assertGetHook(self.locations_config, 'file', 'locations')
2011
def test_get_hook_branch(self):
2012
# Since locations masks branch, we define a different option
2013
self.branch_config.set_user_option('file2', 'branch')
2014
self.assertGetHook(self.branch_config, 'file2', 'branch')
2016
def assertSetHook(self, conf, name, value):
2020
config.OldConfigHooks.install_named_hook('set', hook, None)
2022
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2023
self.assertLength(0, calls)
2024
conf.set_user_option(name, value)
2025
self.assertLength(1, calls)
2026
# We can't assert the conf object below as different configs use
2027
# different means to implement set_user_option and we care only about
2029
self.assertEquals((name, value), calls[0][1:])
2031
def test_set_hook_bazaar(self):
2032
self.assertSetHook(self.bazaar_config, 'foo', 'bazaar')
2034
def test_set_hook_locations(self):
2035
self.assertSetHook(self.locations_config, 'foo', 'locations')
2037
def test_set_hook_branch(self):
2038
self.assertSetHook(self.branch_config, 'foo', 'branch')
2040
def assertRemoveHook(self, conf, name, section_name=None):
2044
config.OldConfigHooks.install_named_hook('remove', hook, None)
2046
config.OldConfigHooks.uninstall_named_hook, 'remove', None)
2047
self.assertLength(0, calls)
2048
conf.remove_user_option(name, section_name)
2049
self.assertLength(1, calls)
2050
# We can't assert the conf object below as different configs use
2051
# different means to implement remove_user_option and we care only about
2053
self.assertEquals((name,), calls[0][1:])
2055
def test_remove_hook_bazaar(self):
2056
self.assertRemoveHook(self.bazaar_config, 'file')
2058
def test_remove_hook_locations(self):
2059
self.assertRemoveHook(self.locations_config, 'file',
2060
self.locations_config.location)
2062
def test_remove_hook_branch(self):
2063
self.assertRemoveHook(self.branch_config, 'file')
2065
def assertLoadHook(self, name, conf_class, *conf_args):
2069
config.OldConfigHooks.install_named_hook('load', hook, None)
2071
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2072
self.assertLength(0, calls)
2074
conf = conf_class(*conf_args)
2075
# Access an option to trigger a load
2076
conf.get_user_option(name)
2077
self.assertLength(1, calls)
2078
# Since we can't assert about conf, we just use the number of calls ;-/
2080
def test_load_hook_bazaar(self):
2081
self.assertLoadHook('file', config.GlobalConfig)
2083
def test_load_hook_locations(self):
2084
self.assertLoadHook('file', config.LocationConfig, self.tree.basedir)
2086
def test_load_hook_branch(self):
2087
self.assertLoadHook('file', config.BranchConfig, self.tree.branch)
2089
def assertSaveHook(self, conf):
2093
config.OldConfigHooks.install_named_hook('save', hook, None)
2095
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2096
self.assertLength(0, calls)
2097
# Setting an option triggers a save
2098
conf.set_user_option('foo', 'bar')
2099
self.assertLength(1, calls)
2100
# Since we can't assert about conf, we just use the number of calls ;-/
2102
def test_save_hook_bazaar(self):
2103
self.assertSaveHook(self.bazaar_config)
2105
def test_save_hook_locations(self):
2106
self.assertSaveHook(self.locations_config)
2108
def test_save_hook_branch(self):
2109
self.assertSaveHook(self.branch_config)
2112
class TestOldConfigHooksForRemote(tests.TestCaseWithTransport):
2113
"""Tests config hooks for remote configs.
2115
No tests for the remove hook as this is not implemented there.
2119
super(TestOldConfigHooksForRemote, self).setUp()
2120
self.transport_server = test_server.SmartTCPServer_for_testing
2121
create_configs_with_file_option(self)
2123
def assertGetHook(self, conf, name, value):
2127
config.OldConfigHooks.install_named_hook('get', hook, None)
2129
config.OldConfigHooks.uninstall_named_hook, 'get', None)
2130
self.assertLength(0, calls)
2131
actual_value = conf.get_option(name)
2132
self.assertEquals(value, actual_value)
2133
self.assertLength(1, calls)
2134
self.assertEquals((conf, name, value), calls[0])
2136
def test_get_hook_remote_branch(self):
2137
remote_branch = branch.Branch.open(self.get_url('tree'))
2138
self.assertGetHook(remote_branch._get_config(), 'file', 'branch')
2140
def test_get_hook_remote_bzrdir(self):
2141
remote_bzrdir = controldir.ControlDir.open(self.get_url('tree'))
2142
conf = remote_bzrdir._get_config()
2143
conf.set_option('remotedir', 'file')
2144
self.assertGetHook(conf, 'file', 'remotedir')
2146
def assertSetHook(self, conf, name, value):
2150
config.OldConfigHooks.install_named_hook('set', hook, None)
2152
config.OldConfigHooks.uninstall_named_hook, 'set', None)
2153
self.assertLength(0, calls)
2154
conf.set_option(value, name)
2155
self.assertLength(1, calls)
2156
# We can't assert the conf object below as different configs use
2157
# different means to implement set_user_option and we care only about
2159
self.assertEquals((name, value), calls[0][1:])
2161
def test_set_hook_remote_branch(self):
2162
remote_branch = branch.Branch.open(self.get_url('tree'))
2163
self.addCleanup(remote_branch.lock_write().unlock)
2164
self.assertSetHook(remote_branch._get_config(), 'file', 'remote')
2166
def test_set_hook_remote_bzrdir(self):
2167
remote_branch = branch.Branch.open(self.get_url('tree'))
2168
self.addCleanup(remote_branch.lock_write().unlock)
2169
remote_bzrdir = controldir.ControlDir.open(self.get_url('tree'))
2170
self.assertSetHook(remote_bzrdir._get_config(), 'file', 'remotedir')
2172
def assertLoadHook(self, expected_nb_calls, name, conf_class, *conf_args):
2176
config.OldConfigHooks.install_named_hook('load', hook, None)
2178
config.OldConfigHooks.uninstall_named_hook, 'load', None)
2179
self.assertLength(0, calls)
2181
conf = conf_class(*conf_args)
2182
# Access an option to trigger a load
2183
conf.get_option(name)
2184
self.assertLength(expected_nb_calls, calls)
2185
# Since we can't assert about conf, we just use the number of calls ;-/
2187
def test_load_hook_remote_branch(self):
2188
remote_branch = branch.Branch.open(self.get_url('tree'))
2189
self.assertLoadHook(1, 'file', remote.RemoteBranchConfig, remote_branch)
2191
def test_load_hook_remote_bzrdir(self):
2192
remote_bzrdir = controldir.ControlDir.open(self.get_url('tree'))
2193
# The config file doesn't exist, set an option to force its creation
2194
conf = remote_bzrdir._get_config()
2195
conf.set_option('remotedir', 'file')
2196
# We get one call for the server and one call for the client, this is
2197
# caused by the differences in implementations betwen
2198
# SmartServerBzrDirRequestConfigFile (in smart/bzrdir.py) and
2199
# SmartServerBranchGetConfigFile (in smart/branch.py)
2200
self.assertLoadHook(2 ,'file', remote.RemoteBzrDirConfig, remote_bzrdir)
2202
def assertSaveHook(self, conf):
2206
config.OldConfigHooks.install_named_hook('save', hook, None)
2208
config.OldConfigHooks.uninstall_named_hook, 'save', None)
2209
self.assertLength(0, calls)
2210
# Setting an option triggers a save
2211
conf.set_option('foo', 'bar')
2212
self.assertLength(1, calls)
2213
# Since we can't assert about conf, we just use the number of calls ;-/
2215
def test_save_hook_remote_branch(self):
2216
remote_branch = branch.Branch.open(self.get_url('tree'))
2217
self.addCleanup(remote_branch.lock_write().unlock)
2218
self.assertSaveHook(remote_branch._get_config())
2220
def test_save_hook_remote_bzrdir(self):
2221
remote_branch = branch.Branch.open(self.get_url('tree'))
2222
self.addCleanup(remote_branch.lock_write().unlock)
2223
remote_bzrdir = controldir.ControlDir.open(self.get_url('tree'))
2224
self.assertSaveHook(remote_bzrdir._get_config())
2227
class TestOptionNames(tests.TestCase):
2229
def is_valid(self, name):
2230
return config._option_ref_re.match('{%s}' % name) is not None
2232
def test_valid_names(self):
2233
self.assertTrue(self.is_valid('foo'))
2234
self.assertTrue(self.is_valid('foo.bar'))
2235
self.assertTrue(self.is_valid('f1'))
2236
self.assertTrue(self.is_valid('_'))
2237
self.assertTrue(self.is_valid('__bar__'))
2238
self.assertTrue(self.is_valid('a_'))
2239
self.assertTrue(self.is_valid('a1'))
2241
def test_invalid_names(self):
2242
self.assertFalse(self.is_valid(' foo'))
2243
self.assertFalse(self.is_valid('foo '))
2244
self.assertFalse(self.is_valid('1'))
2245
self.assertFalse(self.is_valid('1,2'))
2246
self.assertFalse(self.is_valid('foo$'))
2247
self.assertFalse(self.is_valid('!foo'))
2248
self.assertFalse(self.is_valid('foo.'))
2249
self.assertFalse(self.is_valid('foo..bar'))
2250
self.assertFalse(self.is_valid('{}'))
2251
self.assertFalse(self.is_valid('{a}'))
2252
self.assertFalse(self.is_valid('a\n'))
2254
def assertSingleGroup(self, reference):
2255
# the regexp is used with split and as such should match the reference
2256
# *only*, if more groups needs to be defined, (?:...) should be used.
2257
m = config._option_ref_re.match('{a}')
2258
self.assertLength(1, m.groups())
2260
def test_valid_references(self):
2261
self.assertSingleGroup('{a}')
2262
self.assertSingleGroup('{{a}}')
2265
class TestOption(tests.TestCase):
2267
def test_default_value(self):
2268
opt = config.Option('foo', default='bar')
2269
self.assertEquals('bar', opt.get_default())
2271
def test_callable_default_value(self):
2272
def bar_as_unicode():
2274
opt = config.Option('foo', default=bar_as_unicode)
2275
self.assertEquals('bar', opt.get_default())
2277
def test_default_value_from_env(self):
2278
opt = config.Option('foo', default='bar', default_from_env=['FOO'])
2279
self.overrideEnv('FOO', 'quux')
2280
# Env variable provides a default taking over the option one
2281
self.assertEquals('quux', opt.get_default())
2283
def test_first_default_value_from_env_wins(self):
2284
opt = config.Option('foo', default='bar',
2285
default_from_env=['NO_VALUE', 'FOO', 'BAZ'])
2286
self.overrideEnv('FOO', 'foo')
2287
self.overrideEnv('BAZ', 'baz')
2288
# The first env var set wins
2289
self.assertEquals('foo', opt.get_default())
2291
def test_not_supported_list_default_value(self):
2292
self.assertRaises(AssertionError, config.Option, 'foo', default=[1])
2294
def test_not_supported_object_default_value(self):
2295
self.assertRaises(AssertionError, config.Option, 'foo',
2298
def test_not_supported_callable_default_value_not_unicode(self):
2299
def bar_not_unicode():
2301
opt = config.Option('foo', default=bar_not_unicode)
2302
self.assertRaises(AssertionError, opt.get_default)
2304
def test_get_help_topic(self):
2305
opt = config.Option('foo')
2306
self.assertEquals('foo', opt.get_help_topic())
2309
class TestOptionConverterMixin(object):
2311
def assertConverted(self, expected, opt, value):
2312
self.assertEquals(expected, opt.convert_from_unicode(None, value))
2314
def assertWarns(self, opt, value):
2317
warnings.append(args[0] % args[1:])
2318
self.overrideAttr(trace, 'warning', warning)
2319
self.assertEquals(None, opt.convert_from_unicode(None, value))
2320
self.assertLength(1, warnings)
2322
'Value "%s" is not valid for "%s"' % (value, opt.name),
2325
def assertErrors(self, opt, value):
2326
self.assertRaises(errors.ConfigOptionValueError,
2327
opt.convert_from_unicode, None, value)
2329
def assertConvertInvalid(self, opt, invalid_value):
2331
self.assertEquals(None, opt.convert_from_unicode(None, invalid_value))
2332
opt.invalid = 'warning'
2333
self.assertWarns(opt, invalid_value)
2334
opt.invalid = 'error'
2335
self.assertErrors(opt, invalid_value)
2338
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2340
def get_option(self):
2341
return config.Option('foo', help='A boolean.',
2342
from_unicode=config.bool_from_store)
2344
def test_convert_invalid(self):
2345
opt = self.get_option()
2346
# A string that is not recognized as a boolean
2347
self.assertConvertInvalid(opt, u'invalid-boolean')
2348
# A list of strings is never recognized as a boolean
2349
self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2351
def test_convert_valid(self):
2352
opt = self.get_option()
2353
self.assertConverted(True, opt, u'True')
2354
self.assertConverted(True, opt, u'1')
2355
self.assertConverted(False, opt, u'False')
2358
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2360
def get_option(self):
2361
return config.Option('foo', help='An integer.',
2362
from_unicode=config.int_from_store)
2364
def test_convert_invalid(self):
2365
opt = self.get_option()
2366
# A string that is not recognized as an integer
2367
self.assertConvertInvalid(opt, u'forty-two')
2368
# A list of strings is never recognized as an integer
2369
self.assertConvertInvalid(opt, [u'a', u'list'])
2371
def test_convert_valid(self):
2372
opt = self.get_option()
2373
self.assertConverted(16, opt, u'16')
2376
class TestOptionWithSIUnitConverter(tests.TestCase, TestOptionConverterMixin):
2378
def get_option(self):
2379
return config.Option('foo', help='An integer in SI units.',
2380
from_unicode=config.int_SI_from_store)
2382
def test_convert_invalid(self):
2383
opt = self.get_option()
2384
self.assertConvertInvalid(opt, u'not-a-unit')
2385
self.assertConvertInvalid(opt, u'Gb') # Forgot the int
2386
self.assertConvertInvalid(opt, u'1b') # Forgot the unit
2387
self.assertConvertInvalid(opt, u'1GG')
2388
self.assertConvertInvalid(opt, u'1Mbb')
2389
self.assertConvertInvalid(opt, u'1MM')
2391
def test_convert_valid(self):
2392
opt = self.get_option()
2393
self.assertConverted(int(5e3), opt, u'5kb')
2394
self.assertConverted(int(5e6), opt, u'5M')
2395
self.assertConverted(int(5e6), opt, u'5MB')
2396
self.assertConverted(int(5e9), opt, u'5g')
2397
self.assertConverted(int(5e9), opt, u'5gB')
2398
self.assertConverted(100, opt, u'100')
2401
class TestListOption(tests.TestCase, TestOptionConverterMixin):
2403
def get_option(self):
2404
return config.ListOption('foo', help='A list.')
2406
def test_convert_invalid(self):
2407
opt = self.get_option()
2408
# We don't even try to convert a list into a list, we only expect
2410
self.assertConvertInvalid(opt, [1])
2411
# No string is invalid as all forms can be converted to a list
2413
def test_convert_valid(self):
2414
opt = self.get_option()
2415
# An empty string is an empty list
2416
self.assertConverted([], opt, '') # Using a bare str() just in case
2417
self.assertConverted([], opt, u'')
2419
self.assertConverted([u'True'], opt, u'True')
2421
self.assertConverted([u'42'], opt, u'42')
2423
self.assertConverted([u'bar'], opt, u'bar')
2426
class TestRegistryOption(tests.TestCase, TestOptionConverterMixin):
2428
def get_option(self, registry):
2429
return config.RegistryOption('foo', registry,
2430
help='A registry option.')
2432
def test_convert_invalid(self):
2433
registry = _mod_registry.Registry()
2434
opt = self.get_option(registry)
2435
self.assertConvertInvalid(opt, [1])
2436
self.assertConvertInvalid(opt, u"notregistered")
2438
def test_convert_valid(self):
2439
registry = _mod_registry.Registry()
2440
registry.register("someval", 1234)
2441
opt = self.get_option(registry)
2442
# Using a bare str() just in case
2443
self.assertConverted(1234, opt, "someval")
2444
self.assertConverted(1234, opt, u'someval')
2445
self.assertConverted(None, opt, None)
2447
def test_help(self):
2448
registry = _mod_registry.Registry()
2449
registry.register("someval", 1234, help="some option")
2450
registry.register("dunno", 1234, help="some other option")
2451
opt = self.get_option(registry)
2453
'A registry option.\n'
2455
'The following values are supported:\n'
2456
' dunno - some other option\n'
2457
' someval - some option\n',
2460
def test_get_help_text(self):
2461
registry = _mod_registry.Registry()
2462
registry.register("someval", 1234, help="some option")
2463
registry.register("dunno", 1234, help="some other option")
2464
opt = self.get_option(registry)
2466
'A registry option.\n'
2468
'The following values are supported:\n'
2469
' dunno - some other option\n'
2470
' someval - some option\n',
2471
opt.get_help_text())
2474
class TestOptionRegistry(tests.TestCase):
2477
super(TestOptionRegistry, self).setUp()
2478
# Always start with an empty registry
2479
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2480
self.registry = config.option_registry
2482
def test_register(self):
2483
opt = config.Option('foo')
2484
self.registry.register(opt)
2485
self.assertIs(opt, self.registry.get('foo'))
2487
def test_registered_help(self):
2488
opt = config.Option('foo', help='A simple option')
2489
self.registry.register(opt)
2490
self.assertEquals('A simple option', self.registry.get_help('foo'))
2492
def test_dont_register_illegal_name(self):
2493
self.assertRaises(errors.IllegalOptionName,
2494
self.registry.register, config.Option(' foo'))
2495
self.assertRaises(errors.IllegalOptionName,
2496
self.registry.register, config.Option('bar,'))
2498
lazy_option = config.Option('lazy_foo', help='Lazy help')
2500
def test_register_lazy(self):
2501
self.registry.register_lazy('lazy_foo', self.__module__,
2502
'TestOptionRegistry.lazy_option')
2503
self.assertIs(self.lazy_option, self.registry.get('lazy_foo'))
2505
def test_registered_lazy_help(self):
2506
self.registry.register_lazy('lazy_foo', self.__module__,
2507
'TestOptionRegistry.lazy_option')
2508
self.assertEquals('Lazy help', self.registry.get_help('lazy_foo'))
2510
def test_dont_lazy_register_illegal_name(self):
2511
# This is where the root cause of http://pad.lv/1235099 is better
2512
# understood: 'register_lazy' doc string mentions that key should match
2513
# the option name which indirectly requires that the option name is a
2514
# valid python identifier. We violate that rule here (using a key that
2515
# doesn't match the option name) to test the option name checking.
2516
self.assertRaises(errors.IllegalOptionName,
2517
self.registry.register_lazy, ' foo', self.__module__,
2518
'TestOptionRegistry.lazy_option')
2519
self.assertRaises(errors.IllegalOptionName,
2520
self.registry.register_lazy, '1,2', self.__module__,
2521
'TestOptionRegistry.lazy_option')
2524
class TestRegisteredOptions(tests.TestCase):
2525
"""All registered options should verify some constraints."""
2527
scenarios = [(key, {'option_name': key, 'option': option}) for key, option
2528
in config.option_registry.iteritems()]
2531
super(TestRegisteredOptions, self).setUp()
2532
self.registry = config.option_registry
2534
def test_proper_name(self):
2535
# An option should be registered under its own name, this can't be
2536
# checked at registration time for the lazy ones.
2537
self.assertEquals(self.option_name, self.option.name)
2539
def test_help_is_set(self):
2540
option_help = self.registry.get_help(self.option_name)
2541
# Come on, think about the user, he really wants to know what the
2543
self.assertIsNot(None, option_help)
2544
self.assertNotEquals('', option_help)
2547
class TestSection(tests.TestCase):
2549
# FIXME: Parametrize so that all sections produced by Stores run these
2550
# tests -- vila 2011-04-01
2552
def test_get_a_value(self):
2553
a_dict = dict(foo='bar')
2554
section = config.Section('myID', a_dict)
2555
self.assertEquals('bar', section.get('foo'))
2557
def test_get_unknown_option(self):
2559
section = config.Section(None, a_dict)
2560
self.assertEquals('out of thin air',
2561
section.get('foo', 'out of thin air'))
2563
def test_options_is_shared(self):
2565
section = config.Section(None, a_dict)
2566
self.assertIs(a_dict, section.options)
2569
class TestMutableSection(tests.TestCase):
2571
scenarios = [('mutable',
2573
lambda opts: config.MutableSection('myID', opts)},),
2577
a_dict = dict(foo='bar')
2578
section = self.get_section(a_dict)
2579
section.set('foo', 'new_value')
2580
self.assertEquals('new_value', section.get('foo'))
2581
# The change appears in the shared section
2582
self.assertEquals('new_value', a_dict.get('foo'))
2583
# We keep track of the change
2584
self.assertTrue('foo' in section.orig)
2585
self.assertEquals('bar', section.orig.get('foo'))
2587
def test_set_preserve_original_once(self):
2588
a_dict = dict(foo='bar')
2589
section = self.get_section(a_dict)
2590
section.set('foo', 'first_value')
2591
section.set('foo', 'second_value')
2592
# We keep track of the original value
2593
self.assertTrue('foo' in section.orig)
2594
self.assertEquals('bar', section.orig.get('foo'))
2596
def test_remove(self):
2597
a_dict = dict(foo='bar')
2598
section = self.get_section(a_dict)
2599
section.remove('foo')
2600
# We get None for unknown options via the default value
2601
self.assertEquals(None, section.get('foo'))
2602
# Or we just get the default value
2603
self.assertEquals('unknown', section.get('foo', 'unknown'))
2604
self.assertFalse('foo' in section.options)
2605
# We keep track of the deletion
2606
self.assertTrue('foo' in section.orig)
2607
self.assertEquals('bar', section.orig.get('foo'))
2609
def test_remove_new_option(self):
2611
section = self.get_section(a_dict)
2612
section.set('foo', 'bar')
2613
section.remove('foo')
2614
self.assertFalse('foo' in section.options)
2615
# The option didn't exist initially so it we need to keep track of it
2616
# with a special value
2617
self.assertTrue('foo' in section.orig)
2618
self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
2621
class TestCommandLineStore(tests.TestCase):
2624
super(TestCommandLineStore, self).setUp()
2625
self.store = config.CommandLineStore()
2626
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
2628
def get_section(self):
2629
"""Get the unique section for the command line overrides."""
2630
sections = list(self.store.get_sections())
2631
self.assertLength(1, sections)
2632
store, section = sections[0]
2633
self.assertEquals(self.store, store)
2636
def test_no_override(self):
2637
self.store._from_cmdline([])
2638
section = self.get_section()
2639
self.assertLength(0, list(section.iter_option_names()))
2641
def test_simple_override(self):
2642
self.store._from_cmdline(['a=b'])
2643
section = self.get_section()
2644
self.assertEqual('b', section.get('a'))
2646
def test_list_override(self):
2647
opt = config.ListOption('l')
2648
config.option_registry.register(opt)
2649
self.store._from_cmdline(['l=1,2,3'])
2650
val = self.get_section().get('l')
2651
self.assertEqual('1,2,3', val)
2652
# Reminder: lists should be registered as such explicitely, otherwise
2653
# the conversion needs to be done afterwards.
2654
self.assertEqual(['1', '2', '3'],
2655
opt.convert_from_unicode(self.store, val))
2657
def test_multiple_overrides(self):
2658
self.store._from_cmdline(['a=b', 'x=y'])
2659
section = self.get_section()
2660
self.assertEquals('b', section.get('a'))
2661
self.assertEquals('y', section.get('x'))
2663
def test_wrong_syntax(self):
2664
self.assertRaises(errors.BzrCommandError,
2665
self.store._from_cmdline, ['a=b', 'c'])
2667
class TestStoreMinimalAPI(tests.TestCaseWithTransport):
2669
scenarios = [(key, {'get_store': builder}) for key, builder
2670
in config.test_store_builder_registry.iteritems()] + [
2671
('cmdline', {'get_store': lambda test: config.CommandLineStore()})]
2674
store = self.get_store(self)
2675
if type(store) == config.TransportIniFileStore:
2676
raise tests.TestNotApplicable(
2677
"%s is not a concrete Store implementation"
2678
" so it doesn't need an id" % (store.__class__.__name__,))
2679
self.assertIsNot(None, store.id)
2682
class TestStore(tests.TestCaseWithTransport):
2684
def assertSectionContent(self, expected, (store, section)):
2685
"""Assert that some options have the proper values in a section."""
2686
expected_name, expected_options = expected
2687
self.assertEquals(expected_name, section.id)
2690
dict([(k, section.get(k)) for k in expected_options.keys()]))
2693
class TestReadonlyStore(TestStore):
2695
scenarios = [(key, {'get_store': builder}) for key, builder
2696
in config.test_store_builder_registry.iteritems()]
2698
def test_building_delays_load(self):
2699
store = self.get_store(self)
2700
self.assertEquals(False, store.is_loaded())
2701
store._load_from_string('')
2702
self.assertEquals(True, store.is_loaded())
2704
def test_get_no_sections_for_empty(self):
2705
store = self.get_store(self)
2706
store._load_from_string('')
2707
self.assertEquals([], list(store.get_sections()))
2709
def test_get_default_section(self):
2710
store = self.get_store(self)
2711
store._load_from_string('foo=bar')
2712
sections = list(store.get_sections())
2713
self.assertLength(1, sections)
2714
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2716
def test_get_named_section(self):
2717
store = self.get_store(self)
2718
store._load_from_string('[baz]\nfoo=bar')
2719
sections = list(store.get_sections())
2720
self.assertLength(1, sections)
2721
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2723
def test_load_from_string_fails_for_non_empty_store(self):
2724
store = self.get_store(self)
2725
store._load_from_string('foo=bar')
2726
self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2729
class TestStoreQuoting(TestStore):
2731
scenarios = [(key, {'get_store': builder}) for key, builder
2732
in config.test_store_builder_registry.iteritems()]
2735
super(TestStoreQuoting, self).setUp()
2736
self.store = self.get_store(self)
2737
# We need a loaded store but any content will do
2738
self.store._load_from_string('')
2740
def assertIdempotent(self, s):
2741
"""Assert that quoting an unquoted string is a no-op and vice-versa.
2743
What matters here is that option values, as they appear in a store, can
2744
be safely round-tripped out of the store and back.
2746
:param s: A string, quoted if required.
2748
self.assertEquals(s, self.store.quote(self.store.unquote(s)))
2749
self.assertEquals(s, self.store.unquote(self.store.quote(s)))
2751
def test_empty_string(self):
2752
if isinstance(self.store, config.IniFileStore):
2753
# configobj._quote doesn't handle empty values
2754
self.assertRaises(AssertionError,
2755
self.assertIdempotent, '')
2757
self.assertIdempotent('')
2758
# But quoted empty strings are ok
2759
self.assertIdempotent('""')
2761
def test_embedded_spaces(self):
2762
self.assertIdempotent('" a b c "')
2764
def test_embedded_commas(self):
2765
self.assertIdempotent('" a , b c "')
2767
def test_simple_comma(self):
2768
if isinstance(self.store, config.IniFileStore):
2769
# configobj requires that lists are special-cased
2770
self.assertRaises(AssertionError,
2771
self.assertIdempotent, ',')
2773
self.assertIdempotent(',')
2774
# When a single comma is required, quoting is also required
2775
self.assertIdempotent('","')
2777
def test_list(self):
2778
if isinstance(self.store, config.IniFileStore):
2779
# configobj requires that lists are special-cased
2780
self.assertRaises(AssertionError,
2781
self.assertIdempotent, 'a,b')
2783
self.assertIdempotent('a,b')
2786
class TestDictFromStore(tests.TestCase):
2788
def test_unquote_not_string(self):
2789
conf = config.MemoryStack('x=2\n[a_section]\na=1\n')
2790
value = conf.get('a_section')
2791
# Urgh, despite 'conf' asking for the no-name section, we get the
2792
# content of another section as a dict o_O
2793
self.assertEquals({'a': '1'}, value)
2794
unquoted = conf.store.unquote(value)
2795
# Which cannot be unquoted but shouldn't crash either (the use cases
2796
# are getting the value or displaying it. In the later case, '%s' will
2798
self.assertEquals({'a': '1'}, unquoted)
2799
self.assertEquals("{u'a': u'1'}", '%s' % (unquoted,))
2802
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2803
"""Simulate loading a config store with content of various encodings.
2805
All files produced by bzr are in utf8 content.
2807
Users may modify them manually and end up with a file that can't be
2808
loaded. We need to issue proper error messages in this case.
2811
invalid_utf8_char = '\xff'
2813
def test_load_utf8(self):
2814
"""Ensure we can load an utf8-encoded file."""
2815
t = self.get_transport()
2816
# From http://pad.lv/799212
2817
unicode_user = u'b\N{Euro Sign}ar'
2818
unicode_content = u'user=%s' % (unicode_user,)
2819
utf8_content = unicode_content.encode('utf8')
2820
# Store the raw content in the config file
2821
t.put_bytes('foo.conf', utf8_content)
2822
store = config.TransportIniFileStore(t, 'foo.conf')
2824
stack = config.Stack([store.get_sections], store)
2825
self.assertEquals(unicode_user, stack.get('user'))
2827
def test_load_non_ascii(self):
2828
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2829
t = self.get_transport()
2830
t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2831
store = config.TransportIniFileStore(t, 'foo.conf')
2832
self.assertRaises(errors.ConfigContentError, store.load)
2834
def test_load_erroneous_content(self):
2835
"""Ensure we display a proper error on content that can't be parsed."""
2836
t = self.get_transport()
2837
t.put_bytes('foo.conf', '[open_section\n')
2838
store = config.TransportIniFileStore(t, 'foo.conf')
2839
self.assertRaises(errors.ParseConfigError, store.load)
2841
def test_load_permission_denied(self):
2842
"""Ensure we get warned when trying to load an inaccessible file."""
2845
warnings.append(args[0] % args[1:])
2846
self.overrideAttr(trace, 'warning', warning)
2848
t = self.get_transport()
2850
def get_bytes(relpath):
2851
raise errors.PermissionDenied(relpath, "")
2852
t.get_bytes = get_bytes
2853
store = config.TransportIniFileStore(t, 'foo.conf')
2854
self.assertRaises(errors.PermissionDenied, store.load)
2857
[u'Permission denied while trying to load configuration store %s.'
2858
% store.external_url()])
2861
class TestIniConfigContent(tests.TestCaseWithTransport):
2862
"""Simulate loading a IniBasedConfig with content of various encodings.
2864
All files produced by bzr are in utf8 content.
2866
Users may modify them manually and end up with a file that can't be
2867
loaded. We need to issue proper error messages in this case.
2870
invalid_utf8_char = '\xff'
2872
def test_load_utf8(self):
2873
"""Ensure we can load an utf8-encoded file."""
2874
# From http://pad.lv/799212
2875
unicode_user = u'b\N{Euro Sign}ar'
2876
unicode_content = u'user=%s' % (unicode_user,)
2877
utf8_content = unicode_content.encode('utf8')
2878
# Store the raw content in the config file
2879
with open('foo.conf', 'wb') as f:
2880
f.write(utf8_content)
2881
conf = config.IniBasedConfig(file_name='foo.conf')
2882
self.assertEquals(unicode_user, conf.get_user_option('user'))
2884
def test_load_badly_encoded_content(self):
2885
"""Ensure we display a proper error on non-ascii, non utf-8 content."""
2886
with open('foo.conf', 'wb') as f:
2887
f.write('user=foo\n#%s\n' % (self.invalid_utf8_char,))
2888
conf = config.IniBasedConfig(file_name='foo.conf')
2889
self.assertRaises(errors.ConfigContentError, conf._get_parser)
2891
def test_load_erroneous_content(self):
2892
"""Ensure we display a proper error on content that can't be parsed."""
2893
with open('foo.conf', 'wb') as f:
2894
f.write('[open_section\n')
2895
conf = config.IniBasedConfig(file_name='foo.conf')
2896
self.assertRaises(errors.ParseConfigError, conf._get_parser)
2899
class TestMutableStore(TestStore):
2901
scenarios = [(key, {'store_id': key, 'get_store': builder}) for key, builder
2902
in config.test_store_builder_registry.iteritems()]
2905
super(TestMutableStore, self).setUp()
2906
self.transport = self.get_transport()
2908
def has_store(self, store):
2909
store_basename = urlutils.relative_url(self.transport.external_url(),
2910
store.external_url())
2911
return self.transport.has(store_basename)
2913
def test_save_empty_creates_no_file(self):
2914
# FIXME: There should be a better way than relying on the test
2915
# parametrization to identify branch.conf -- vila 2011-0526
2916
if self.store_id in ('branch', 'remote_branch'):
2917
raise tests.TestNotApplicable(
2918
'branch.conf is *always* created when a branch is initialized')
2919
store = self.get_store(self)
2921
self.assertEquals(False, self.has_store(store))
2923
def test_mutable_section_shared(self):
2924
store = self.get_store(self)
2925
store._load_from_string('foo=bar\n')
2926
# FIXME: There should be a better way than relying on the test
2927
# parametrization to identify branch.conf -- vila 2011-0526
2928
if self.store_id in ('branch', 'remote_branch'):
2929
# branch stores requires write locked branches
2930
self.addCleanup(store.branch.lock_write().unlock)
2931
section1 = store.get_mutable_section(None)
2932
section2 = store.get_mutable_section(None)
2933
# If we get different sections, different callers won't share the
2935
self.assertIs(section1, section2)
2937
def test_save_emptied_succeeds(self):
2938
store = self.get_store(self)
2939
store._load_from_string('foo=bar\n')
2940
# FIXME: There should be a better way than relying on the test
2941
# parametrization to identify branch.conf -- vila 2011-0526
2942
if self.store_id in ('branch', 'remote_branch'):
2943
# branch stores requires write locked branches
2944
self.addCleanup(store.branch.lock_write().unlock)
2945
section = store.get_mutable_section(None)
2946
section.remove('foo')
2948
self.assertEquals(True, self.has_store(store))
2949
modified_store = self.get_store(self)
2950
sections = list(modified_store.get_sections())
2951
self.assertLength(0, sections)
2953
def test_save_with_content_succeeds(self):
2954
# FIXME: There should be a better way than relying on the test
2955
# parametrization to identify branch.conf -- vila 2011-0526
2956
if self.store_id in ('branch', 'remote_branch'):
2957
raise tests.TestNotApplicable(
2958
'branch.conf is *always* created when a branch is initialized')
2959
store = self.get_store(self)
2960
store._load_from_string('foo=bar\n')
2961
self.assertEquals(False, self.has_store(store))
2963
self.assertEquals(True, self.has_store(store))
2964
modified_store = self.get_store(self)
2965
sections = list(modified_store.get_sections())
2966
self.assertLength(1, sections)
2967
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2969
def test_set_option_in_empty_store(self):
2970
store = self.get_store(self)
2971
# FIXME: There should be a better way than relying on the test
2972
# parametrization to identify branch.conf -- vila 2011-0526
2973
if self.store_id in ('branch', 'remote_branch'):
2974
# branch stores requires write locked branches
2975
self.addCleanup(store.branch.lock_write().unlock)
2976
section = store.get_mutable_section(None)
2977
section.set('foo', 'bar')
2979
modified_store = self.get_store(self)
2980
sections = list(modified_store.get_sections())
2981
self.assertLength(1, sections)
2982
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
2984
def test_set_option_in_default_section(self):
2985
store = self.get_store(self)
2986
store._load_from_string('')
2987
# FIXME: There should be a better way than relying on the test
2988
# parametrization to identify branch.conf -- vila 2011-0526
2989
if self.store_id in ('branch', 'remote_branch'):
2990
# branch stores requires write locked branches
2991
self.addCleanup(store.branch.lock_write().unlock)
2992
section = store.get_mutable_section(None)
2993
section.set('foo', 'bar')
2995
modified_store = self.get_store(self)
2996
sections = list(modified_store.get_sections())
2997
self.assertLength(1, sections)
2998
self.assertSectionContent((None, {'foo': 'bar'}), sections[0])
3000
def test_set_option_in_named_section(self):
3001
store = self.get_store(self)
3002
store._load_from_string('')
3003
# FIXME: There should be a better way than relying on the test
3004
# parametrization to identify branch.conf -- vila 2011-0526
3005
if self.store_id in ('branch', 'remote_branch'):
3006
# branch stores requires write locked branches
3007
self.addCleanup(store.branch.lock_write().unlock)
3008
section = store.get_mutable_section('baz')
3009
section.set('foo', 'bar')
3011
modified_store = self.get_store(self)
3012
sections = list(modified_store.get_sections())
3013
self.assertLength(1, sections)
3014
self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
3016
def test_load_hook(self):
3017
# First, we need to ensure that the store exists
3018
store = self.get_store(self)
3019
# FIXME: There should be a better way than relying on the test
3020
# parametrization to identify branch.conf -- vila 2011-0526
3021
if self.store_id in ('branch', 'remote_branch'):
3022
# branch stores requires write locked branches
3023
self.addCleanup(store.branch.lock_write().unlock)
3024
section = store.get_mutable_section('baz')
3025
section.set('foo', 'bar')
3027
# Now we can try to load it
3028
store = self.get_store(self)
3032
config.ConfigHooks.install_named_hook('load', hook, None)
3033
self.assertLength(0, calls)
3035
self.assertLength(1, calls)
3036
self.assertEquals((store,), calls[0])
3038
def test_save_hook(self):
3042
config.ConfigHooks.install_named_hook('save', hook, None)
3043
self.assertLength(0, calls)
3044
store = self.get_store(self)
3045
# FIXME: There should be a better way than relying on the test
3046
# parametrization to identify branch.conf -- vila 2011-0526
3047
if self.store_id in ('branch', 'remote_branch'):
3048
# branch stores requires write locked branches
3049
self.addCleanup(store.branch.lock_write().unlock)
3050
section = store.get_mutable_section('baz')
3051
section.set('foo', 'bar')
3053
self.assertLength(1, calls)
3054
self.assertEquals((store,), calls[0])
3056
def test_set_mark_dirty(self):
3057
stack = config.MemoryStack('')
3058
self.assertLength(0, stack.store.dirty_sections)
3059
stack.set('foo', 'baz')
3060
self.assertLength(1, stack.store.dirty_sections)
3061
self.assertTrue(stack.store._need_saving())
3063
def test_remove_mark_dirty(self):
3064
stack = config.MemoryStack('foo=bar')
3065
self.assertLength(0, stack.store.dirty_sections)
3067
self.assertLength(1, stack.store.dirty_sections)
3068
self.assertTrue(stack.store._need_saving())
3071
class TestStoreSaveChanges(tests.TestCaseWithTransport):
3072
"""Tests that config changes are kept in memory and saved on-demand."""
3075
super(TestStoreSaveChanges, self).setUp()
3076
self.transport = self.get_transport()
3077
# Most of the tests involve two stores pointing to the same persistent
3078
# storage to observe the effects of concurrent changes
3079
self.st1 = config.TransportIniFileStore(self.transport, 'foo.conf')
3080
self.st2 = config.TransportIniFileStore(self.transport, 'foo.conf')
3083
self.warnings.append(args[0] % args[1:])
3084
self.overrideAttr(trace, 'warning', warning)
3086
def has_store(self, store):
3087
store_basename = urlutils.relative_url(self.transport.external_url(),
3088
store.external_url())
3089
return self.transport.has(store_basename)
3091
def get_stack(self, store):
3092
# Any stack will do as long as it uses the right store, just a single
3093
# no-name section is enough
3094
return config.Stack([store.get_sections], store)
3096
def test_no_changes_no_save(self):
3097
s = self.get_stack(self.st1)
3098
s.store.save_changes()
3099
self.assertEquals(False, self.has_store(self.st1))
3101
def test_unrelated_concurrent_update(self):
3102
s1 = self.get_stack(self.st1)
3103
s2 = self.get_stack(self.st2)
3104
s1.set('foo', 'bar')
3105
s2.set('baz', 'quux')
3107
# Changes don't propagate magically
3108
self.assertEquals(None, s1.get('baz'))
3109
s2.store.save_changes()
3110
self.assertEquals('quux', s2.get('baz'))
3111
# Changes are acquired when saving
3112
self.assertEquals('bar', s2.get('foo'))
3113
# Since there is no overlap, no warnings are emitted
3114
self.assertLength(0, self.warnings)
3116
def test_concurrent_update_modified(self):
3117
s1 = self.get_stack(self.st1)
3118
s2 = self.get_stack(self.st2)
3119
s1.set('foo', 'bar')
3120
s2.set('foo', 'baz')
3123
s2.store.save_changes()
3124
self.assertEquals('baz', s2.get('foo'))
3125
# But the user get a warning
3126
self.assertLength(1, self.warnings)
3127
warning = self.warnings[0]
3128
self.assertStartsWith(warning, 'Option foo in section None')
3129
self.assertEndsWith(warning, 'was changed from <CREATED> to bar.'
3130
' The baz value will be saved.')
3132
def test_concurrent_deletion(self):
3133
self.st1._load_from_string('foo=bar')
3135
s1 = self.get_stack(self.st1)
3136
s2 = self.get_stack(self.st2)
3139
s1.store.save_changes()
3141
self.assertLength(0, self.warnings)
3142
s2.store.save_changes()
3144
self.assertLength(1, self.warnings)
3145
warning = self.warnings[0]
3146
self.assertStartsWith(warning, 'Option foo in section None')
3147
self.assertEndsWith(warning, 'was changed from bar to <CREATED>.'
3148
' The <DELETED> value will be saved.')
3151
class TestQuotingIniFileStore(tests.TestCaseWithTransport):
3153
def get_store(self):
3154
return config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3156
def test_get_quoted_string(self):
3157
store = self.get_store()
3158
store._load_from_string('foo= " abc "')
3159
stack = config.Stack([store.get_sections])
3160
self.assertEquals(' abc ', stack.get('foo'))
3162
def test_set_quoted_string(self):
3163
store = self.get_store()
3164
stack = config.Stack([store.get_sections], store)
3165
stack.set('foo', ' a b c ')
3167
self.assertFileEqual('foo = " a b c "' + os.linesep, 'foo.conf')
3170
class TestTransportIniFileStore(TestStore):
3172
def test_loading_unknown_file_fails(self):
3173
store = config.TransportIniFileStore(self.get_transport(),
3175
self.assertRaises(errors.NoSuchFile, store.load)
3177
def test_invalid_content(self):
3178
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3179
self.assertEquals(False, store.is_loaded())
3180
exc = self.assertRaises(
3181
errors.ParseConfigError, store._load_from_string,
3182
'this is invalid !')
3183
self.assertEndsWith(exc.filename, 'foo.conf')
3184
# And the load failed
3185
self.assertEquals(False, store.is_loaded())
3187
def test_get_embedded_sections(self):
3188
# A more complicated example (which also shows that section names and
3189
# option names share the same name space...)
3190
# FIXME: This should be fixed by forbidding dicts as values ?
3191
# -- vila 2011-04-05
3192
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3193
store._load_from_string('''
3197
foo_in_DEFAULT=foo_DEFAULT
3205
sections = list(store.get_sections())
3206
self.assertLength(4, sections)
3207
# The default section has no name.
3208
# List values are provided as strings and need to be explicitly
3209
# converted by specifying from_unicode=list_from_store at option
3211
self.assertSectionContent((None, {'foo': 'bar', 'l': u'1,2'}),
3213
self.assertSectionContent(
3214
('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
3215
self.assertSectionContent(
3216
('bar', {'foo_in_bar': 'barbar'}), sections[2])
3217
# sub sections are provided as embedded dicts.
3218
self.assertSectionContent(
3219
('baz', {'foo_in_baz': 'barbaz', 'qux': {'foo_in_qux': 'quux'}}),
3223
class TestLockableIniFileStore(TestStore):
3225
def test_create_store_in_created_dir(self):
3226
self.assertPathDoesNotExist('dir')
3227
t = self.get_transport('dir/subdir')
3228
store = config.LockableIniFileStore(t, 'foo.conf')
3229
store.get_mutable_section(None).set('foo', 'bar')
3231
self.assertPathExists('dir/subdir')
3234
class TestConcurrentStoreUpdates(TestStore):
3235
"""Test that Stores properly handle conccurent updates.
3237
New Store implementation may fail some of these tests but until such
3238
implementations exist it's hard to properly filter them from the scenarios
3239
applied here. If you encounter such a case, contact the bzr devs.
3242
scenarios = [(key, {'get_stack': builder}) for key, builder
3243
in config.test_stack_builder_registry.iteritems()]
3246
super(TestConcurrentStoreUpdates, self).setUp()
3247
self.stack = self.get_stack(self)
3248
if not isinstance(self.stack, config._CompatibleStack):
3249
raise tests.TestNotApplicable(
3250
'%s is not meant to be compatible with the old config design'
3252
self.stack.set('one', '1')
3253
self.stack.set('two', '2')
3255
self.stack.store.save()
3257
def test_simple_read_access(self):
3258
self.assertEquals('1', self.stack.get('one'))
3260
def test_simple_write_access(self):
3261
self.stack.set('one', 'one')
3262
self.assertEquals('one', self.stack.get('one'))
3264
def test_listen_to_the_last_speaker(self):
3266
c2 = self.get_stack(self)
3267
c1.set('one', 'ONE')
3268
c2.set('two', 'TWO')
3269
self.assertEquals('ONE', c1.get('one'))
3270
self.assertEquals('TWO', c2.get('two'))
3271
# The second update respect the first one
3272
self.assertEquals('ONE', c2.get('one'))
3274
def test_last_speaker_wins(self):
3275
# If the same config is not shared, the same variable modified twice
3276
# can only see a single result.
3278
c2 = self.get_stack(self)
3281
self.assertEquals('c2', c2.get('one'))
3282
# The first modification is still available until another refresh
3284
self.assertEquals('c1', c1.get('one'))
3285
c1.set('two', 'done')
3286
self.assertEquals('c2', c1.get('one'))
3288
def test_writes_are_serialized(self):
3290
c2 = self.get_stack(self)
3292
# We spawn a thread that will pause *during* the config saving.
3293
before_writing = threading.Event()
3294
after_writing = threading.Event()
3295
writing_done = threading.Event()
3296
c1_save_without_locking_orig = c1.store.save_without_locking
3297
def c1_save_without_locking():
3298
before_writing.set()
3299
c1_save_without_locking_orig()
3300
# The lock is held. We wait for the main thread to decide when to
3302
after_writing.wait()
3303
c1.store.save_without_locking = c1_save_without_locking
3307
t1 = threading.Thread(target=c1_set)
3308
# Collect the thread after the test
3309
self.addCleanup(t1.join)
3310
# Be ready to unblock the thread if the test goes wrong
3311
self.addCleanup(after_writing.set)
3313
before_writing.wait()
3314
self.assertRaises(errors.LockContention,
3315
c2.set, 'one', 'c2')
3316
self.assertEquals('c1', c1.get('one'))
3317
# Let the lock be released
3321
self.assertEquals('c2', c2.get('one'))
3323
def test_read_while_writing(self):
3325
# We spawn a thread that will pause *during* the write
3326
ready_to_write = threading.Event()
3327
do_writing = threading.Event()
3328
writing_done = threading.Event()
3329
# We override the _save implementation so we know the store is locked
3330
c1_save_without_locking_orig = c1.store.save_without_locking
3331
def c1_save_without_locking():
3332
ready_to_write.set()
3333
# The lock is held. We wait for the main thread to decide when to
3336
c1_save_without_locking_orig()
3338
c1.store.save_without_locking = c1_save_without_locking
3341
t1 = threading.Thread(target=c1_set)
3342
# Collect the thread after the test
3343
self.addCleanup(t1.join)
3344
# Be ready to unblock the thread if the test goes wrong
3345
self.addCleanup(do_writing.set)
3347
# Ensure the thread is ready to write
3348
ready_to_write.wait()
3349
self.assertEquals('c1', c1.get('one'))
3350
# If we read during the write, we get the old value
3351
c2 = self.get_stack(self)
3352
self.assertEquals('1', c2.get('one'))
3353
# Let the writing occur and ensure it occurred
3356
# Now we get the updated value
3357
c3 = self.get_stack(self)
3358
self.assertEquals('c1', c3.get('one'))
3360
# FIXME: It may be worth looking into removing the lock dir when it's not
3361
# needed anymore and look at possible fallouts for concurrent lockers. This
3362
# will matter if/when we use config files outside of bazaar directories
3363
# (.bazaar or .bzr) -- vila 20110-04-111
3366
class TestSectionMatcher(TestStore):
3368
scenarios = [('location', {'matcher': config.LocationMatcher}),
3369
('id', {'matcher': config.NameMatcher}),]
3372
super(TestSectionMatcher, self).setUp()
3373
# Any simple store is good enough
3374
self.get_store = config.test_store_builder_registry.get('configobj')
3376
def test_no_matches_for_empty_stores(self):
3377
store = self.get_store(self)
3378
store._load_from_string('')
3379
matcher = self.matcher(store, '/bar')
3380
self.assertEquals([], list(matcher.get_sections()))
3382
def test_build_doesnt_load_store(self):
3383
store = self.get_store(self)
3384
self.matcher(store, '/bar')
3385
self.assertFalse(store.is_loaded())
3388
class TestLocationSection(tests.TestCase):
3390
def get_section(self, options, extra_path):
3391
section = config.Section('foo', options)
3392
return config.LocationSection(section, extra_path)
3394
def test_simple_option(self):
3395
section = self.get_section({'foo': 'bar'}, '')
3396
self.assertEquals('bar', section.get('foo'))
3398
def test_option_with_extra_path(self):
3399
section = self.get_section({'foo': 'bar', 'foo:policy': 'appendpath'},
3401
self.assertEquals('bar/baz', section.get('foo'))
3403
def test_invalid_policy(self):
3404
section = self.get_section({'foo': 'bar', 'foo:policy': 'die'},
3406
# invalid policies are ignored
3407
self.assertEquals('bar', section.get('foo'))
3410
class TestLocationMatcher(TestStore):
3413
super(TestLocationMatcher, self).setUp()
3414
# Any simple store is good enough
3415
self.get_store = config.test_store_builder_registry.get('configobj')
3417
def test_unrelated_section_excluded(self):
3418
store = self.get_store(self)
3419
store._load_from_string('''
3427
section=/foo/bar/baz
3431
self.assertEquals(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
3433
[section.id for _, section in store.get_sections()])
3434
matcher = config.LocationMatcher(store, '/foo/bar/quux')
3435
sections = [section for _, section in matcher.get_sections()]
3436
self.assertEquals(['/foo/bar', '/foo'],
3437
[section.id for section in sections])
3438
self.assertEquals(['quux', 'bar/quux'],
3439
[section.extra_path for section in sections])
3441
def test_more_specific_sections_first(self):
3442
store = self.get_store(self)
3443
store._load_from_string('''
3449
self.assertEquals(['/foo', '/foo/bar'],
3450
[section.id for _, section in store.get_sections()])
3451
matcher = config.LocationMatcher(store, '/foo/bar/baz')
3452
sections = [section for _, section in matcher.get_sections()]
3453
self.assertEquals(['/foo/bar', '/foo'],
3454
[section.id for section in sections])
3455
self.assertEquals(['baz', 'bar/baz'],
3456
[section.extra_path for section in sections])
3458
def test_appendpath_in_no_name_section(self):
3459
# It's a bit weird to allow appendpath in a no-name section, but
3460
# someone may found a use for it
3461
store = self.get_store(self)
3462
store._load_from_string('''
3464
foo:policy = appendpath
3466
matcher = config.LocationMatcher(store, 'dir/subdir')
3467
sections = list(matcher.get_sections())
3468
self.assertLength(1, sections)
3469
self.assertEquals('bar/dir/subdir', sections[0][1].get('foo'))
3471
def test_file_urls_are_normalized(self):
3472
store = self.get_store(self)
3473
if sys.platform == 'win32':
3474
expected_url = 'file:///C:/dir/subdir'
3475
expected_location = 'C:/dir/subdir'
3477
expected_url = 'file:///dir/subdir'
3478
expected_location = '/dir/subdir'
3479
matcher = config.LocationMatcher(store, expected_url)
3480
self.assertEquals(expected_location, matcher.location)
3482
def test_branch_name_colo(self):
3483
store = self.get_store(self)
3484
store._load_from_string(dedent("""\
3486
push_location=my{branchname}
3488
matcher = config.LocationMatcher(store, 'file:///,branch=example%3c')
3489
self.assertEqual('example<', matcher.branch_name)
3490
((_, section),) = matcher.get_sections()
3491
self.assertEqual('example<', section.locals['branchname'])
3493
def test_branch_name_basename(self):
3494
store = self.get_store(self)
3495
store._load_from_string(dedent("""\
3497
push_location=my{branchname}
3499
matcher = config.LocationMatcher(store, 'file:///parent/example%3c')
3500
self.assertEqual('example<', matcher.branch_name)
3501
((_, section),) = matcher.get_sections()
3502
self.assertEqual('example<', section.locals['branchname'])
3505
class TestStartingPathMatcher(TestStore):
3508
super(TestStartingPathMatcher, self).setUp()
3509
# Any simple store is good enough
3510
self.store = config.IniFileStore()
3512
def assertSectionIDs(self, expected, location, content):
3513
self.store._load_from_string(content)
3514
matcher = config.StartingPathMatcher(self.store, location)
3515
sections = list(matcher.get_sections())
3516
self.assertLength(len(expected), sections)
3517
self.assertEqual(expected, [section.id for _, section in sections])
3520
def test_empty(self):
3521
self.assertSectionIDs([], self.get_url(), '')
3523
def test_url_vs_local_paths(self):
3524
# The matcher location is an url and the section names are local paths
3525
self.assertSectionIDs(['/foo/bar', '/foo'],
3526
'file:///foo/bar/baz', '''\
3531
def test_local_path_vs_url(self):
3532
# The matcher location is a local path and the section names are urls
3533
self.assertSectionIDs(['file:///foo/bar', 'file:///foo'],
3534
'/foo/bar/baz', '''\
3540
def test_no_name_section_included_when_present(self):
3541
# Note that other tests will cover the case where the no-name section
3542
# is empty and as such, not included.
3543
sections = self.assertSectionIDs(['/foo/bar', '/foo', None],
3544
'/foo/bar/baz', '''\
3545
option = defined so the no-name section exists
3549
self.assertEquals(['baz', 'bar/baz', '/foo/bar/baz'],
3550
[s.locals['relpath'] for _, s in sections])
3552
def test_order_reversed(self):
3553
self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
3558
def test_unrelated_section_excluded(self):
3559
self.assertSectionIDs(['/foo/bar', '/foo'], '/foo/bar/baz', '''\
3565
def test_glob_included(self):
3566
sections = self.assertSectionIDs(['/foo/*/baz', '/foo/b*', '/foo'],
3567
'/foo/bar/baz', '''\
3573
# Note that 'baz' as a relpath for /foo/b* is not fully correct, but
3574
# nothing really is... as far using {relpath} to append it to something
3575
# else, this seems good enough though.
3576
self.assertEquals(['', 'baz', 'bar/baz'],
3577
[s.locals['relpath'] for _, s in sections])
3579
def test_respect_order(self):
3580
self.assertSectionIDs(['/foo', '/foo/b*', '/foo/*/baz'],
3581
'/foo/bar/baz', '''\
3589
class TestNameMatcher(TestStore):
3592
super(TestNameMatcher, self).setUp()
3593
self.matcher = config.NameMatcher
3594
# Any simple store is good enough
3595
self.get_store = config.test_store_builder_registry.get('configobj')
3597
def get_matching_sections(self, name):
3598
store = self.get_store(self)
3599
store._load_from_string('''
3607
matcher = self.matcher(store, name)
3608
return list(matcher.get_sections())
3610
def test_matching(self):
3611
sections = self.get_matching_sections('foo')
3612
self.assertLength(1, sections)
3613
self.assertSectionContent(('foo', {'option': 'foo'}), sections[0])
3615
def test_not_matching(self):
3616
sections = self.get_matching_sections('baz')
3617
self.assertLength(0, sections)
3620
class TestBaseStackGet(tests.TestCase):
3623
super(TestBaseStackGet, self).setUp()
3624
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3626
def test_get_first_definition(self):
3627
store1 = config.IniFileStore()
3628
store1._load_from_string('foo=bar')
3629
store2 = config.IniFileStore()
3630
store2._load_from_string('foo=baz')
3631
conf = config.Stack([store1.get_sections, store2.get_sections])
3632
self.assertEquals('bar', conf.get('foo'))
3634
def test_get_with_registered_default_value(self):
3635
config.option_registry.register(config.Option('foo', default='bar'))
3636
conf_stack = config.Stack([])
3637
self.assertEquals('bar', conf_stack.get('foo'))
3639
def test_get_without_registered_default_value(self):
3640
config.option_registry.register(config.Option('foo'))
3641
conf_stack = config.Stack([])
3642
self.assertEquals(None, conf_stack.get('foo'))
3644
def test_get_without_default_value_for_not_registered(self):
3645
conf_stack = config.Stack([])
3646
self.assertEquals(None, conf_stack.get('foo'))
3648
def test_get_for_empty_section_callable(self):
3649
conf_stack = config.Stack([lambda : []])
3650
self.assertEquals(None, conf_stack.get('foo'))
3652
def test_get_for_broken_callable(self):
3653
# Trying to use and invalid callable raises an exception on first use
3654
conf_stack = config.Stack([object])
3655
self.assertRaises(TypeError, conf_stack.get, 'foo')
3658
class TestStackWithSimpleStore(tests.TestCase):
3661
super(TestStackWithSimpleStore, self).setUp()
3662
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3663
self.registry = config.option_registry
3665
def get_conf(self, content=None):
3666
return config.MemoryStack(content)
3668
def test_override_value_from_env(self):
3669
self.overrideEnv('FOO', None)
3670
self.registry.register(
3671
config.Option('foo', default='bar', override_from_env=['FOO']))
3672
self.overrideEnv('FOO', 'quux')
3673
# Env variable provides a default taking over the option one
3674
conf = self.get_conf('foo=store')
3675
self.assertEquals('quux', conf.get('foo'))
3677
def test_first_override_value_from_env_wins(self):
3678
self.overrideEnv('NO_VALUE', None)
3679
self.overrideEnv('FOO', None)
3680
self.overrideEnv('BAZ', None)
3681
self.registry.register(
3682
config.Option('foo', default='bar',
3683
override_from_env=['NO_VALUE', 'FOO', 'BAZ']))
3684
self.overrideEnv('FOO', 'foo')
3685
self.overrideEnv('BAZ', 'baz')
3686
# The first env var set wins
3687
conf = self.get_conf('foo=store')
3688
self.assertEquals('foo', conf.get('foo'))
3691
class TestMemoryStack(tests.TestCase):
3694
conf = config.MemoryStack('foo=bar')
3695
self.assertEquals('bar', conf.get('foo'))
3698
conf = config.MemoryStack('foo=bar')
3699
conf.set('foo', 'baz')
3700
self.assertEquals('baz', conf.get('foo'))
3702
def test_no_content(self):
3703
conf = config.MemoryStack()
3704
# No content means no loading
3705
self.assertFalse(conf.store.is_loaded())
3706
self.assertRaises(NotImplementedError, conf.get, 'foo')
3707
# But a content can still be provided
3708
conf.store._load_from_string('foo=bar')
3709
self.assertEquals('bar', conf.get('foo'))
3712
class TestStackIterSections(tests.TestCase):
3714
def test_empty_stack(self):
3715
conf = config.Stack([])
3716
sections = list(conf.iter_sections())
3717
self.assertLength(0, sections)
3719
def test_empty_store(self):
3720
store = config.IniFileStore()
3721
store._load_from_string('')
3722
conf = config.Stack([store.get_sections])
3723
sections = list(conf.iter_sections())
3724
self.assertLength(0, sections)
3726
def test_simple_store(self):
3727
store = config.IniFileStore()
3728
store._load_from_string('foo=bar')
3729
conf = config.Stack([store.get_sections])
3730
tuples = list(conf.iter_sections())
3731
self.assertLength(1, tuples)
3732
(found_store, found_section) = tuples[0]
3733
self.assertIs(store, found_store)
3735
def test_two_stores(self):
3736
store1 = config.IniFileStore()
3737
store1._load_from_string('foo=bar')
3738
store2 = config.IniFileStore()
3739
store2._load_from_string('bar=qux')
3740
conf = config.Stack([store1.get_sections, store2.get_sections])
3741
tuples = list(conf.iter_sections())
3742
self.assertLength(2, tuples)
3743
self.assertIs(store1, tuples[0][0])
3744
self.assertIs(store2, tuples[1][0])
3747
class TestStackWithTransport(tests.TestCaseWithTransport):
3749
scenarios = [(key, {'get_stack': builder}) for key, builder
3750
in config.test_stack_builder_registry.iteritems()]
3753
class TestConcreteStacks(TestStackWithTransport):
3755
def test_build_stack(self):
3756
# Just a smoke test to help debug builders
3757
self.get_stack(self)
3760
class TestStackGet(TestStackWithTransport):
3763
super(TestStackGet, self).setUp()
3764
self.conf = self.get_stack(self)
3766
def test_get_for_empty_stack(self):
3767
self.assertEquals(None, self.conf.get('foo'))
3769
def test_get_hook(self):
3770
self.conf.set('foo', 'bar')
3774
config.ConfigHooks.install_named_hook('get', hook, None)
3775
self.assertLength(0, calls)
3776
value = self.conf.get('foo')
3777
self.assertEquals('bar', value)
3778
self.assertLength(1, calls)
3779
self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
3782
class TestStackGetWithConverter(tests.TestCase):
3785
super(TestStackGetWithConverter, self).setUp()
3786
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3787
self.registry = config.option_registry
3789
def get_conf(self, content=None):
3790
return config.MemoryStack(content)
3792
def register_bool_option(self, name, default=None, default_from_env=None):
3793
b = config.Option(name, help='A boolean.',
3794
default=default, default_from_env=default_from_env,
3795
from_unicode=config.bool_from_store)
3796
self.registry.register(b)
3798
def test_get_default_bool_None(self):
3799
self.register_bool_option('foo')
3800
conf = self.get_conf('')
3801
self.assertEquals(None, conf.get('foo'))
3803
def test_get_default_bool_True(self):
3804
self.register_bool_option('foo', u'True')
3805
conf = self.get_conf('')
3806
self.assertEquals(True, conf.get('foo'))
3808
def test_get_default_bool_False(self):
3809
self.register_bool_option('foo', False)
3810
conf = self.get_conf('')
3811
self.assertEquals(False, conf.get('foo'))
3813
def test_get_default_bool_False_as_string(self):
3814
self.register_bool_option('foo', u'False')
3815
conf = self.get_conf('')
3816
self.assertEquals(False, conf.get('foo'))
3818
def test_get_default_bool_from_env_converted(self):
3819
self.register_bool_option('foo', u'True', default_from_env=['FOO'])
3820
self.overrideEnv('FOO', 'False')
3821
conf = self.get_conf('')
3822
self.assertEquals(False, conf.get('foo'))
3824
def test_get_default_bool_when_conversion_fails(self):
3825
self.register_bool_option('foo', default='True')
3826
conf = self.get_conf('foo=invalid boolean')
3827
self.assertEquals(True, conf.get('foo'))
3829
def register_integer_option(self, name,
3830
default=None, default_from_env=None):
3831
i = config.Option(name, help='An integer.',
3832
default=default, default_from_env=default_from_env,
3833
from_unicode=config.int_from_store)
3834
self.registry.register(i)
3836
def test_get_default_integer_None(self):
3837
self.register_integer_option('foo')
3838
conf = self.get_conf('')
3839
self.assertEquals(None, conf.get('foo'))
3841
def test_get_default_integer(self):
3842
self.register_integer_option('foo', 42)
3843
conf = self.get_conf('')
3844
self.assertEquals(42, conf.get('foo'))
3846
def test_get_default_integer_as_string(self):
3847
self.register_integer_option('foo', u'42')
3848
conf = self.get_conf('')
3849
self.assertEquals(42, conf.get('foo'))
3851
def test_get_default_integer_from_env(self):
3852
self.register_integer_option('foo', default_from_env=['FOO'])
3853
self.overrideEnv('FOO', '18')
3854
conf = self.get_conf('')
3855
self.assertEquals(18, conf.get('foo'))
3857
def test_get_default_integer_when_conversion_fails(self):
3858
self.register_integer_option('foo', default='12')
3859
conf = self.get_conf('foo=invalid integer')
3860
self.assertEquals(12, conf.get('foo'))
3862
def register_list_option(self, name, default=None, default_from_env=None):
3863
l = config.ListOption(name, help='A list.', default=default,
3864
default_from_env=default_from_env)
3865
self.registry.register(l)
3867
def test_get_default_list_None(self):
3868
self.register_list_option('foo')
3869
conf = self.get_conf('')
3870
self.assertEquals(None, conf.get('foo'))
3872
def test_get_default_list_empty(self):
3873
self.register_list_option('foo', '')
3874
conf = self.get_conf('')
3875
self.assertEquals([], conf.get('foo'))
3877
def test_get_default_list_from_env(self):
3878
self.register_list_option('foo', default_from_env=['FOO'])
3879
self.overrideEnv('FOO', '')
3880
conf = self.get_conf('')
3881
self.assertEquals([], conf.get('foo'))
3883
def test_get_with_list_converter_no_item(self):
3884
self.register_list_option('foo', None)
3885
conf = self.get_conf('foo=,')
3886
self.assertEquals([], conf.get('foo'))
3888
def test_get_with_list_converter_many_items(self):
3889
self.register_list_option('foo', None)
3890
conf = self.get_conf('foo=m,o,r,e')
3891
self.assertEquals(['m', 'o', 'r', 'e'], conf.get('foo'))
3893
def test_get_with_list_converter_embedded_spaces_many_items(self):
3894
self.register_list_option('foo', None)
3895
conf = self.get_conf('foo=" bar", "baz "')
3896
self.assertEquals([' bar', 'baz '], conf.get('foo'))
3898
def test_get_with_list_converter_stripped_spaces_many_items(self):
3899
self.register_list_option('foo', None)
3900
conf = self.get_conf('foo= bar , baz ')
3901
self.assertEquals(['bar', 'baz'], conf.get('foo'))
3904
class TestIterOptionRefs(tests.TestCase):
3905
"""iter_option_refs is a bit unusual, document some cases."""
3907
def assertRefs(self, expected, string):
3908
self.assertEquals(expected, list(config.iter_option_refs(string)))
3910
def test_empty(self):
3911
self.assertRefs([(False, '')], '')
3913
def test_no_refs(self):
3914
self.assertRefs([(False, 'foo bar')], 'foo bar')
3916
def test_single_ref(self):
3917
self.assertRefs([(False, ''), (True, '{foo}'), (False, '')], '{foo}')
3919
def test_broken_ref(self):
3920
self.assertRefs([(False, '{foo')], '{foo')
3922
def test_embedded_ref(self):
3923
self.assertRefs([(False, '{'), (True, '{foo}'), (False, '}')],
3926
def test_two_refs(self):
3927
self.assertRefs([(False, ''), (True, '{foo}'),
3928
(False, ''), (True, '{bar}'),
3932
def test_newline_in_refs_are_not_matched(self):
3933
self.assertRefs([(False, '{\nxx}{xx\n}{{\n}}')], '{\nxx}{xx\n}{{\n}}')
3936
class TestStackExpandOptions(tests.TestCaseWithTransport):
3939
super(TestStackExpandOptions, self).setUp()
3940
self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3941
self.registry = config.option_registry
3942
store = config.TransportIniFileStore(self.get_transport(), 'foo.conf')
3943
self.conf = config.Stack([store.get_sections], store)
3945
def assertExpansion(self, expected, string, env=None):
3946
self.assertEquals(expected, self.conf.expand_options(string, env))
3948
def test_no_expansion(self):
3949
self.assertExpansion('foo', 'foo')
3951
def test_expand_default_value(self):
3952
self.conf.store._load_from_string('bar=baz')
3953
self.registry.register(config.Option('foo', default=u'{bar}'))
3954
self.assertEquals('baz', self.conf.get('foo', expand=True))
3956
def test_expand_default_from_env(self):
3957
self.conf.store._load_from_string('bar=baz')
3958
self.registry.register(config.Option('foo', default_from_env=['FOO']))
3959
self.overrideEnv('FOO', '{bar}')
3960
self.assertEquals('baz', self.conf.get('foo', expand=True))
3962
def test_expand_default_on_failed_conversion(self):
3963
self.conf.store._load_from_string('baz=bogus\nbar=42\nfoo={baz}')
3964
self.registry.register(
3965
config.Option('foo', default=u'{bar}',
3966
from_unicode=config.int_from_store))
3967
self.assertEquals(42, self.conf.get('foo', expand=True))
3969
def test_env_adding_options(self):
3970
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3972
def test_env_overriding_options(self):
3973
self.conf.store._load_from_string('foo=baz')
3974
self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3976
def test_simple_ref(self):
3977
self.conf.store._load_from_string('foo=xxx')
3978
self.assertExpansion('xxx', '{foo}')
3980
def test_unknown_ref(self):
3981
self.assertRaises(errors.ExpandingUnknownOption,
3982
self.conf.expand_options, '{foo}')
3984
def test_illegal_def_is_ignored(self):
3985
self.assertExpansion('{1,2}', '{1,2}')
3986
self.assertExpansion('{ }', '{ }')
3987
self.assertExpansion('${Foo,f}', '${Foo,f}')
3989
def test_indirect_ref(self):
3990
self.conf.store._load_from_string('''
3994
self.assertExpansion('xxx', '{bar}')
3996
def test_embedded_ref(self):
3997
self.conf.store._load_from_string('''
4001
self.assertExpansion('xxx', '{{bar}}')
4003
def test_simple_loop(self):
4004
self.conf.store._load_from_string('foo={foo}')
4005
self.assertRaises(errors.OptionExpansionLoop,
4006
self.conf.expand_options, '{foo}')
4008
def test_indirect_loop(self):
4009
self.conf.store._load_from_string('''
4013
e = self.assertRaises(errors.OptionExpansionLoop,
4014
self.conf.expand_options, '{foo}')
4015
self.assertEquals('foo->bar->baz', e.refs)
4016
self.assertEquals('{foo}', e.string)
4018
def test_list(self):
4019
self.conf.store._load_from_string('''
4023
list={foo},{bar},{baz}
4025
self.registry.register(
4026
config.ListOption('list'))
4027
self.assertEquals(['start', 'middle', 'end'],
4028
self.conf.get('list', expand=True))
4030
def test_cascading_list(self):
4031
self.conf.store._load_from_string('''
4037
self.registry.register(config.ListOption('list'))
4038
# Register an intermediate option as a list to ensure no conversion
4039
# happen while expanding. Conversion should only occur for the original
4040
# option ('list' here).
4041
self.registry.register(config.ListOption('baz'))
4042
self.assertEquals(['start', 'middle', 'end'],
4043
self.conf.get('list', expand=True))
4045
def test_pathologically_hidden_list(self):
4046
self.conf.store._load_from_string('''
4052
hidden={start}{middle}{end}
4054
# What matters is what the registration says, the conversion happens
4055
# only after all expansions have been performed
4056
self.registry.register(config.ListOption('hidden'))
4057
self.assertEquals(['bin', 'go'],
4058
self.conf.get('hidden', expand=True))
4061
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
4064
super(TestStackCrossSectionsExpand, self).setUp()
4066
def get_config(self, location, string):
4069
# Since we don't save the config we won't strictly require to inherit
4070
# from TestCaseInTempDir, but an error occurs so quickly...
4071
c = config.LocationStack(location)
4072
c.store._load_from_string(string)
4075
def test_dont_cross_unrelated_section(self):
4076
c = self.get_config('/another/branch/path','''
4081
[/another/branch/path]
4084
self.assertRaises(errors.ExpandingUnknownOption,
4085
c.get, 'bar', expand=True)
4087
def test_cross_related_sections(self):
4088
c = self.get_config('/project/branch/path','''
4092
[/project/branch/path]
4095
self.assertEquals('quux', c.get('bar', expand=True))
4098
class TestStackCrossStoresExpand(tests.TestCaseWithTransport):
4100
def test_cross_global_locations(self):
4101
l_store = config.LocationStore()
4102
l_store._load_from_string('''
4108
g_store = config.GlobalStore()
4109
g_store._load_from_string('''
4115
stack = config.LocationStack('/branch')
4116
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
4117
self.assertEquals('loc-foo', stack.get('gfoo', expand=True))
4120
class TestStackExpandSectionLocals(tests.TestCaseWithTransport):
4122
def test_expand_locals_empty(self):
4123
l_store = config.LocationStore()
4124
l_store._load_from_string('''
4125
[/home/user/project]
4130
stack = config.LocationStack('/home/user/project/')
4131
self.assertEquals('', stack.get('base', expand=True))
4132
self.assertEquals('', stack.get('rel', expand=True))
4134
def test_expand_basename_locally(self):
4135
l_store = config.LocationStore()
4136
l_store._load_from_string('''
4137
[/home/user/project]
4141
stack = config.LocationStack('/home/user/project/branch')
4142
self.assertEquals('branch', stack.get('bfoo', expand=True))
4144
def test_expand_basename_locally_longer_path(self):
4145
l_store = config.LocationStore()
4146
l_store._load_from_string('''
4151
stack = config.LocationStack('/home/user/project/dir/branch')
4152
self.assertEquals('branch', stack.get('bfoo', expand=True))
4154
def test_expand_relpath_locally(self):
4155
l_store = config.LocationStore()
4156
l_store._load_from_string('''
4157
[/home/user/project]
4158
lfoo = loc-foo/{relpath}
4161
stack = config.LocationStack('/home/user/project/branch')
4162
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
4164
def test_expand_relpath_unknonw_in_global(self):
4165
g_store = config.GlobalStore()
4166
g_store._load_from_string('''
4171
stack = config.LocationStack('/home/user/project/branch')
4172
self.assertRaises(errors.ExpandingUnknownOption,
4173
stack.get, 'gfoo', expand=True)
4175
def test_expand_local_option_locally(self):
4176
l_store = config.LocationStore()
4177
l_store._load_from_string('''
4178
[/home/user/project]
4179
lfoo = loc-foo/{relpath}
4183
g_store = config.GlobalStore()
4184
g_store._load_from_string('''
4190
stack = config.LocationStack('/home/user/project/branch')
4191
self.assertEquals('glob-bar', stack.get('lbar', expand=True))
4192
self.assertEquals('loc-foo/branch', stack.get('gfoo', expand=True))
4194
def test_locals_dont_leak(self):
4195
"""Make sure we chose the right local in presence of several sections.
4197
l_store = config.LocationStore()
4198
l_store._load_from_string('''
4200
lfoo = loc-foo/{relpath}
4201
[/home/user/project]
4202
lfoo = loc-foo/{relpath}
4205
stack = config.LocationStack('/home/user/project/branch')
4206
self.assertEquals('loc-foo/branch', stack.get('lfoo', expand=True))
4207
stack = config.LocationStack('/home/user/bar/baz')
4208
self.assertEquals('loc-foo/bar/baz', stack.get('lfoo', expand=True))
4212
class TestStackSet(TestStackWithTransport):
4214
def test_simple_set(self):
4215
conf = self.get_stack(self)
4216
self.assertEquals(None, conf.get('foo'))
4217
conf.set('foo', 'baz')
4218
# Did we get it back ?
4219
self.assertEquals('baz', conf.get('foo'))
4221
def test_set_creates_a_new_section(self):
4222
conf = self.get_stack(self)
4223
conf.set('foo', 'baz')
4224
self.assertEquals, 'baz', conf.get('foo')
4226
def test_set_hook(self):
4230
config.ConfigHooks.install_named_hook('set', hook, None)
4231
self.assertLength(0, calls)
4232
conf = self.get_stack(self)
4233
conf.set('foo', 'bar')
4234
self.assertLength(1, calls)
4235
self.assertEquals((conf, 'foo', 'bar'), calls[0])
4238
class TestStackRemove(TestStackWithTransport):
4240
def test_remove_existing(self):
4241
conf = self.get_stack(self)
4242
conf.set('foo', 'bar')
4243
self.assertEquals('bar', conf.get('foo'))
4245
# Did we get it back ?
4246
self.assertEquals(None, conf.get('foo'))
4248
def test_remove_unknown(self):
4249
conf = self.get_stack(self)
4250
self.assertRaises(KeyError, conf.remove, 'I_do_not_exist')
4252
def test_remove_hook(self):
4256
config.ConfigHooks.install_named_hook('remove', hook, None)
4257
self.assertLength(0, calls)
4258
conf = self.get_stack(self)
4259
conf.set('foo', 'bar')
4261
self.assertLength(1, calls)
4262
self.assertEquals((conf, 'foo'), calls[0])
4265
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
4268
super(TestConfigGetOptions, self).setUp()
4269
create_configs(self)
4271
def test_no_variable(self):
4272
# Using branch should query branch, locations and bazaar
4273
self.assertOptions([], self.branch_config)
4275
def test_option_in_bazaar(self):
4276
self.bazaar_config.set_user_option('file', 'bazaar')
4277
self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
4280
def test_option_in_locations(self):
4281
self.locations_config.set_user_option('file', 'locations')
4283
[('file', 'locations', self.tree.basedir, 'locations')],
4284
self.locations_config)
4286
def test_option_in_branch(self):
4287
self.branch_config.set_user_option('file', 'branch')
4288
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
4291
def test_option_in_bazaar_and_branch(self):
4292
self.bazaar_config.set_user_option('file', 'bazaar')
4293
self.branch_config.set_user_option('file', 'branch')
4294
self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
4295
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4298
def test_option_in_branch_and_locations(self):
4299
# Hmm, locations override branch :-/
4300
self.locations_config.set_user_option('file', 'locations')
4301
self.branch_config.set_user_option('file', 'branch')
4303
[('file', 'locations', self.tree.basedir, 'locations'),
4304
('file', 'branch', 'DEFAULT', 'branch'),],
4307
def test_option_in_bazaar_locations_and_branch(self):
4308
self.bazaar_config.set_user_option('file', 'bazaar')
4309
self.locations_config.set_user_option('file', 'locations')
4310
self.branch_config.set_user_option('file', 'branch')
4312
[('file', 'locations', self.tree.basedir, 'locations'),
4313
('file', 'branch', 'DEFAULT', 'branch'),
4314
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4318
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
4321
super(TestConfigRemoveOption, self).setUp()
4322
create_configs_with_file_option(self)
4324
def test_remove_in_locations(self):
4325
self.locations_config.remove_user_option('file', self.tree.basedir)
4327
[('file', 'branch', 'DEFAULT', 'branch'),
4328
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4331
def test_remove_in_branch(self):
4332
self.branch_config.remove_user_option('file')
4334
[('file', 'locations', self.tree.basedir, 'locations'),
4335
('file', 'bazaar', 'DEFAULT', 'bazaar'),],
4338
def test_remove_in_bazaar(self):
4339
self.bazaar_config.remove_user_option('file')
4341
[('file', 'locations', self.tree.basedir, 'locations'),
4342
('file', 'branch', 'DEFAULT', 'branch'),],
4346
class TestConfigGetSections(tests.TestCaseWithTransport):
4349
super(TestConfigGetSections, self).setUp()
4350
create_configs(self)
4352
def assertSectionNames(self, expected, conf, name=None):
4353
"""Check which sections are returned for a given config.
4355
If fallback configurations exist their sections can be included.
4357
:param expected: A list of section names.
4359
:param conf: The configuration that will be queried.
4361
:param name: An optional section name that will be passed to
4364
sections = list(conf._get_sections(name))
4365
self.assertLength(len(expected), sections)
4366
self.assertEqual(expected, [n for n, _, _ in sections])
4368
def test_bazaar_default_section(self):
4369
self.assertSectionNames(['DEFAULT'], self.bazaar_config)
4371
def test_locations_default_section(self):
4372
# No sections are defined in an empty file
4373
self.assertSectionNames([], self.locations_config)
4375
def test_locations_named_section(self):
4376
self.locations_config.set_user_option('file', 'locations')
4377
self.assertSectionNames([self.tree.basedir], self.locations_config)
4379
def test_locations_matching_sections(self):
4380
loc_config = self.locations_config
4381
loc_config.set_user_option('file', 'locations')
4382
# We need to cheat a bit here to create an option in sections above and
4383
# below the 'location' one.
4384
parser = loc_config._get_parser()
4385
# locations.cong deals with '/' ignoring native os.sep
4386
location_names = self.tree.basedir.split('/')
4387
parent = '/'.join(location_names[:-1])
4388
child = '/'.join(location_names + ['child'])
4390
parser[parent]['file'] = 'parent'
4392
parser[child]['file'] = 'child'
4393
self.assertSectionNames([self.tree.basedir, parent], loc_config)
4395
def test_branch_data_default_section(self):
4396
self.assertSectionNames([None],
4397
self.branch_config._get_branch_data_config())
4399
def test_branch_default_sections(self):
4400
# No sections are defined in an empty locations file
4401
self.assertSectionNames([None, 'DEFAULT'],
4403
# Unless we define an option
4404
self.branch_config._get_location_config().set_user_option(
4405
'file', 'locations')
4406
self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
4409
def test_bazaar_named_section(self):
4410
# We need to cheat as the API doesn't give direct access to sections
4411
# other than DEFAULT.
4412
self.bazaar_config.set_alias('bazaar', 'bzr')
4413
self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
4416
class TestSharedStores(tests.TestCaseInTempDir):
4418
def test_bazaar_conf_shared(self):
4419
g1 = config.GlobalStack()
4420
g2 = config.GlobalStack()
4421
# The two stacks share the same store
4422
self.assertIs(g1.store, g2.store)
4425
class TestAuthenticationConfigFile(tests.TestCase):
4426
"""Test the authentication.conf file matching"""
4428
def _got_user_passwd(self, expected_user, expected_password,
4429
config, *args, **kwargs):
4430
credentials = config.get_credentials(*args, **kwargs)
4431
if credentials is None:
4435
user = credentials['user']
4436
password = credentials['password']
4437
self.assertEquals(expected_user, user)
4438
self.assertEquals(expected_password, password)
4440
def test_empty_config(self):
4441
conf = config.AuthenticationConfig(_file=StringIO())
4442
self.assertEquals({}, conf._get_config())
4443
self._got_user_passwd(None, None, conf, 'http', 'foo.net')
4445
def test_non_utf8_config(self):
4446
conf = config.AuthenticationConfig(_file=StringIO(
4448
self.assertRaises(errors.ConfigContentError, conf._get_config)
4450
def test_missing_auth_section_header(self):
4451
conf = config.AuthenticationConfig(_file=StringIO('foo = bar'))
4452
self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
4454
def test_auth_section_header_not_closed(self):
4455
conf = config.AuthenticationConfig(_file=StringIO('[DEF'))
4456
self.assertRaises(errors.ParseConfigError, conf._get_config)
4458
def test_auth_value_not_boolean(self):
4459
conf = config.AuthenticationConfig(_file=StringIO(
4463
verify_certificates=askme # Error: Not a boolean
4465
self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
4467
def test_auth_value_not_int(self):
4468
conf = config.AuthenticationConfig(_file=StringIO(
4472
port=port # Error: Not an int
4474
self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
4476
def test_unknown_password_encoding(self):
4477
conf = config.AuthenticationConfig(_file=StringIO(
4481
password_encoding=unknown
4483
self.assertRaises(ValueError, conf.get_password,
4484
'ftp', 'foo.net', 'joe')
4486
def test_credentials_for_scheme_host(self):
4487
conf = config.AuthenticationConfig(_file=StringIO(
4488
"""# Identity on foo.net
4493
password=secret-pass
4496
self._got_user_passwd('joe', 'secret-pass', conf, 'ftp', 'foo.net')
4498
self._got_user_passwd(None, None, conf, 'http', 'foo.net')
4500
self._got_user_passwd(None, None, conf, 'ftp', 'bar.net')
4502
def test_credentials_for_host_port(self):
4503
conf = config.AuthenticationConfig(_file=StringIO(
4504
"""# Identity on foo.net
4510
password=secret-pass
4513
self._got_user_passwd('joe', 'secret-pass',
4514
conf, 'ftp', 'foo.net', port=10021)
4516
self._got_user_passwd(None, None, conf, 'ftp', 'foo.net')
4518
def test_for_matching_host(self):
4519
conf = config.AuthenticationConfig(_file=StringIO(
4520
"""# Identity on foo.net
4526
[sourceforge domain]
4533
self._got_user_passwd('georges', 'bendover',
4534
conf, 'bzr', 'foo.bzr.sf.net')
4536
self._got_user_passwd(None, None,
4537
conf, 'bzr', 'bbzr.sf.net')
4539
def test_for_matching_host_None(self):
4540
conf = config.AuthenticationConfig(_file=StringIO(
4541
"""# Identity on foo.net
4551
self._got_user_passwd('joe', 'joepass',
4552
conf, 'bzr', 'quux.net')
4553
# no host but different scheme
4554
self._got_user_passwd('georges', 'bendover',
4555
conf, 'ftp', 'quux.net')
4557
def test_credentials_for_path(self):
4558
conf = config.AuthenticationConfig(_file=StringIO(
4574
self._got_user_passwd(None, None,
4575
conf, 'http', host='bar.org', path='/dir3')
4577
self._got_user_passwd('georges', 'bendover',
4578
conf, 'http', host='bar.org', path='/dir2')
4580
self._got_user_passwd('jim', 'jimpass',
4581
conf, 'http', host='bar.org',path='/dir1/subdir')
4583
def test_credentials_for_user(self):
4584
conf = config.AuthenticationConfig(_file=StringIO(
4593
self._got_user_passwd('jim', 'jimpass',
4594
conf, 'http', 'bar.org')
4596
self._got_user_passwd('jim', 'jimpass',
4597
conf, 'http', 'bar.org', user='jim')
4598
# Don't get a different user if one is specified
4599
self._got_user_passwd(None, None,
4600
conf, 'http', 'bar.org', user='georges')
4602
def test_credentials_for_user_without_password(self):
4603
conf = config.AuthenticationConfig(_file=StringIO(
4610
# Get user but no password
4611
self._got_user_passwd('jim', None,
4612
conf, 'http', 'bar.org')
4614
def test_verify_certificates(self):
4615
conf = config.AuthenticationConfig(_file=StringIO(
4622
verify_certificates=False
4629
credentials = conf.get_credentials('https', 'bar.org')
4630
self.assertEquals(False, credentials.get('verify_certificates'))
4631
credentials = conf.get_credentials('https', 'foo.net')
4632
self.assertEquals(True, credentials.get('verify_certificates'))
4635
class TestAuthenticationStorage(tests.TestCaseInTempDir):
4637
def test_set_credentials(self):
4638
conf = config.AuthenticationConfig()
4639
conf.set_credentials('name', 'host', 'user', 'scheme', 'password',
4640
99, path='/foo', verify_certificates=False, realm='realm')
4641
credentials = conf.get_credentials(host='host', scheme='scheme',
4642
port=99, path='/foo',
4644
CREDENTIALS = {'name': 'name', 'user': 'user', 'password': 'password',
4645
'verify_certificates': False, 'scheme': 'scheme',
4646
'host': 'host', 'port': 99, 'path': '/foo',
4648
self.assertEqual(CREDENTIALS, credentials)
4649
credentials_from_disk = config.AuthenticationConfig().get_credentials(
4650
host='host', scheme='scheme', port=99, path='/foo', realm='realm')
4651
self.assertEqual(CREDENTIALS, credentials_from_disk)
4653
def test_reset_credentials_different_name(self):
4654
conf = config.AuthenticationConfig()
4655
conf.set_credentials('name', 'host', 'user', 'scheme', 'password'),
4656
conf.set_credentials('name2', 'host', 'user2', 'scheme', 'password'),
4657
self.assertIs(None, conf._get_config().get('name'))
4658
credentials = conf.get_credentials(host='host', scheme='scheme')
4659
CREDENTIALS = {'name': 'name2', 'user': 'user2', 'password':
4660
'password', 'verify_certificates': True,
4661
'scheme': 'scheme', 'host': 'host', 'port': None,
4662
'path': None, 'realm': None}
4663
self.assertEqual(CREDENTIALS, credentials)
4666
class TestAuthenticationConfig(tests.TestCase):
4667
"""Test AuthenticationConfig behaviour"""
4669
def _check_default_password_prompt(self, expected_prompt_format, scheme,
4670
host=None, port=None, realm=None,
4674
user, password = 'jim', 'precious'
4675
expected_prompt = expected_prompt_format % {
4676
'scheme': scheme, 'host': host, 'port': port,
4677
'user': user, 'realm': realm}
4679
stdout = tests.StringIOWrapper()
4680
stderr = tests.StringIOWrapper()
4681
ui.ui_factory = tests.TestUIFactory(stdin=password + '\n',
4682
stdout=stdout, stderr=stderr)
4683
# We use an empty conf so that the user is always prompted
4684
conf = config.AuthenticationConfig()
4685
self.assertEquals(password,
4686
conf.get_password(scheme, host, user, port=port,
4687
realm=realm, path=path))
4688
self.assertEquals(expected_prompt, stderr.getvalue())
4689
self.assertEquals('', stdout.getvalue())
4691
def _check_default_username_prompt(self, expected_prompt_format, scheme,
4692
host=None, port=None, realm=None,
4697
expected_prompt = expected_prompt_format % {
4698
'scheme': scheme, 'host': host, 'port': port,
4700
stdout = tests.StringIOWrapper()
4701
stderr = tests.StringIOWrapper()
4702
ui.ui_factory = tests.TestUIFactory(stdin=username+ '\n',
4703
stdout=stdout, stderr=stderr)
4704
# We use an empty conf so that the user is always prompted
4705
conf = config.AuthenticationConfig()
4706
self.assertEquals(username, conf.get_user(scheme, host, port=port,
4707
realm=realm, path=path, ask=True))
4708
self.assertEquals(expected_prompt, stderr.getvalue())
4709
self.assertEquals('', stdout.getvalue())
4711
def test_username_defaults_prompts(self):
4712
# HTTP prompts can't be tested here, see test_http.py
4713
self._check_default_username_prompt(u'FTP %(host)s username: ', 'ftp')
4714
self._check_default_username_prompt(
4715
u'FTP %(host)s:%(port)d username: ', 'ftp', port=10020)
4716
self._check_default_username_prompt(
4717
u'SSH %(host)s:%(port)d username: ', 'ssh', port=12345)
4719
def test_username_default_no_prompt(self):
4720
conf = config.AuthenticationConfig()
4721
self.assertEquals(None,
4722
conf.get_user('ftp', 'example.com'))
4723
self.assertEquals("explicitdefault",
4724
conf.get_user('ftp', 'example.com', default="explicitdefault"))
4726
def test_password_default_prompts(self):
4727
# HTTP prompts can't be tested here, see test_http.py
4728
self._check_default_password_prompt(
4729
u'FTP %(user)s@%(host)s password: ', 'ftp')
4730
self._check_default_password_prompt(
4731
u'FTP %(user)s@%(host)s:%(port)d password: ', 'ftp', port=10020)
4732
self._check_default_password_prompt(
4733
u'SSH %(user)s@%(host)s:%(port)d password: ', 'ssh', port=12345)
4734
# SMTP port handling is a bit special (it's handled if embedded in the
4736
# FIXME: should we: forbid that, extend it to other schemes, leave
4737
# things as they are that's fine thank you ?
4738
self._check_default_password_prompt(
4739
u'SMTP %(user)s@%(host)s password: ', 'smtp')
4740
self._check_default_password_prompt(
4741
u'SMTP %(user)s@%(host)s password: ', 'smtp', host='bar.org:10025')
4742
self._check_default_password_prompt(
4743
u'SMTP %(user)s@%(host)s:%(port)d password: ', 'smtp', port=10025)
4745
def test_ssh_password_emits_warning(self):
4746
conf = config.AuthenticationConfig(_file=StringIO(
4754
entered_password = 'typed-by-hand'
4755
stdout = tests.StringIOWrapper()
4756
stderr = tests.StringIOWrapper()
4757
ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
4758
stdout=stdout, stderr=stderr)
4760
# Since the password defined in the authentication config is ignored,
4761
# the user is prompted
4762
self.assertEquals(entered_password,
4763
conf.get_password('ssh', 'bar.org', user='jim'))
4764
self.assertContainsRe(
4766
'password ignored in section \[ssh with password\]')
4768
def test_ssh_without_password_doesnt_emit_warning(self):
4769
conf = config.AuthenticationConfig(_file=StringIO(
4776
entered_password = 'typed-by-hand'
4777
stdout = tests.StringIOWrapper()
4778
stderr = tests.StringIOWrapper()
4779
ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
4783
# Since the password defined in the authentication config is ignored,
4784
# the user is prompted
4785
self.assertEquals(entered_password,
4786
conf.get_password('ssh', 'bar.org', user='jim'))
4787
# No warning shoud be emitted since there is no password. We are only
4789
self.assertNotContainsRe(
4791
'password ignored in section \[ssh with password\]')
4793
def test_uses_fallback_stores(self):
4794
self.overrideAttr(config, 'credential_store_registry',
4795
config.CredentialStoreRegistry())
4796
store = StubCredentialStore()
4797
store.add_credentials("http", "example.com", "joe", "secret")
4798
config.credential_store_registry.register("stub", store, fallback=True)
4799
conf = config.AuthenticationConfig(_file=StringIO())
4800
creds = conf.get_credentials("http", "example.com")
4801
self.assertEquals("joe", creds["user"])
4802
self.assertEquals("secret", creds["password"])
4805
class StubCredentialStore(config.CredentialStore):
4811
def add_credentials(self, scheme, host, user, password=None):
4812
self._username[(scheme, host)] = user
4813
self._password[(scheme, host)] = password
4815
def get_credentials(self, scheme, host, port=None, user=None,
4816
path=None, realm=None):
4817
key = (scheme, host)
4818
if not key in self._username:
4820
return { "scheme": scheme, "host": host, "port": port,
4821
"user": self._username[key], "password": self._password[key]}
4824
class CountingCredentialStore(config.CredentialStore):
4829
def get_credentials(self, scheme, host, port=None, user=None,
4830
path=None, realm=None):
4835
class TestCredentialStoreRegistry(tests.TestCase):
4837
def _get_cs_registry(self):
4838
return config.credential_store_registry
4840
def test_default_credential_store(self):
4841
r = self._get_cs_registry()
4842
default = r.get_credential_store(None)
4843
self.assertIsInstance(default, config.PlainTextCredentialStore)
4845
def test_unknown_credential_store(self):
4846
r = self._get_cs_registry()
4847
# It's hard to imagine someone creating a credential store named
4848
# 'unknown' so we use that as an never registered key.
4849
self.assertRaises(KeyError, r.get_credential_store, 'unknown')
4851
def test_fallback_none_registered(self):
4852
r = config.CredentialStoreRegistry()
4853
self.assertEquals(None,
4854
r.get_fallback_credentials("http", "example.com"))
4856
def test_register(self):
4857
r = config.CredentialStoreRegistry()
4858
r.register("stub", StubCredentialStore(), fallback=False)
4859
r.register("another", StubCredentialStore(), fallback=True)
4860
self.assertEquals(["another", "stub"], r.keys())
4862
def test_register_lazy(self):
4863
r = config.CredentialStoreRegistry()
4864
r.register_lazy("stub", "bzrlib.tests.test_config",
4865
"StubCredentialStore", fallback=False)
4866
self.assertEquals(["stub"], r.keys())
4867
self.assertIsInstance(r.get_credential_store("stub"),
4868
StubCredentialStore)
4870
def test_is_fallback(self):
4871
r = config.CredentialStoreRegistry()
4872
r.register("stub1", None, fallback=False)
4873
r.register("stub2", None, fallback=True)
4874
self.assertEquals(False, r.is_fallback("stub1"))
4875
self.assertEquals(True, r.is_fallback("stub2"))
4877
def test_no_fallback(self):
4878
r = config.CredentialStoreRegistry()
4879
store = CountingCredentialStore()
4880
r.register("count", store, fallback=False)
4881
self.assertEquals(None,
4882
r.get_fallback_credentials("http", "example.com"))
4883
self.assertEquals(0, store._calls)
4885
def test_fallback_credentials(self):
4886
r = config.CredentialStoreRegistry()
4887
store = StubCredentialStore()
4888
store.add_credentials("http", "example.com",
4889
"somebody", "geheim")
4890
r.register("stub", store, fallback=True)
4891
creds = r.get_fallback_credentials("http", "example.com")
4892
self.assertEquals("somebody", creds["user"])
4893
self.assertEquals("geheim", creds["password"])
4895
def test_fallback_first_wins(self):
4896
r = config.CredentialStoreRegistry()
4897
stub1 = StubCredentialStore()
4898
stub1.add_credentials("http", "example.com",
4899
"somebody", "stub1")
4900
r.register("stub1", stub1, fallback=True)
4901
stub2 = StubCredentialStore()
4902
stub2.add_credentials("http", "example.com",
4903
"somebody", "stub2")
4904
r.register("stub2", stub1, fallback=True)
4905
creds = r.get_fallback_credentials("http", "example.com")
4906
self.assertEquals("somebody", creds["user"])
4907
self.assertEquals("stub1", creds["password"])
4910
class TestPlainTextCredentialStore(tests.TestCase):
4912
def test_decode_password(self):
4913
r = config.credential_store_registry
4914
plain_text = r.get_credential_store()
4915
decoded = plain_text.decode_password(dict(password='secret'))
4916
self.assertEquals('secret', decoded)
4919
class TestBase64CredentialStore(tests.TestCase):
4921
def test_decode_password(self):
4922
r = config.credential_store_registry
4923
plain_text = r.get_credential_store('base64')
4924
decoded = plain_text.decode_password(dict(password='c2VjcmV0'))
4925
self.assertEquals('secret', decoded)
4928
# FIXME: Once we have a way to declare authentication to all test servers, we
4929
# can implement generic tests.
4930
# test_user_password_in_url
4931
# test_user_in_url_password_from_config
4932
# test_user_in_url_password_prompted
4933
# test_user_in_config
4934
# test_user_getpass.getuser
4935
# test_user_prompted ?
4936
class TestAuthenticationRing(tests.TestCaseWithTransport):
4940
class TestAutoUserId(tests.TestCase):
4941
"""Test inferring an automatic user name."""
4943
def test_auto_user_id(self):
4944
"""Automatic inference of user name.
4946
This is a bit hard to test in an isolated way, because it depends on
4947
system functions that go direct to /etc or perhaps somewhere else.
4948
But it's reasonable to say that on Unix, with an /etc/mailname, we ought
4949
to be able to choose a user name with no configuration.
4951
if sys.platform == 'win32':
4952
raise tests.TestSkipped(
4953
"User name inference not implemented on win32")
4954
realname, address = config._auto_user_id()
4955
if os.path.exists('/etc/mailname'):
4956
self.assertIsNot(None, realname)
4957
self.assertIsNot(None, address)
4959
self.assertEquals((None, None), (realname, address))
4962
class TestDefaultMailDomain(tests.TestCaseInTempDir):
4963
"""Test retrieving default domain from mailname file"""
4965
def test_default_mail_domain_simple(self):
4966
f = file('simple', 'w')
4968
f.write("domainname.com\n")
4971
r = config._get_default_mail_domain('simple')
4972
self.assertEquals('domainname.com', r)
4974
def test_default_mail_domain_no_eol(self):
4975
f = file('no_eol', 'w')
4977
f.write("domainname.com")
4980
r = config._get_default_mail_domain('no_eol')
4981
self.assertEquals('domainname.com', r)
4983
def test_default_mail_domain_multiple_lines(self):
4984
f = file('multiple_lines', 'w')
4986
f.write("domainname.com\nsome other text\n")
4989
r = config._get_default_mail_domain('multiple_lines')
4990
self.assertEquals('domainname.com', r)
4993
class EmailOptionTests(tests.TestCase):
4995
def test_default_email_uses_BZR_EMAIL(self):
4996
conf = config.MemoryStack('email=jelmer@debian.org')
4997
# BZR_EMAIL takes precedence over EMAIL
4998
self.overrideEnv('BZR_EMAIL', 'jelmer@samba.org')
4999
self.overrideEnv('EMAIL', 'jelmer@apache.org')
5000
self.assertEquals('jelmer@samba.org', conf.get('email'))
5002
def test_default_email_uses_EMAIL(self):
5003
conf = config.MemoryStack('')
5004
self.overrideEnv('BZR_EMAIL', None)
5005
self.overrideEnv('EMAIL', 'jelmer@apache.org')
5006
self.assertEquals('jelmer@apache.org', conf.get('email'))
5008
def test_BZR_EMAIL_overrides(self):
5009
conf = config.MemoryStack('email=jelmer@debian.org')
5010
self.overrideEnv('BZR_EMAIL', 'jelmer@apache.org')
5011
self.assertEquals('jelmer@apache.org', conf.get('email'))
5012
self.overrideEnv('BZR_EMAIL', None)
5013
self.overrideEnv('EMAIL', 'jelmer@samba.org')
5014
self.assertEquals('jelmer@debian.org', conf.get('email'))
5017
class MailClientOptionTests(tests.TestCase):
5019
def test_default(self):
5020
conf = config.MemoryStack('')
5021
client = conf.get('mail_client')
5022
self.assertIs(client, mail_client.DefaultMail)
5024
def test_evolution(self):
5025
conf = config.MemoryStack('mail_client=evolution')
5026
client = conf.get('mail_client')
5027
self.assertIs(client, mail_client.Evolution)
5029
def test_kmail(self):
5030
conf = config.MemoryStack('mail_client=kmail')
5031
client = conf.get('mail_client')
5032
self.assertIs(client, mail_client.KMail)
5034
def test_mutt(self):
5035
conf = config.MemoryStack('mail_client=mutt')
5036
client = conf.get('mail_client')
5037
self.assertIs(client, mail_client.Mutt)
5039
def test_thunderbird(self):
5040
conf = config.MemoryStack('mail_client=thunderbird')
5041
client = conf.get('mail_client')
5042
self.assertIs(client, mail_client.Thunderbird)
5044
def test_explicit_default(self):
5045
conf = config.MemoryStack('mail_client=default')
5046
client = conf.get('mail_client')
5047
self.assertIs(client, mail_client.DefaultMail)
5049
def test_editor(self):
5050
conf = config.MemoryStack('mail_client=editor')
5051
client = conf.get('mail_client')
5052
self.assertIs(client, mail_client.Editor)
5054
def test_mapi(self):
5055
conf = config.MemoryStack('mail_client=mapi')
5056
client = conf.get('mail_client')
5057
self.assertIs(client, mail_client.MAPIClient)
5059
def test_xdg_email(self):
5060
conf = config.MemoryStack('mail_client=xdg-email')
5061
client = conf.get('mail_client')
5062
self.assertIs(client, mail_client.XDGEmail)
5064
def test_unknown(self):
5065
conf = config.MemoryStack('mail_client=firebird')
5066
self.assertRaises(errors.ConfigOptionValueError, conf.get,