~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_config.py

(gz) Backslash escape selftest output when printing to non-unicode consoles
 (Martin [gz])

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2008, 2009 Canonical Ltd
 
1
# Copyright (C) 2005-2010 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
19
19
from cStringIO import StringIO
20
20
import os
21
21
import sys
 
22
import threading
 
23
 
 
24
 
 
25
from testtools import matchers
22
26
 
23
27
#import bzrlib specific imports here
24
28
from bzrlib import (
35
39
    trace,
36
40
    transport,
37
41
    )
 
42
from bzrlib.tests import (
 
43
    features,
 
44
    scenarios,
 
45
    )
38
46
from bzrlib.util.configobj import configobj
39
47
 
40
48
 
 
49
def lockable_config_scenarios():
 
50
    return [
 
51
        ('global',
 
52
         {'config_class': config.GlobalConfig,
 
53
          'config_args': [],
 
54
          'config_section': 'DEFAULT'}),
 
55
        ('locations',
 
56
         {'config_class': config.LocationConfig,
 
57
          'config_args': ['.'],
 
58
          'config_section': '.'}),]
 
59
 
 
60
 
 
61
load_tests = scenarios.load_tests_apply_scenarios
 
62
 
 
63
 
41
64
sample_long_alias="log -r-15..-1 --line"
42
65
sample_config_text = u"""
43
66
[DEFAULT]
105
128
"""
106
129
 
107
130
 
 
131
def create_configs(test):
 
132
    """Create configuration files for a given test.
 
133
 
 
134
    This requires creating a tree (and populate the ``test.tree`` attribute)
 
135
    and its associated branch and will populate the following attributes:
 
136
 
 
137
    - branch_config: A BranchConfig for the associated branch.
 
138
 
 
139
    - locations_config : A LocationConfig for the associated branch
 
140
 
 
141
    - bazaar_config: A GlobalConfig.
 
142
 
 
143
    The tree and branch are created in a 'tree' subdirectory so the tests can
 
144
    still use the test directory to stay outside of the branch.
 
145
    """
 
146
    tree = test.make_branch_and_tree('tree')
 
147
    test.tree = tree
 
148
    test.branch_config = config.BranchConfig(tree.branch)
 
149
    test.locations_config = config.LocationConfig(tree.basedir)
 
150
    test.bazaar_config = config.GlobalConfig()
 
151
 
 
152
 
 
153
def create_configs_with_file_option(test):
 
154
    """Create configuration files with a ``file`` option set in each.
 
155
 
 
156
    This builds on ``create_configs`` and add one ``file`` option in each
 
157
    configuration with a value which allows identifying the configuration file.
 
158
    """
 
159
    create_configs(test)
 
160
    test.bazaar_config.set_user_option('file', 'bazaar')
 
161
    test.locations_config.set_user_option('file', 'locations')
 
162
    test.branch_config.set_user_option('file', 'branch')
 
163
 
 
164
 
 
165
class TestOptionsMixin:
 
166
 
 
167
    def assertOptions(self, expected, conf):
 
168
        # We don't care about the parser (as it will make tests hard to write
 
169
        # and error-prone anyway)
 
170
        self.assertThat([opt[:4] for opt in conf._get_options()],
 
171
                        matchers.Equals(expected))
 
172
 
 
173
 
108
174
class InstrumentedConfigObj(object):
109
175
    """A config obj look-enough-alike to record calls made to it."""
110
176
 
129
195
        self._calls.append(('keys',))
130
196
        return []
131
197
 
 
198
    def reload(self):
 
199
        self._calls.append(('reload',))
 
200
 
132
201
    def write(self, arg):
133
202
        self._calls.append(('write',))
134
203
 
350
419
        self.assertEqual(config.config_filename(),
351
420
                         self.bzr_home + '/bazaar.conf')
352
421
 
353
 
    def test_branches_config_filename(self):
354
 
        self.assertEqual(config.branches_config_filename(),
355
 
                         self.bzr_home + '/branches.conf')
356
 
 
357
422
    def test_locations_config_filename(self):
358
423
        self.assertEqual(config.locations_config_filename(),
359
424
                         self.bzr_home + '/locations.conf')
367
432
            '/home/bogus/.cache')
368
433
 
369
434
 
370
 
class TestIniConfig(tests.TestCase):
 
435
class TestIniConfig(tests.TestCaseInTempDir):
371
436
 
372
437
    def make_config_parser(self, s):
373
 
        conf = config.IniBasedConfig(None)
374
 
        parser = conf._get_parser(file=StringIO(s.encode('utf-8')))
375
 
        return conf, parser
 
438
        conf = config.IniBasedConfig.from_string(s)
 
439
        return conf, conf._get_parser()
376
440
 
377
441
 
378
442
class TestIniConfigBuilding(TestIniConfig):
379
443
 
380
444
    def test_contructs(self):
381
 
        my_config = config.IniBasedConfig("nothing")
 
445
        my_config = config.IniBasedConfig()
382
446
 
383
447
    def test_from_fp(self):
384
 
        config_file = StringIO(sample_config_text.encode('utf-8'))
385
 
        my_config = config.IniBasedConfig(None)
386
 
        self.failUnless(
387
 
            isinstance(my_config._get_parser(file=config_file),
388
 
                        configobj.ConfigObj))
 
448
        my_config = config.IniBasedConfig.from_string(sample_config_text)
 
449
        self.assertIsInstance(my_config._get_parser(), configobj.ConfigObj)
389
450
 
390
451
    def test_cached(self):
391
 
        config_file = StringIO(sample_config_text.encode('utf-8'))
392
 
        my_config = config.IniBasedConfig(None)
393
 
        parser = my_config._get_parser(file=config_file)
 
452
        my_config = config.IniBasedConfig.from_string(sample_config_text)
 
453
        parser = my_config._get_parser()
394
454
        self.failUnless(my_config._get_parser() is parser)
395
455
 
 
456
    def _dummy_chown(self, path, uid, gid):
 
457
        self.path, self.uid, self.gid = path, uid, gid
 
458
 
 
459
    def test_ini_config_ownership(self):
 
460
        """Ensure that chown is happening during _write_config_file"""
 
461
        self.requireFeature(features.chown_feature)
 
462
        self.overrideAttr(os, 'chown', self._dummy_chown)
 
463
        self.path = self.uid = self.gid = None
 
464
        conf = config.IniBasedConfig(file_name='./foo.conf')
 
465
        conf._write_config_file()
 
466
        self.assertEquals(self.path, './foo.conf')
 
467
        self.assertTrue(isinstance(self.uid, int))
 
468
        self.assertTrue(isinstance(self.gid, int))
 
469
 
 
470
    def test_get_filename_parameter_is_deprecated_(self):
 
471
        conf = self.callDeprecated([
 
472
            'IniBasedConfig.__init__(get_filename) was deprecated in 2.3.'
 
473
            ' Use file_name instead.'],
 
474
            config.IniBasedConfig, lambda: 'ini.conf')
 
475
        self.assertEqual('ini.conf', conf.file_name)
 
476
 
 
477
    def test_get_parser_file_parameter_is_deprecated_(self):
 
478
        config_file = StringIO(sample_config_text.encode('utf-8'))
 
479
        conf = config.IniBasedConfig.from_string(sample_config_text)
 
480
        conf = self.callDeprecated([
 
481
            'IniBasedConfig._get_parser(file=xxx) was deprecated in 2.3.'
 
482
            ' Use IniBasedConfig(_content=xxx) instead.'],
 
483
            conf._get_parser, file=config_file)
 
484
 
 
485
class TestIniConfigSaving(tests.TestCaseInTempDir):
 
486
 
 
487
    def test_cant_save_without_a_file_name(self):
 
488
        conf = config.IniBasedConfig()
 
489
        self.assertRaises(AssertionError, conf._write_config_file)
 
490
 
 
491
    def test_saved_with_content(self):
 
492
        content = 'foo = bar\n'
 
493
        conf = config.IniBasedConfig.from_string(
 
494
            content, file_name='./test.conf', save=True)
 
495
        self.assertFileEqual(content, 'test.conf')
 
496
 
 
497
 
 
498
class TestIniBaseConfigOnDisk(tests.TestCaseInTempDir):
 
499
 
 
500
    def test_cannot_reload_without_name(self):
 
501
        conf = config.IniBasedConfig.from_string(sample_config_text)
 
502
        self.assertRaises(AssertionError, conf.reload)
 
503
 
 
504
    def test_reload_see_new_value(self):
 
505
        c1 = config.IniBasedConfig.from_string('editor=vim\n',
 
506
                                               file_name='./test/conf')
 
507
        c1._write_config_file()
 
508
        c2 = config.IniBasedConfig.from_string('editor=emacs\n',
 
509
                                               file_name='./test/conf')
 
510
        c2._write_config_file()
 
511
        self.assertEqual('vim', c1.get_user_option('editor'))
 
512
        self.assertEqual('emacs', c2.get_user_option('editor'))
 
513
        # Make sure we get the Right value
 
514
        c1.reload()
 
515
        self.assertEqual('emacs', c1.get_user_option('editor'))
 
516
 
 
517
 
 
518
class TestLockableConfig(tests.TestCaseInTempDir):
 
519
 
 
520
    scenarios = lockable_config_scenarios()
 
521
 
 
522
    # Set by load_tests
 
523
    config_class = None
 
524
    config_args = None
 
525
    config_section = None
 
526
 
 
527
    def setUp(self):
 
528
        super(TestLockableConfig, self).setUp()
 
529
        self._content = '[%s]\none=1\ntwo=2\n' % (self.config_section,)
 
530
        self.config = self.create_config(self._content)
 
531
 
 
532
    def get_existing_config(self):
 
533
        return self.config_class(*self.config_args)
 
534
 
 
535
    def create_config(self, content):
 
536
        kwargs = dict(save=True)
 
537
        c = self.config_class.from_string(content, *self.config_args, **kwargs)
 
538
        return c
 
539
 
 
540
    def test_simple_read_access(self):
 
541
        self.assertEquals('1', self.config.get_user_option('one'))
 
542
 
 
543
    def test_simple_write_access(self):
 
544
        self.config.set_user_option('one', 'one')
 
545
        self.assertEquals('one', self.config.get_user_option('one'))
 
546
 
 
547
    def test_listen_to_the_last_speaker(self):
 
548
        c1 = self.config
 
549
        c2 = self.get_existing_config()
 
550
        c1.set_user_option('one', 'ONE')
 
551
        c2.set_user_option('two', 'TWO')
 
552
        self.assertEquals('ONE', c1.get_user_option('one'))
 
553
        self.assertEquals('TWO', c2.get_user_option('two'))
 
554
        # The second update respect the first one
 
555
        self.assertEquals('ONE', c2.get_user_option('one'))
 
556
 
 
557
    def test_last_speaker_wins(self):
 
558
        # If the same config is not shared, the same variable modified twice
 
559
        # can only see a single result.
 
560
        c1 = self.config
 
561
        c2 = self.get_existing_config()
 
562
        c1.set_user_option('one', 'c1')
 
563
        c2.set_user_option('one', 'c2')
 
564
        self.assertEquals('c2', c2._get_user_option('one'))
 
565
        # The first modification is still available until another refresh
 
566
        # occur
 
567
        self.assertEquals('c1', c1._get_user_option('one'))
 
568
        c1.set_user_option('two', 'done')
 
569
        self.assertEquals('c2', c1._get_user_option('one'))
 
570
 
 
571
    def test_writes_are_serialized(self):
 
572
        c1 = self.config
 
573
        c2 = self.get_existing_config()
 
574
 
 
575
        # We spawn a thread that will pause *during* the write
 
576
        before_writing = threading.Event()
 
577
        after_writing = threading.Event()
 
578
        writing_done = threading.Event()
 
579
        c1_orig = c1._write_config_file
 
580
        def c1_write_config_file():
 
581
            before_writing.set()
 
582
            c1_orig()
 
583
            # The lock is held we wait for the main thread to decide when to
 
584
            # continue
 
585
            after_writing.wait()
 
586
        c1._write_config_file = c1_write_config_file
 
587
        def c1_set_option():
 
588
            c1.set_user_option('one', 'c1')
 
589
            writing_done.set()
 
590
        t1 = threading.Thread(target=c1_set_option)
 
591
        # Collect the thread after the test
 
592
        self.addCleanup(t1.join)
 
593
        # Be ready to unblock the thread if the test goes wrong
 
594
        self.addCleanup(after_writing.set)
 
595
        t1.start()
 
596
        before_writing.wait()
 
597
        self.assertTrue(c1._lock.is_held)
 
598
        self.assertRaises(errors.LockContention,
 
599
                          c2.set_user_option, 'one', 'c2')
 
600
        self.assertEquals('c1', c1.get_user_option('one'))
 
601
        # Let the lock be released
 
602
        after_writing.set()
 
603
        writing_done.wait()
 
604
        c2.set_user_option('one', 'c2')
 
605
        self.assertEquals('c2', c2.get_user_option('one'))
 
606
 
 
607
    def test_read_while_writing(self):
 
608
       c1 = self.config
 
609
       # We spawn a thread that will pause *during* the write
 
610
       ready_to_write = threading.Event()
 
611
       do_writing = threading.Event()
 
612
       writing_done = threading.Event()
 
613
       c1_orig = c1._write_config_file
 
614
       def c1_write_config_file():
 
615
           ready_to_write.set()
 
616
           # The lock is held we wait for the main thread to decide when to
 
617
           # continue
 
618
           do_writing.wait()
 
619
           c1_orig()
 
620
           writing_done.set()
 
621
       c1._write_config_file = c1_write_config_file
 
622
       def c1_set_option():
 
623
           c1.set_user_option('one', 'c1')
 
624
       t1 = threading.Thread(target=c1_set_option)
 
625
       # Collect the thread after the test
 
626
       self.addCleanup(t1.join)
 
627
       # Be ready to unblock the thread if the test goes wrong
 
628
       self.addCleanup(do_writing.set)
 
629
       t1.start()
 
630
       # Ensure the thread is ready to write
 
631
       ready_to_write.wait()
 
632
       self.assertTrue(c1._lock.is_held)
 
633
       self.assertEquals('c1', c1.get_user_option('one'))
 
634
       # If we read during the write, we get the old value
 
635
       c2 = self.get_existing_config()
 
636
       self.assertEquals('1', c2.get_user_option('one'))
 
637
       # Let the writing occur and ensure it occurred
 
638
       do_writing.set()
 
639
       writing_done.wait()
 
640
       # Now we get the updated value
 
641
       c3 = self.get_existing_config()
 
642
       self.assertEquals('c1', c3.get_user_option('one'))
 
643
 
396
644
 
397
645
class TestGetUserOptionAs(TestIniConfig):
398
646
 
406
654
        get_bool = conf.get_user_option_as_bool
407
655
        self.assertEqual(True, get_bool('a_true_bool'))
408
656
        self.assertEqual(False, get_bool('a_false_bool'))
 
657
        warnings = []
 
658
        def warning(*args):
 
659
            warnings.append(args[0] % args[1:])
 
660
        self.overrideAttr(trace, 'warning', warning)
 
661
        msg = 'Value "%s" is not a boolean for "%s"'
409
662
        self.assertIs(None, get_bool('an_invalid_bool'))
 
663
        self.assertEquals(msg % ('maybe', 'an_invalid_bool'), warnings[0])
 
664
        warnings = []
410
665
        self.assertIs(None, get_bool('not_defined_in_this_config'))
411
 
 
 
666
        self.assertEquals([], warnings)
412
667
 
413
668
    def test_get_user_option_as_list(self):
414
669
        conf, parser = self.make_config_parser("""
498
753
        branch = self.make_branch('branch')
499
754
        self.assertEqual('branch', branch.nick)
500
755
 
501
 
        locations = config.locations_config_filename()
502
 
        config.ensure_config_dir_exists()
503
756
        local_url = urlutils.local_path_to_url('branch')
504
 
        open(locations, 'wb').write('[%s]\nnickname = foobar'
505
 
                                    % (local_url,))
 
757
        conf = config.LocationConfig.from_string(
 
758
            '[%s]\nnickname = foobar' % (local_url,),
 
759
            local_url, save=True)
506
760
        self.assertEqual('foobar', branch.nick)
507
761
 
508
762
    def test_config_local_path(self):
510
764
        branch = self.make_branch('branch')
511
765
        self.assertEqual('branch', branch.nick)
512
766
 
513
 
        locations = config.locations_config_filename()
514
 
        config.ensure_config_dir_exists()
515
 
        open(locations, 'wb').write('[%s/branch]\nnickname = barry'
516
 
                                    % (osutils.getcwd().encode('utf8'),))
 
767
        local_path = osutils.getcwd().encode('utf8')
 
768
        conf = config.LocationConfig.from_string(
 
769
            '[%s/branch]\nnickname = barry' % (local_path,),
 
770
            'branch',  save=True)
517
771
        self.assertEqual('barry', branch.nick)
518
772
 
519
773
    def test_config_creates_local(self):
520
774
        """Creating a new entry in config uses a local path."""
521
775
        branch = self.make_branch('branch', format='knit')
522
776
        branch.set_push_location('http://foobar')
523
 
        locations = config.locations_config_filename()
524
777
        local_path = osutils.getcwd().encode('utf8')
525
778
        # Surprisingly ConfigObj doesn't create a trailing newline
526
 
        self.check_file_contents(locations,
 
779
        self.check_file_contents(config.locations_config_filename(),
527
780
                                 '[%s/branch]\n'
528
781
                                 'push_location = http://foobar\n'
529
782
                                 'push_location:policy = norecurse\n'
534
787
        self.assertEqual('!repo', b.get_config().get_nickname())
535
788
 
536
789
    def test_warn_if_masked(self):
537
 
        _warning = trace.warning
538
790
        warnings = []
539
791
        def warning(*args):
540
792
            warnings.append(args[0] % args[1:])
 
793
        self.overrideAttr(trace, 'warning', warning)
541
794
 
542
795
        def set_option(store, warn_masked=True):
543
796
            warnings[:] = []
549
802
            else:
550
803
                self.assertEqual(1, len(warnings))
551
804
                self.assertEqual(warning, warnings[0])
552
 
        trace.warning = warning
553
 
        try:
554
 
            branch = self.make_branch('.')
555
 
            conf = branch.get_config()
556
 
            set_option(config.STORE_GLOBAL)
557
 
            assertWarning(None)
558
 
            set_option(config.STORE_BRANCH)
559
 
            assertWarning(None)
560
 
            set_option(config.STORE_GLOBAL)
561
 
            assertWarning('Value "4" is masked by "3" from branch.conf')
562
 
            set_option(config.STORE_GLOBAL, warn_masked=False)
563
 
            assertWarning(None)
564
 
            set_option(config.STORE_LOCATION)
565
 
            assertWarning(None)
566
 
            set_option(config.STORE_BRANCH)
567
 
            assertWarning('Value "3" is masked by "0" from locations.conf')
568
 
            set_option(config.STORE_BRANCH, warn_masked=False)
569
 
            assertWarning(None)
570
 
        finally:
571
 
            trace.warning = _warning
572
 
 
573
 
 
574
 
class TestGlobalConfigItems(tests.TestCase):
 
805
        branch = self.make_branch('.')
 
806
        conf = branch.get_config()
 
807
        set_option(config.STORE_GLOBAL)
 
808
        assertWarning(None)
 
809
        set_option(config.STORE_BRANCH)
 
810
        assertWarning(None)
 
811
        set_option(config.STORE_GLOBAL)
 
812
        assertWarning('Value "4" is masked by "3" from branch.conf')
 
813
        set_option(config.STORE_GLOBAL, warn_masked=False)
 
814
        assertWarning(None)
 
815
        set_option(config.STORE_LOCATION)
 
816
        assertWarning(None)
 
817
        set_option(config.STORE_BRANCH)
 
818
        assertWarning('Value "3" is masked by "0" from locations.conf')
 
819
        set_option(config.STORE_BRANCH, warn_masked=False)
 
820
        assertWarning(None)
 
821
 
 
822
 
 
823
class TestGlobalConfigItems(tests.TestCaseInTempDir):
575
824
 
576
825
    def test_user_id(self):
577
 
        config_file = StringIO(sample_config_text.encode('utf-8'))
578
 
        my_config = config.GlobalConfig()
579
 
        my_config._parser = my_config._get_parser(file=config_file)
 
826
        my_config = config.GlobalConfig.from_string(sample_config_text)
580
827
        self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
581
828
                         my_config._get_user_id())
582
829
 
583
830
    def test_absent_user_id(self):
584
 
        config_file = StringIO("")
585
831
        my_config = config.GlobalConfig()
586
 
        my_config._parser = my_config._get_parser(file=config_file)
587
832
        self.assertEqual(None, my_config._get_user_id())
588
833
 
589
834
    def test_configured_editor(self):
590
 
        config_file = StringIO(sample_config_text.encode('utf-8'))
591
 
        my_config = config.GlobalConfig()
592
 
        my_config._parser = my_config._get_parser(file=config_file)
 
835
        my_config = config.GlobalConfig.from_string(sample_config_text)
593
836
        self.assertEqual("vim", my_config.get_editor())
594
837
 
595
838
    def test_signatures_always(self):
596
 
        config_file = StringIO(sample_always_signatures)
597
 
        my_config = config.GlobalConfig()
598
 
        my_config._parser = my_config._get_parser(file=config_file)
 
839
        my_config = config.GlobalConfig.from_string(sample_always_signatures)
599
840
        self.assertEqual(config.CHECK_NEVER,
600
841
                         my_config.signature_checking())
601
842
        self.assertEqual(config.SIGN_ALWAYS,
603
844
        self.assertEqual(True, my_config.signature_needed())
604
845
 
605
846
    def test_signatures_if_possible(self):
606
 
        config_file = StringIO(sample_maybe_signatures)
607
 
        my_config = config.GlobalConfig()
608
 
        my_config._parser = my_config._get_parser(file=config_file)
 
847
        my_config = config.GlobalConfig.from_string(sample_maybe_signatures)
609
848
        self.assertEqual(config.CHECK_NEVER,
610
849
                         my_config.signature_checking())
611
850
        self.assertEqual(config.SIGN_WHEN_REQUIRED,
613
852
        self.assertEqual(False, my_config.signature_needed())
614
853
 
615
854
    def test_signatures_ignore(self):
616
 
        config_file = StringIO(sample_ignore_signatures)
617
 
        my_config = config.GlobalConfig()
618
 
        my_config._parser = my_config._get_parser(file=config_file)
 
855
        my_config = config.GlobalConfig.from_string(sample_ignore_signatures)
619
856
        self.assertEqual(config.CHECK_ALWAYS,
620
857
                         my_config.signature_checking())
621
858
        self.assertEqual(config.SIGN_NEVER,
623
860
        self.assertEqual(False, my_config.signature_needed())
624
861
 
625
862
    def _get_sample_config(self):
626
 
        config_file = StringIO(sample_config_text.encode('utf-8'))
627
 
        my_config = config.GlobalConfig()
628
 
        my_config._parser = my_config._get_parser(file=config_file)
 
863
        my_config = config.GlobalConfig.from_string(sample_config_text)
629
864
        return my_config
630
865
 
631
866
    def test_gpg_signing_command(self):
634
869
        self.assertEqual(False, my_config.signature_needed())
635
870
 
636
871
    def _get_empty_config(self):
637
 
        config_file = StringIO("")
638
872
        my_config = config.GlobalConfig()
639
 
        my_config._parser = my_config._get_parser(file=config_file)
640
873
        return my_config
641
874
 
642
875
    def test_gpg_signing_command_unset(self):
715
948
        self.assertIs(None, new_config.get_alias('commit'))
716
949
 
717
950
 
718
 
class TestLocationConfig(tests.TestCaseInTempDir):
 
951
class TestLocationConfig(tests.TestCaseInTempDir, TestOptionsMixin):
719
952
 
720
953
    def test_constructs(self):
721
954
        my_config = config.LocationConfig('http://example.com')
737
970
        self.assertEqual(parser._calls,
738
971
                         [('__init__', config.locations_config_filename(),
739
972
                           'utf-8')])
740
 
        config.ensure_config_dir_exists()
741
 
        #os.mkdir(config.config_dir())
742
 
        f = file(config.branches_config_filename(), 'wb')
743
 
        f.write('')
744
 
        f.close()
745
 
        oldparserclass = config.ConfigObj
746
 
        config.ConfigObj = InstrumentedConfigObj
747
 
        try:
748
 
            my_config = config.LocationConfig('http://www.example.com')
749
 
            parser = my_config._get_parser()
750
 
        finally:
751
 
            config.ConfigObj = oldparserclass
752
973
 
753
974
    def test_get_global_config(self):
754
975
        my_config = config.BranchConfig(FakeBranch('http://example.com'))
841
1062
            'http://www.example.com', 'appendpath_option'),
842
1063
            config.POLICY_APPENDPATH)
843
1064
 
 
1065
    def test__get_options_with_policy(self):
 
1066
        self.get_branch_config('/dir/subdir',
 
1067
                               location_config="""\
 
1068
[/dir]
 
1069
other_url = /other-dir
 
1070
other_url:policy = appendpath
 
1071
[/dir/subdir]
 
1072
other_url = /other-subdir
 
1073
""")
 
1074
        self.assertOptions(
 
1075
            [(u'other_url', u'/other-subdir', u'/dir/subdir', 'locations'),
 
1076
             (u'other_url', u'/other-dir', u'/dir', 'locations'),
 
1077
             (u'other_url:policy', u'appendpath', u'/dir', 'locations')],
 
1078
            self.my_location_config)
 
1079
 
844
1080
    def test_location_without_username(self):
845
1081
        self.get_branch_config('http://www.example.com/ignoreparent')
846
1082
        self.assertEqual(u'Erik B\u00e5gfors <erik@bagfors.nu>',
982
1218
        self.assertEqual('bzrlib.tests.test_config.post_commit',
983
1219
                         self.my_config.post_commit())
984
1220
 
985
 
    def get_branch_config(self, location, global_config=None):
 
1221
    def get_branch_config(self, location, global_config=None,
 
1222
                          location_config=None):
 
1223
        my_branch = FakeBranch(location)
986
1224
        if global_config is None:
987
 
            global_file = StringIO(sample_config_text.encode('utf-8'))
988
 
        else:
989
 
            global_file = StringIO(global_config.encode('utf-8'))
990
 
        branches_file = StringIO(sample_branches_text.encode('utf-8'))
991
 
        self.my_config = config.BranchConfig(FakeBranch(location))
992
 
        # Force location config to use specified file
993
 
        self.my_location_config = self.my_config._get_location_config()
994
 
        self.my_location_config._get_parser(branches_file)
995
 
        # Force global config to use specified file
996
 
        self.my_config._get_global_config()._get_parser(global_file)
 
1225
            global_config = sample_config_text
 
1226
        if location_config is None:
 
1227
            location_config = sample_branches_text
 
1228
 
 
1229
        my_global_config = config.GlobalConfig.from_string(global_config,
 
1230
                                                           save=True)
 
1231
        my_location_config = config.LocationConfig.from_string(
 
1232
            location_config, my_branch.base, save=True)
 
1233
        my_config = config.BranchConfig(my_branch)
 
1234
        self.my_config = my_config
 
1235
        self.my_location_config = my_config._get_location_config()
997
1236
 
998
1237
    def test_set_user_setting_sets_and_saves(self):
999
1238
        self.get_branch_config('/a/c')
1000
1239
        record = InstrumentedConfigObj("foo")
1001
1240
        self.my_location_config._parser = record
1002
1241
 
1003
 
        real_mkdir = os.mkdir
1004
 
        self.created = False
1005
 
        def checked_mkdir(path, mode=0777):
1006
 
            self.log('making directory: %s', path)
1007
 
            real_mkdir(path, mode)
1008
 
            self.created = True
1009
 
 
1010
 
        os.mkdir = checked_mkdir
1011
 
        try:
1012
 
            self.callDeprecated(['The recurse option is deprecated as of '
1013
 
                                 '0.14.  The section "/a/c" has been '
1014
 
                                 'converted to use policies.'],
1015
 
                                self.my_config.set_user_option,
1016
 
                                'foo', 'bar', store=config.STORE_LOCATION)
1017
 
        finally:
1018
 
            os.mkdir = real_mkdir
1019
 
 
1020
 
        self.failUnless(self.created, 'Failed to create ~/.bazaar')
1021
 
        self.assertEqual([('__contains__', '/a/c'),
 
1242
        self.callDeprecated(['The recurse option is deprecated as of '
 
1243
                             '0.14.  The section "/a/c" has been '
 
1244
                             'converted to use policies.'],
 
1245
                            self.my_config.set_user_option,
 
1246
                            'foo', 'bar', store=config.STORE_LOCATION)
 
1247
        self.assertEqual([('reload',),
 
1248
                          ('__contains__', '/a/c'),
1022
1249
                          ('__contains__', '/a/c/'),
1023
1250
                          ('__setitem__', '/a/c', {}),
1024
1251
                          ('__getitem__', '/a/c'),
1067
1294
option = exact
1068
1295
"""
1069
1296
 
1070
 
 
1071
1297
class TestBranchConfigItems(tests.TestCaseInTempDir):
1072
1298
 
1073
1299
    def get_branch_config(self, global_config=None, location=None,
1074
1300
                          location_config=None, branch_data_config=None):
1075
 
        my_config = config.BranchConfig(FakeBranch(location))
 
1301
        my_branch = FakeBranch(location)
1076
1302
        if global_config is not None:
1077
 
            global_file = StringIO(global_config.encode('utf-8'))
1078
 
            my_config._get_global_config()._get_parser(global_file)
1079
 
        self.my_location_config = my_config._get_location_config()
 
1303
            my_global_config = config.GlobalConfig.from_string(global_config,
 
1304
                                                               save=True)
1080
1305
        if location_config is not None:
1081
 
            location_file = StringIO(location_config.encode('utf-8'))
1082
 
            self.my_location_config._get_parser(location_file)
 
1306
            my_location_config = config.LocationConfig.from_string(
 
1307
                location_config, my_branch.base, save=True)
 
1308
        my_config = config.BranchConfig(my_branch)
1083
1309
        if branch_data_config is not None:
1084
1310
            my_config.branch.control_files.files['branch.conf'] = \
1085
1311
                branch_data_config
1099
1325
                         my_config.username())
1100
1326
 
1101
1327
    def test_not_set_in_branch(self):
1102
 
        my_config = self.get_branch_config(sample_config_text)
 
1328
        my_config = self.get_branch_config(global_config=sample_config_text)
1103
1329
        self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
1104
1330
                         my_config._get_user_id())
1105
1331
        my_config.branch.control_files.files['email'] = "John"
1129
1355
 
1130
1356
    def test_gpg_signing_command(self):
1131
1357
        my_config = self.get_branch_config(
 
1358
            global_config=sample_config_text,
1132
1359
            # branch data cannot set gpg_signing_command
1133
1360
            branch_data_config="gpg_signing_command=pgp")
1134
 
        config_file = StringIO(sample_config_text.encode('utf-8'))
1135
 
        my_config._get_global_config()._get_parser(config_file)
1136
1361
        self.assertEqual('gnome-gpg', my_config.gpg_signing_command())
1137
1362
 
1138
1363
    def test_get_user_option_global(self):
1139
 
        branch = FakeBranch()
1140
 
        my_config = config.BranchConfig(branch)
1141
 
        config_file = StringIO(sample_config_text.encode('utf-8'))
1142
 
        (my_config._get_global_config()._get_parser(config_file))
 
1364
        my_config = self.get_branch_config(global_config=sample_config_text)
1143
1365
        self.assertEqual('something',
1144
1366
                         my_config.get_user_option('user_global_option'))
1145
1367
 
1146
1368
    def test_post_commit_default(self):
1147
 
        branch = FakeBranch()
1148
 
        my_config = self.get_branch_config(sample_config_text, '/a/c',
1149
 
                                           sample_branches_text)
 
1369
        my_config = self.get_branch_config(global_config=sample_config_text,
 
1370
                                      location='/a/c',
 
1371
                                      location_config=sample_branches_text)
1150
1372
        self.assertEqual(my_config.branch.base, '/a/c')
1151
1373
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1152
1374
                         my_config.post_commit())
1153
1375
        my_config.set_user_option('post_commit', 'rmtree_root')
1154
 
        # post-commit is ignored when bresent in branch data
 
1376
        # post-commit is ignored when present in branch data
1155
1377
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1156
1378
                         my_config.post_commit())
1157
1379
        my_config.set_user_option('post_commit', 'rmtree_root',
1159
1381
        self.assertEqual('rmtree_root', my_config.post_commit())
1160
1382
 
1161
1383
    def test_config_precedence(self):
 
1384
        # FIXME: eager test, luckily no persitent config file makes it fail
 
1385
        # -- vila 20100716
1162
1386
        my_config = self.get_branch_config(global_config=precedence_global)
1163
1387
        self.assertEqual(my_config.get_user_option('option'), 'global')
1164
1388
        my_config = self.get_branch_config(global_config=precedence_global,
1165
 
                                      branch_data_config=precedence_branch)
 
1389
                                           branch_data_config=precedence_branch)
1166
1390
        self.assertEqual(my_config.get_user_option('option'), 'branch')
1167
 
        my_config = self.get_branch_config(global_config=precedence_global,
1168
 
                                      branch_data_config=precedence_branch,
1169
 
                                      location_config=precedence_location)
 
1391
        my_config = self.get_branch_config(
 
1392
            global_config=precedence_global,
 
1393
            branch_data_config=precedence_branch,
 
1394
            location_config=precedence_location)
1170
1395
        self.assertEqual(my_config.get_user_option('option'), 'recurse')
1171
 
        my_config = self.get_branch_config(global_config=precedence_global,
1172
 
                                      branch_data_config=precedence_branch,
1173
 
                                      location_config=precedence_location,
1174
 
                                      location='http://example.com/specific')
 
1396
        my_config = self.get_branch_config(
 
1397
            global_config=precedence_global,
 
1398
            branch_data_config=precedence_branch,
 
1399
            location_config=precedence_location,
 
1400
            location='http://example.com/specific')
1175
1401
        self.assertEqual(my_config.get_user_option('option'), 'exact')
1176
1402
 
1177
1403
    def test_get_mail_client(self):
1305
1531
        self.assertIs(None, bzrdir_config.get_default_stack_on())
1306
1532
 
1307
1533
 
 
1534
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
 
1535
 
 
1536
    def setUp(self):
 
1537
        super(TestConfigGetOptions, self).setUp()
 
1538
        create_configs(self)
 
1539
 
 
1540
    # One variable in none of the above
 
1541
    def test_no_variable(self):
 
1542
        # Using branch should query branch, locations and bazaar
 
1543
        self.assertOptions([], self.branch_config)
 
1544
 
 
1545
    def test_option_in_bazaar(self):
 
1546
        self.bazaar_config.set_user_option('file', 'bazaar')
 
1547
        self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
 
1548
                           self.bazaar_config)
 
1549
 
 
1550
    def test_option_in_locations(self):
 
1551
        self.locations_config.set_user_option('file', 'locations')
 
1552
        self.assertOptions(
 
1553
            [('file', 'locations', self.tree.basedir, 'locations')],
 
1554
            self.locations_config)
 
1555
 
 
1556
    def test_option_in_branch(self):
 
1557
        self.branch_config.set_user_option('file', 'branch')
 
1558
        self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
 
1559
                           self.branch_config)
 
1560
 
 
1561
    def test_option_in_bazaar_and_branch(self):
 
1562
        self.bazaar_config.set_user_option('file', 'bazaar')
 
1563
        self.branch_config.set_user_option('file', 'branch')
 
1564
        self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
 
1565
                            ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
 
1566
                           self.branch_config)
 
1567
 
 
1568
    def test_option_in_branch_and_locations(self):
 
1569
        # Hmm, locations override branch :-/
 
1570
        self.locations_config.set_user_option('file', 'locations')
 
1571
        self.branch_config.set_user_option('file', 'branch')
 
1572
        self.assertOptions(
 
1573
            [('file', 'locations', self.tree.basedir, 'locations'),
 
1574
             ('file', 'branch', 'DEFAULT', 'branch'),],
 
1575
            self.branch_config)
 
1576
 
 
1577
    def test_option_in_bazaar_locations_and_branch(self):
 
1578
        self.bazaar_config.set_user_option('file', 'bazaar')
 
1579
        self.locations_config.set_user_option('file', 'locations')
 
1580
        self.branch_config.set_user_option('file', 'branch')
 
1581
        self.assertOptions(
 
1582
            [('file', 'locations', self.tree.basedir, 'locations'),
 
1583
             ('file', 'branch', 'DEFAULT', 'branch'),
 
1584
             ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
 
1585
            self.branch_config)
 
1586
 
 
1587
 
 
1588
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
 
1589
 
 
1590
    def setUp(self):
 
1591
        super(TestConfigRemoveOption, self).setUp()
 
1592
        create_configs_with_file_option(self)
 
1593
 
 
1594
    def test_remove_in_locations(self):
 
1595
        self.locations_config.remove_user_option('file', self.tree.basedir)
 
1596
        self.assertOptions(
 
1597
            [('file', 'branch', 'DEFAULT', 'branch'),
 
1598
             ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
 
1599
            self.branch_config)
 
1600
 
 
1601
    def test_remove_in_branch(self):
 
1602
        self.branch_config.remove_user_option('file')
 
1603
        self.assertOptions(
 
1604
            [('file', 'locations', self.tree.basedir, 'locations'),
 
1605
             ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
 
1606
            self.branch_config)
 
1607
 
 
1608
    def test_remove_in_bazaar(self):
 
1609
        self.bazaar_config.remove_user_option('file')
 
1610
        self.assertOptions(
 
1611
            [('file', 'locations', self.tree.basedir, 'locations'),
 
1612
             ('file', 'branch', 'DEFAULT', 'branch'),],
 
1613
            self.branch_config)
 
1614
 
 
1615
 
 
1616
class TestConfigGetSections(tests.TestCaseWithTransport):
 
1617
 
 
1618
    def setUp(self):
 
1619
        super(TestConfigGetSections, self).setUp()
 
1620
        create_configs(self)
 
1621
 
 
1622
    def assertSectionNames(self, expected, conf, name=None):
 
1623
        """Check which sections are returned for a given config.
 
1624
 
 
1625
        If fallback configurations exist their sections can be included.
 
1626
 
 
1627
        :param expected: A list of section names.
 
1628
 
 
1629
        :param conf: The configuration that will be queried.
 
1630
 
 
1631
        :param name: An optional section name that will be passed to
 
1632
            get_sections().
 
1633
        """
 
1634
        sections = list(conf._get_sections(name))
 
1635
        self.assertLength(len(expected), sections)
 
1636
        self.assertEqual(expected, [name for name, _, _ in sections])
 
1637
 
 
1638
    def test_bazaar_default_section(self):
 
1639
        self.assertSectionNames(['DEFAULT'], self.bazaar_config)
 
1640
 
 
1641
    def test_locations_default_section(self):
 
1642
        # No sections are defined in an empty file
 
1643
        self.assertSectionNames([], self.locations_config)
 
1644
 
 
1645
    def test_locations_named_section(self):
 
1646
        self.locations_config.set_user_option('file', 'locations')
 
1647
        self.assertSectionNames([self.tree.basedir], self.locations_config)
 
1648
 
 
1649
    def test_locations_matching_sections(self):
 
1650
        loc_config = self.locations_config
 
1651
        loc_config.set_user_option('file', 'locations')
 
1652
        # We need to cheat a bit here to create an option in sections above and
 
1653
        # below the 'location' one.
 
1654
        parser = loc_config._get_parser()
 
1655
        # locations.cong deals with '/' ignoring native os.sep
 
1656
        location_names = self.tree.basedir.split('/')
 
1657
        parent = '/'.join(location_names[:-1])
 
1658
        child = '/'.join(location_names + ['child'])
 
1659
        parser[parent] = {}
 
1660
        parser[parent]['file'] = 'parent'
 
1661
        parser[child] = {}
 
1662
        parser[child]['file'] = 'child'
 
1663
        self.assertSectionNames([self.tree.basedir, parent], loc_config)
 
1664
 
 
1665
    def test_branch_data_default_section(self):
 
1666
        self.assertSectionNames([None],
 
1667
                                self.branch_config._get_branch_data_config())
 
1668
 
 
1669
    def test_branch_default_sections(self):
 
1670
        # No sections are defined in an empty locations file
 
1671
        self.assertSectionNames([None, 'DEFAULT'],
 
1672
                                self.branch_config)
 
1673
        # Unless we define an option
 
1674
        self.branch_config._get_location_config().set_user_option(
 
1675
            'file', 'locations')
 
1676
        self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
 
1677
                                self.branch_config)
 
1678
 
 
1679
    def test_bazaar_named_section(self):
 
1680
        # We need to cheat as the API doesn't give direct access to sections
 
1681
        # other than DEFAULT.
 
1682
        self.bazaar_config.set_alias('bazaar', 'bzr')
 
1683
        self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
 
1684
 
 
1685
 
1308
1686
class TestAuthenticationConfigFile(tests.TestCase):
1309
1687
    """Test the authentication.conf file matching"""
1310
1688