/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_config.py

  • Committer: Martin Pool
  • Date: 2011-04-01 03:07:34 UTC
  • mfrom: (5609.29.3 2.3)
  • mto: (5609.29.4 2.3)
  • mto: This revision was merged to the branch mainline in revision 5755.
  • Revision ID: mbp@canonical.com-20110401030734-wip8a66uf8aphgud
merge up to bzr 2.3

Show diffs side-by-side

added added

removed removed

Lines of Context:
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 (
38
42
from bzrlib.tests import (
39
43
    features,
40
44
    TestSkipped,
 
45
    scenarios,
41
46
    )
42
47
from bzrlib.util.configobj import configobj
43
48
 
44
49
 
 
50
def lockable_config_scenarios():
 
51
    return [
 
52
        ('global',
 
53
         {'config_class': config.GlobalConfig,
 
54
          'config_args': [],
 
55
          'config_section': 'DEFAULT'}),
 
56
        ('locations',
 
57
         {'config_class': config.LocationConfig,
 
58
          'config_args': ['.'],
 
59
          'config_section': '.'}),]
 
60
 
 
61
 
 
62
load_tests = scenarios.load_tests_apply_scenarios
 
63
 
 
64
 
45
65
sample_long_alias="log -r-15..-1 --line"
46
66
sample_config_text = u"""
47
67
[DEFAULT]
109
129
"""
110
130
 
111
131
 
 
132
def create_configs(test):
 
133
    """Create configuration files for a given test.
 
134
 
 
135
    This requires creating a tree (and populate the ``test.tree`` attribute)
 
136
    and its associated branch and will populate the following attributes:
 
137
 
 
138
    - branch_config: A BranchConfig for the associated branch.
 
139
 
 
140
    - locations_config : A LocationConfig for the associated branch
 
141
 
 
142
    - bazaar_config: A GlobalConfig.
 
143
 
 
144
    The tree and branch are created in a 'tree' subdirectory so the tests can
 
145
    still use the test directory to stay outside of the branch.
 
146
    """
 
147
    tree = test.make_branch_and_tree('tree')
 
148
    test.tree = tree
 
149
    test.branch_config = config.BranchConfig(tree.branch)
 
150
    test.locations_config = config.LocationConfig(tree.basedir)
 
151
    test.bazaar_config = config.GlobalConfig()
 
152
 
 
153
 
 
154
def create_configs_with_file_option(test):
 
155
    """Create configuration files with a ``file`` option set in each.
 
156
 
 
157
    This builds on ``create_configs`` and add one ``file`` option in each
 
158
    configuration with a value which allows identifying the configuration file.
 
159
    """
 
160
    create_configs(test)
 
161
    test.bazaar_config.set_user_option('file', 'bazaar')
 
162
    test.locations_config.set_user_option('file', 'locations')
 
163
    test.branch_config.set_user_option('file', 'branch')
 
164
 
 
165
 
 
166
class TestOptionsMixin:
 
167
 
 
168
    def assertOptions(self, expected, conf):
 
169
        # We don't care about the parser (as it will make tests hard to write
 
170
        # and error-prone anyway)
 
171
        self.assertThat([opt[:4] for opt in conf._get_options()],
 
172
                        matchers.Equals(expected))
 
173
 
 
174
 
112
175
class InstrumentedConfigObj(object):
113
176
    """A config obj look-enough-alike to record calls made to it."""
114
177
 
133
196
        self._calls.append(('keys',))
134
197
        return []
135
198
 
 
199
    def reload(self):
 
200
        self._calls.append(('reload',))
 
201
 
136
202
    def write(self, arg):
137
203
        self._calls.append(('write',))
138
204
 
244
310
        """
245
311
        co = config.ConfigObj()
246
312
        co['test'] = 'foo#bar'
247
 
        lines = co.write()
 
313
        outfile = StringIO()
 
314
        co.write(outfile=outfile)
 
315
        lines = outfile.getvalue().splitlines()
248
316
        self.assertEqual(lines, ['test = "foo#bar"'])
249
317
        co2 = config.ConfigObj(lines)
250
318
        self.assertEqual(co2['test'], 'foo#bar')
251
319
 
 
320
    def test_triple_quotes(self):
 
321
        # Bug #710410: if the value string has triple quotes
 
322
        # then ConfigObj versions up to 4.7.2 will quote them wrong
 
323
        # and won't able to read them back
 
324
        triple_quotes_value = '''spam
 
325
""" that's my spam """
 
326
eggs'''
 
327
        co = config.ConfigObj()
 
328
        co['test'] = triple_quotes_value
 
329
        # While writing this test another bug in ConfigObj has been found:
 
330
        # method co.write() without arguments produces list of lines
 
331
        # one option per line, and multiline values are not split
 
332
        # across multiple lines,
 
333
        # and that breaks the parsing these lines back by ConfigObj.
 
334
        # This issue only affects test, but it's better to avoid
 
335
        # `co.write()` construct at all.
 
336
        # [bialix 20110222] bug report sent to ConfigObj's author
 
337
        outfile = StringIO()
 
338
        co.write(outfile=outfile)
 
339
        output = outfile.getvalue()
 
340
        # now we're trying to read it back
 
341
        co2 = config.ConfigObj(StringIO(output))
 
342
        self.assertEquals(triple_quotes_value, co2['test'])
 
343
 
252
344
 
253
345
erroneous_config = """[section] # line 1
254
346
good=good # line 2
337
429
 
338
430
    def setUp(self):
339
431
        super(TestConfigPath, self).setUp()
340
 
        os.environ['HOME'] = '/home/bogus'
341
 
        os.environ['XDG_CACHE_DIR'] = ''
 
432
        self.overrideEnv('HOME', '/home/bogus')
 
433
        self.overrideEnv('XDG_CACHE_DIR', '')
342
434
        if sys.platform == 'win32':
343
 
            os.environ['BZR_HOME'] = \
344
 
                r'C:\Documents and Settings\bogus\Application Data'
 
435
            self.overrideEnv(
 
436
                'BZR_HOME', r'C:\Documents and Settings\bogus\Application Data')
345
437
            self.bzr_home = \
346
438
                'C:/Documents and Settings/bogus/Application Data/bazaar/2.0'
347
439
        else:
354
446
        self.assertEqual(config.config_filename(),
355
447
                         self.bzr_home + '/bazaar.conf')
356
448
 
357
 
    def test_branches_config_filename(self):
358
 
        self.assertEqual(config.branches_config_filename(),
359
 
                         self.bzr_home + '/branches.conf')
360
 
 
361
449
    def test_locations_config_filename(self):
362
450
        self.assertEqual(config.locations_config_filename(),
363
451
                         self.bzr_home + '/locations.conf')
371
459
            '/home/bogus/.cache')
372
460
 
373
461
 
 
462
class TestXDGConfigDir(tests.TestCaseInTempDir):
 
463
    # must be in temp dir because config tests for the existence of the bazaar
 
464
    # subdirectory of $XDG_CONFIG_HOME
 
465
 
 
466
    def setUp(self):
 
467
        if sys.platform in ('darwin', 'win32'):
 
468
            raise tests.TestNotApplicable(
 
469
                'XDG config dir not used on this platform')
 
470
        super(TestXDGConfigDir, self).setUp()
 
471
        self.overrideEnv('HOME', self.test_home_dir)
 
472
        # BZR_HOME overrides everything we want to test so unset it.
 
473
        self.overrideEnv('BZR_HOME', None)
 
474
 
 
475
    def test_xdg_config_dir_exists(self):
 
476
        """When ~/.config/bazaar exists, use it as the config dir."""
 
477
        newdir = osutils.pathjoin(self.test_home_dir, '.config', 'bazaar')
 
478
        os.makedirs(newdir)
 
479
        self.assertEqual(config.config_dir(), newdir)
 
480
 
 
481
    def test_xdg_config_home(self):
 
482
        """When XDG_CONFIG_HOME is set, use it."""
 
483
        xdgconfigdir = osutils.pathjoin(self.test_home_dir, 'xdgconfig')
 
484
        self.overrideEnv('XDG_CONFIG_HOME', xdgconfigdir)
 
485
        newdir = osutils.pathjoin(xdgconfigdir, 'bazaar')
 
486
        os.makedirs(newdir)
 
487
        self.assertEqual(config.config_dir(), newdir)
 
488
 
 
489
 
374
490
class TestIniConfig(tests.TestCaseInTempDir):
375
491
 
376
492
    def make_config_parser(self, s):
377
 
        conf = config.IniBasedConfig(None)
378
 
        parser = conf._get_parser(file=StringIO(s.encode('utf-8')))
379
 
        return conf, parser
 
493
        conf = config.IniBasedConfig.from_string(s)
 
494
        return conf, conf._get_parser()
380
495
 
381
496
 
382
497
class TestIniConfigBuilding(TestIniConfig):
383
498
 
384
499
    def test_contructs(self):
385
 
        my_config = config.IniBasedConfig("nothing")
 
500
        my_config = config.IniBasedConfig()
386
501
 
387
502
    def test_from_fp(self):
388
 
        config_file = StringIO(sample_config_text.encode('utf-8'))
389
 
        my_config = config.IniBasedConfig(None)
390
 
        self.failUnless(
391
 
            isinstance(my_config._get_parser(file=config_file),
392
 
                        configobj.ConfigObj))
 
503
        my_config = config.IniBasedConfig.from_string(sample_config_text)
 
504
        self.assertIsInstance(my_config._get_parser(), configobj.ConfigObj)
393
505
 
394
506
    def test_cached(self):
395
 
        config_file = StringIO(sample_config_text.encode('utf-8'))
396
 
        my_config = config.IniBasedConfig(None)
397
 
        parser = my_config._get_parser(file=config_file)
 
507
        my_config = config.IniBasedConfig.from_string(sample_config_text)
 
508
        parser = my_config._get_parser()
398
509
        self.failUnless(my_config._get_parser() is parser)
399
510
 
400
511
    def _dummy_chown(self, path, uid, gid):
401
512
        self.path, self.uid, self.gid = path, uid, gid
402
513
 
403
514
    def test_ini_config_ownership(self):
404
 
        """Ensure that chown is happening during _write_config_file.
405
 
        """
 
515
        """Ensure that chown is happening during _write_config_file"""
406
516
        self.requireFeature(features.chown_feature)
407
517
        self.overrideAttr(os, 'chown', self._dummy_chown)
408
518
        self.path = self.uid = self.gid = None
409
 
        def get_filename():
410
 
            return 'foo.conf'
411
 
        conf = config.IniBasedConfig(get_filename)
 
519
        conf = config.IniBasedConfig(file_name='./foo.conf')
412
520
        conf._write_config_file()
413
 
        self.assertEquals(self.path, 'foo.conf')
 
521
        self.assertEquals(self.path, './foo.conf')
414
522
        self.assertTrue(isinstance(self.uid, int))
415
523
        self.assertTrue(isinstance(self.gid, int))
416
524
 
 
525
    def test_get_filename_parameter_is_deprecated_(self):
 
526
        conf = self.callDeprecated([
 
527
            'IniBasedConfig.__init__(get_filename) was deprecated in 2.3.'
 
528
            ' Use file_name instead.'],
 
529
            config.IniBasedConfig, lambda: 'ini.conf')
 
530
        self.assertEqual('ini.conf', conf.file_name)
 
531
 
 
532
    def test_get_parser_file_parameter_is_deprecated_(self):
 
533
        config_file = StringIO(sample_config_text.encode('utf-8'))
 
534
        conf = config.IniBasedConfig.from_string(sample_config_text)
 
535
        conf = self.callDeprecated([
 
536
            'IniBasedConfig._get_parser(file=xxx) was deprecated in 2.3.'
 
537
            ' Use IniBasedConfig(_content=xxx) instead.'],
 
538
            conf._get_parser, file=config_file)
 
539
 
 
540
class TestIniConfigSaving(tests.TestCaseInTempDir):
 
541
 
 
542
    def test_cant_save_without_a_file_name(self):
 
543
        conf = config.IniBasedConfig()
 
544
        self.assertRaises(AssertionError, conf._write_config_file)
 
545
 
 
546
    def test_saved_with_content(self):
 
547
        content = 'foo = bar\n'
 
548
        conf = config.IniBasedConfig.from_string(
 
549
            content, file_name='./test.conf', save=True)
 
550
        self.assertFileEqual(content, 'test.conf')
 
551
 
 
552
 
 
553
class TestIniBaseConfigOnDisk(tests.TestCaseInTempDir):
 
554
 
 
555
    def test_cannot_reload_without_name(self):
 
556
        conf = config.IniBasedConfig.from_string(sample_config_text)
 
557
        self.assertRaises(AssertionError, conf.reload)
 
558
 
 
559
    def test_reload_see_new_value(self):
 
560
        c1 = config.IniBasedConfig.from_string('editor=vim\n',
 
561
                                               file_name='./test/conf')
 
562
        c1._write_config_file()
 
563
        c2 = config.IniBasedConfig.from_string('editor=emacs\n',
 
564
                                               file_name='./test/conf')
 
565
        c2._write_config_file()
 
566
        self.assertEqual('vim', c1.get_user_option('editor'))
 
567
        self.assertEqual('emacs', c2.get_user_option('editor'))
 
568
        # Make sure we get the Right value
 
569
        c1.reload()
 
570
        self.assertEqual('emacs', c1.get_user_option('editor'))
 
571
 
 
572
 
 
573
class TestLockableConfig(tests.TestCaseInTempDir):
 
574
 
 
575
    scenarios = lockable_config_scenarios()
 
576
 
 
577
    # Set by load_tests
 
578
    config_class = None
 
579
    config_args = None
 
580
    config_section = None
 
581
 
 
582
    def setUp(self):
 
583
        super(TestLockableConfig, self).setUp()
 
584
        self._content = '[%s]\none=1\ntwo=2\n' % (self.config_section,)
 
585
        self.config = self.create_config(self._content)
 
586
 
 
587
    def get_existing_config(self):
 
588
        return self.config_class(*self.config_args)
 
589
 
 
590
    def create_config(self, content):
 
591
        kwargs = dict(save=True)
 
592
        c = self.config_class.from_string(content, *self.config_args, **kwargs)
 
593
        return c
 
594
 
 
595
    def test_simple_read_access(self):
 
596
        self.assertEquals('1', self.config.get_user_option('one'))
 
597
 
 
598
    def test_simple_write_access(self):
 
599
        self.config.set_user_option('one', 'one')
 
600
        self.assertEquals('one', self.config.get_user_option('one'))
 
601
 
 
602
    def test_listen_to_the_last_speaker(self):
 
603
        c1 = self.config
 
604
        c2 = self.get_existing_config()
 
605
        c1.set_user_option('one', 'ONE')
 
606
        c2.set_user_option('two', 'TWO')
 
607
        self.assertEquals('ONE', c1.get_user_option('one'))
 
608
        self.assertEquals('TWO', c2.get_user_option('two'))
 
609
        # The second update respect the first one
 
610
        self.assertEquals('ONE', c2.get_user_option('one'))
 
611
 
 
612
    def test_last_speaker_wins(self):
 
613
        # If the same config is not shared, the same variable modified twice
 
614
        # can only see a single result.
 
615
        c1 = self.config
 
616
        c2 = self.get_existing_config()
 
617
        c1.set_user_option('one', 'c1')
 
618
        c2.set_user_option('one', 'c2')
 
619
        self.assertEquals('c2', c2._get_user_option('one'))
 
620
        # The first modification is still available until another refresh
 
621
        # occur
 
622
        self.assertEquals('c1', c1._get_user_option('one'))
 
623
        c1.set_user_option('two', 'done')
 
624
        self.assertEquals('c2', c1._get_user_option('one'))
 
625
 
 
626
    def test_writes_are_serialized(self):
 
627
        c1 = self.config
 
628
        c2 = self.get_existing_config()
 
629
 
 
630
        # We spawn a thread that will pause *during* the write
 
631
        before_writing = threading.Event()
 
632
        after_writing = threading.Event()
 
633
        writing_done = threading.Event()
 
634
        c1_orig = c1._write_config_file
 
635
        def c1_write_config_file():
 
636
            before_writing.set()
 
637
            c1_orig()
 
638
            # The lock is held we wait for the main thread to decide when to
 
639
            # continue
 
640
            after_writing.wait()
 
641
        c1._write_config_file = c1_write_config_file
 
642
        def c1_set_option():
 
643
            c1.set_user_option('one', 'c1')
 
644
            writing_done.set()
 
645
        t1 = threading.Thread(target=c1_set_option)
 
646
        # Collect the thread after the test
 
647
        self.addCleanup(t1.join)
 
648
        # Be ready to unblock the thread if the test goes wrong
 
649
        self.addCleanup(after_writing.set)
 
650
        t1.start()
 
651
        before_writing.wait()
 
652
        self.assertTrue(c1._lock.is_held)
 
653
        self.assertRaises(errors.LockContention,
 
654
                          c2.set_user_option, 'one', 'c2')
 
655
        self.assertEquals('c1', c1.get_user_option('one'))
 
656
        # Let the lock be released
 
657
        after_writing.set()
 
658
        writing_done.wait()
 
659
        c2.set_user_option('one', 'c2')
 
660
        self.assertEquals('c2', c2.get_user_option('one'))
 
661
 
 
662
    def test_read_while_writing(self):
 
663
       c1 = self.config
 
664
       # We spawn a thread that will pause *during* the write
 
665
       ready_to_write = threading.Event()
 
666
       do_writing = threading.Event()
 
667
       writing_done = threading.Event()
 
668
       c1_orig = c1._write_config_file
 
669
       def c1_write_config_file():
 
670
           ready_to_write.set()
 
671
           # The lock is held we wait for the main thread to decide when to
 
672
           # continue
 
673
           do_writing.wait()
 
674
           c1_orig()
 
675
           writing_done.set()
 
676
       c1._write_config_file = c1_write_config_file
 
677
       def c1_set_option():
 
678
           c1.set_user_option('one', 'c1')
 
679
       t1 = threading.Thread(target=c1_set_option)
 
680
       # Collect the thread after the test
 
681
       self.addCleanup(t1.join)
 
682
       # Be ready to unblock the thread if the test goes wrong
 
683
       self.addCleanup(do_writing.set)
 
684
       t1.start()
 
685
       # Ensure the thread is ready to write
 
686
       ready_to_write.wait()
 
687
       self.assertTrue(c1._lock.is_held)
 
688
       self.assertEquals('c1', c1.get_user_option('one'))
 
689
       # If we read during the write, we get the old value
 
690
       c2 = self.get_existing_config()
 
691
       self.assertEquals('1', c2.get_user_option('one'))
 
692
       # Let the writing occur and ensure it occurred
 
693
       do_writing.set()
 
694
       writing_done.wait()
 
695
       # Now we get the updated value
 
696
       c3 = self.get_existing_config()
 
697
       self.assertEquals('c1', c3.get_user_option('one'))
 
698
 
 
699
 
417
700
class TestGetUserOptionAs(TestIniConfig):
418
701
 
419
702
    def test_get_user_option_as_bool(self):
525
808
        branch = self.make_branch('branch')
526
809
        self.assertEqual('branch', branch.nick)
527
810
 
528
 
        locations = config.locations_config_filename()
529
 
        config.ensure_config_dir_exists()
530
811
        local_url = urlutils.local_path_to_url('branch')
531
 
        open(locations, 'wb').write('[%s]\nnickname = foobar'
532
 
                                    % (local_url,))
 
812
        conf = config.LocationConfig.from_string(
 
813
            '[%s]\nnickname = foobar' % (local_url,),
 
814
            local_url, save=True)
533
815
        self.assertEqual('foobar', branch.nick)
534
816
 
535
817
    def test_config_local_path(self):
537
819
        branch = self.make_branch('branch')
538
820
        self.assertEqual('branch', branch.nick)
539
821
 
540
 
        locations = config.locations_config_filename()
541
 
        config.ensure_config_dir_exists()
542
 
        open(locations, 'wb').write('[%s/branch]\nnickname = barry'
543
 
                                    % (osutils.getcwd().encode('utf8'),))
 
822
        local_path = osutils.getcwd().encode('utf8')
 
823
        conf = config.LocationConfig.from_string(
 
824
            '[%s/branch]\nnickname = barry' % (local_path,),
 
825
            'branch',  save=True)
544
826
        self.assertEqual('barry', branch.nick)
545
827
 
546
828
    def test_config_creates_local(self):
547
829
        """Creating a new entry in config uses a local path."""
548
830
        branch = self.make_branch('branch', format='knit')
549
831
        branch.set_push_location('http://foobar')
550
 
        locations = config.locations_config_filename()
551
832
        local_path = osutils.getcwd().encode('utf8')
552
833
        # Surprisingly ConfigObj doesn't create a trailing newline
553
 
        self.check_file_contents(locations,
 
834
        self.check_file_contents(config.locations_config_filename(),
554
835
                                 '[%s/branch]\n'
555
836
                                 'push_location = http://foobar\n'
556
837
                                 'push_location:policy = norecurse\n'
561
842
        self.assertEqual('!repo', b.get_config().get_nickname())
562
843
 
563
844
    def test_warn_if_masked(self):
564
 
        _warning = trace.warning
565
845
        warnings = []
566
846
        def warning(*args):
567
847
            warnings.append(args[0] % args[1:])
 
848
        self.overrideAttr(trace, 'warning', warning)
568
849
 
569
850
        def set_option(store, warn_masked=True):
570
851
            warnings[:] = []
576
857
            else:
577
858
                self.assertEqual(1, len(warnings))
578
859
                self.assertEqual(warning, warnings[0])
579
 
        trace.warning = warning
580
 
        try:
581
 
            branch = self.make_branch('.')
582
 
            conf = branch.get_config()
583
 
            set_option(config.STORE_GLOBAL)
584
 
            assertWarning(None)
585
 
            set_option(config.STORE_BRANCH)
586
 
            assertWarning(None)
587
 
            set_option(config.STORE_GLOBAL)
588
 
            assertWarning('Value "4" is masked by "3" from branch.conf')
589
 
            set_option(config.STORE_GLOBAL, warn_masked=False)
590
 
            assertWarning(None)
591
 
            set_option(config.STORE_LOCATION)
592
 
            assertWarning(None)
593
 
            set_option(config.STORE_BRANCH)
594
 
            assertWarning('Value "3" is masked by "0" from locations.conf')
595
 
            set_option(config.STORE_BRANCH, warn_masked=False)
596
 
            assertWarning(None)
597
 
        finally:
598
 
            trace.warning = _warning
599
 
 
600
 
 
601
 
class TestGlobalConfigItems(tests.TestCase):
 
860
        branch = self.make_branch('.')
 
861
        conf = branch.get_config()
 
862
        set_option(config.STORE_GLOBAL)
 
863
        assertWarning(None)
 
864
        set_option(config.STORE_BRANCH)
 
865
        assertWarning(None)
 
866
        set_option(config.STORE_GLOBAL)
 
867
        assertWarning('Value "4" is masked by "3" from branch.conf')
 
868
        set_option(config.STORE_GLOBAL, warn_masked=False)
 
869
        assertWarning(None)
 
870
        set_option(config.STORE_LOCATION)
 
871
        assertWarning(None)
 
872
        set_option(config.STORE_BRANCH)
 
873
        assertWarning('Value "3" is masked by "0" from locations.conf')
 
874
        set_option(config.STORE_BRANCH, warn_masked=False)
 
875
        assertWarning(None)
 
876
 
 
877
 
 
878
class TestGlobalConfigItems(tests.TestCaseInTempDir):
602
879
 
603
880
    def test_user_id(self):
604
 
        config_file = StringIO(sample_config_text.encode('utf-8'))
605
 
        my_config = config.GlobalConfig()
606
 
        my_config._parser = my_config._get_parser(file=config_file)
 
881
        my_config = config.GlobalConfig.from_string(sample_config_text)
607
882
        self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
608
883
                         my_config._get_user_id())
609
884
 
610
885
    def test_absent_user_id(self):
611
 
        config_file = StringIO("")
612
886
        my_config = config.GlobalConfig()
613
 
        my_config._parser = my_config._get_parser(file=config_file)
614
887
        self.assertEqual(None, my_config._get_user_id())
615
888
 
616
889
    def test_configured_editor(self):
617
 
        config_file = StringIO(sample_config_text.encode('utf-8'))
618
 
        my_config = config.GlobalConfig()
619
 
        my_config._parser = my_config._get_parser(file=config_file)
 
890
        my_config = config.GlobalConfig.from_string(sample_config_text)
620
891
        self.assertEqual("vim", my_config.get_editor())
621
892
 
622
893
    def test_signatures_always(self):
623
 
        config_file = StringIO(sample_always_signatures)
624
 
        my_config = config.GlobalConfig()
625
 
        my_config._parser = my_config._get_parser(file=config_file)
 
894
        my_config = config.GlobalConfig.from_string(sample_always_signatures)
626
895
        self.assertEqual(config.CHECK_NEVER,
627
896
                         my_config.signature_checking())
628
897
        self.assertEqual(config.SIGN_ALWAYS,
630
899
        self.assertEqual(True, my_config.signature_needed())
631
900
 
632
901
    def test_signatures_if_possible(self):
633
 
        config_file = StringIO(sample_maybe_signatures)
634
 
        my_config = config.GlobalConfig()
635
 
        my_config._parser = my_config._get_parser(file=config_file)
 
902
        my_config = config.GlobalConfig.from_string(sample_maybe_signatures)
636
903
        self.assertEqual(config.CHECK_NEVER,
637
904
                         my_config.signature_checking())
638
905
        self.assertEqual(config.SIGN_WHEN_REQUIRED,
640
907
        self.assertEqual(False, my_config.signature_needed())
641
908
 
642
909
    def test_signatures_ignore(self):
643
 
        config_file = StringIO(sample_ignore_signatures)
644
 
        my_config = config.GlobalConfig()
645
 
        my_config._parser = my_config._get_parser(file=config_file)
 
910
        my_config = config.GlobalConfig.from_string(sample_ignore_signatures)
646
911
        self.assertEqual(config.CHECK_ALWAYS,
647
912
                         my_config.signature_checking())
648
913
        self.assertEqual(config.SIGN_NEVER,
650
915
        self.assertEqual(False, my_config.signature_needed())
651
916
 
652
917
    def _get_sample_config(self):
653
 
        config_file = StringIO(sample_config_text.encode('utf-8'))
654
 
        my_config = config.GlobalConfig()
655
 
        my_config._parser = my_config._get_parser(file=config_file)
 
918
        my_config = config.GlobalConfig.from_string(sample_config_text)
656
919
        return my_config
657
920
 
658
921
    def test_gpg_signing_command(self):
661
924
        self.assertEqual(False, my_config.signature_needed())
662
925
 
663
926
    def _get_empty_config(self):
664
 
        config_file = StringIO("")
665
927
        my_config = config.GlobalConfig()
666
 
        my_config._parser = my_config._get_parser(file=config_file)
667
928
        return my_config
668
929
 
669
930
    def test_gpg_signing_command_unset(self):
742
1003
        self.assertIs(None, new_config.get_alias('commit'))
743
1004
 
744
1005
 
745
 
class TestLocationConfig(tests.TestCaseInTempDir):
 
1006
class TestLocationConfig(tests.TestCaseInTempDir, TestOptionsMixin):
746
1007
 
747
1008
    def test_constructs(self):
748
1009
        my_config = config.LocationConfig('http://example.com')
764
1025
        self.assertEqual(parser._calls,
765
1026
                         [('__init__', config.locations_config_filename(),
766
1027
                           'utf-8')])
767
 
        config.ensure_config_dir_exists()
768
 
        #os.mkdir(config.config_dir())
769
 
        f = file(config.branches_config_filename(), 'wb')
770
 
        f.write('')
771
 
        f.close()
772
 
        oldparserclass = config.ConfigObj
773
 
        config.ConfigObj = InstrumentedConfigObj
774
 
        try:
775
 
            my_config = config.LocationConfig('http://www.example.com')
776
 
            parser = my_config._get_parser()
777
 
        finally:
778
 
            config.ConfigObj = oldparserclass
779
1028
 
780
1029
    def test_get_global_config(self):
781
1030
        my_config = config.BranchConfig(FakeBranch('http://example.com'))
868
1117
            'http://www.example.com', 'appendpath_option'),
869
1118
            config.POLICY_APPENDPATH)
870
1119
 
 
1120
    def test__get_options_with_policy(self):
 
1121
        self.get_branch_config('/dir/subdir',
 
1122
                               location_config="""\
 
1123
[/dir]
 
1124
other_url = /other-dir
 
1125
other_url:policy = appendpath
 
1126
[/dir/subdir]
 
1127
other_url = /other-subdir
 
1128
""")
 
1129
        self.assertOptions(
 
1130
            [(u'other_url', u'/other-subdir', u'/dir/subdir', 'locations'),
 
1131
             (u'other_url', u'/other-dir', u'/dir', 'locations'),
 
1132
             (u'other_url:policy', u'appendpath', u'/dir', 'locations')],
 
1133
            self.my_location_config)
 
1134
 
871
1135
    def test_location_without_username(self):
872
1136
        self.get_branch_config('http://www.example.com/ignoreparent')
873
1137
        self.assertEqual(u'Erik B\u00e5gfors <erik@bagfors.nu>',
1009
1273
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1010
1274
                         self.my_config.post_commit())
1011
1275
 
1012
 
    def get_branch_config(self, location, global_config=None):
 
1276
    def get_branch_config(self, location, global_config=None,
 
1277
                          location_config=None):
 
1278
        my_branch = FakeBranch(location)
1013
1279
        if global_config is None:
1014
 
            global_file = StringIO(sample_config_text.encode('utf-8'))
1015
 
        else:
1016
 
            global_file = StringIO(global_config.encode('utf-8'))
1017
 
        branches_file = StringIO(sample_branches_text.encode('utf-8'))
1018
 
        self.my_config = config.BranchConfig(FakeBranch(location))
1019
 
        # Force location config to use specified file
1020
 
        self.my_location_config = self.my_config._get_location_config()
1021
 
        self.my_location_config._get_parser(branches_file)
1022
 
        # Force global config to use specified file
1023
 
        self.my_config._get_global_config()._get_parser(global_file)
 
1280
            global_config = sample_config_text
 
1281
        if location_config is None:
 
1282
            location_config = sample_branches_text
 
1283
 
 
1284
        my_global_config = config.GlobalConfig.from_string(global_config,
 
1285
                                                           save=True)
 
1286
        my_location_config = config.LocationConfig.from_string(
 
1287
            location_config, my_branch.base, save=True)
 
1288
        my_config = config.BranchConfig(my_branch)
 
1289
        self.my_config = my_config
 
1290
        self.my_location_config = my_config._get_location_config()
1024
1291
 
1025
1292
    def test_set_user_setting_sets_and_saves(self):
1026
1293
        self.get_branch_config('/a/c')
1027
1294
        record = InstrumentedConfigObj("foo")
1028
1295
        self.my_location_config._parser = record
1029
1296
 
1030
 
        real_mkdir = os.mkdir
1031
 
        self.created = False
1032
 
        def checked_mkdir(path, mode=0777):
1033
 
            self.log('making directory: %s', path)
1034
 
            real_mkdir(path, mode)
1035
 
            self.created = True
1036
 
 
1037
 
        os.mkdir = checked_mkdir
1038
 
        try:
1039
 
            self.callDeprecated(['The recurse option is deprecated as of '
1040
 
                                 '0.14.  The section "/a/c" has been '
1041
 
                                 'converted to use policies.'],
1042
 
                                self.my_config.set_user_option,
1043
 
                                'foo', 'bar', store=config.STORE_LOCATION)
1044
 
        finally:
1045
 
            os.mkdir = real_mkdir
1046
 
 
1047
 
        self.failUnless(self.created, 'Failed to create ~/.bazaar')
1048
 
        self.assertEqual([('__contains__', '/a/c'),
 
1297
        self.callDeprecated(['The recurse option is deprecated as of '
 
1298
                             '0.14.  The section "/a/c" has been '
 
1299
                             'converted to use policies.'],
 
1300
                            self.my_config.set_user_option,
 
1301
                            'foo', 'bar', store=config.STORE_LOCATION)
 
1302
        self.assertEqual([('reload',),
 
1303
                          ('__contains__', '/a/c'),
1049
1304
                          ('__contains__', '/a/c/'),
1050
1305
                          ('__setitem__', '/a/c', {}),
1051
1306
                          ('__getitem__', '/a/c'),
1080
1335
        self.assertEqual('bzr', my_config.get_bzr_remote_path())
1081
1336
        my_config.set_user_option('bzr_remote_path', '/path-bzr')
1082
1337
        self.assertEqual('/path-bzr', my_config.get_bzr_remote_path())
1083
 
        os.environ['BZR_REMOTE_PATH'] = '/environ-bzr'
 
1338
        self.overrideEnv('BZR_REMOTE_PATH', '/environ-bzr')
1084
1339
        self.assertEqual('/environ-bzr', my_config.get_bzr_remote_path())
1085
1340
 
1086
1341
 
1094
1349
option = exact
1095
1350
"""
1096
1351
 
1097
 
 
1098
1352
class TestBranchConfigItems(tests.TestCaseInTempDir):
1099
1353
 
1100
1354
    def get_branch_config(self, global_config=None, location=None,
1101
1355
                          location_config=None, branch_data_config=None):
1102
 
        my_config = config.BranchConfig(FakeBranch(location))
 
1356
        my_branch = FakeBranch(location)
1103
1357
        if global_config is not None:
1104
 
            global_file = StringIO(global_config.encode('utf-8'))
1105
 
            my_config._get_global_config()._get_parser(global_file)
1106
 
        self.my_location_config = my_config._get_location_config()
 
1358
            my_global_config = config.GlobalConfig.from_string(global_config,
 
1359
                                                               save=True)
1107
1360
        if location_config is not None:
1108
 
            location_file = StringIO(location_config.encode('utf-8'))
1109
 
            self.my_location_config._get_parser(location_file)
 
1361
            my_location_config = config.LocationConfig.from_string(
 
1362
                location_config, my_branch.base, save=True)
 
1363
        my_config = config.BranchConfig(my_branch)
1110
1364
        if branch_data_config is not None:
1111
1365
            my_config.branch.control_files.files['branch.conf'] = \
1112
1366
                branch_data_config
1126
1380
                         my_config.username())
1127
1381
 
1128
1382
    def test_not_set_in_branch(self):
1129
 
        my_config = self.get_branch_config(sample_config_text)
 
1383
        my_config = self.get_branch_config(global_config=sample_config_text)
1130
1384
        self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
1131
1385
                         my_config._get_user_id())
1132
1386
        my_config.branch.control_files.files['email'] = "John"
1133
1387
        self.assertEqual("John", my_config._get_user_id())
1134
1388
 
1135
1389
    def test_BZR_EMAIL_OVERRIDES(self):
1136
 
        os.environ['BZR_EMAIL'] = "Robert Collins <robertc@example.org>"
 
1390
        self.overrideEnv('BZR_EMAIL', "Robert Collins <robertc@example.org>")
1137
1391
        branch = FakeBranch()
1138
1392
        my_config = config.BranchConfig(branch)
1139
1393
        self.assertEqual("Robert Collins <robertc@example.org>",
1156
1410
 
1157
1411
    def test_gpg_signing_command(self):
1158
1412
        my_config = self.get_branch_config(
 
1413
            global_config=sample_config_text,
1159
1414
            # branch data cannot set gpg_signing_command
1160
1415
            branch_data_config="gpg_signing_command=pgp")
1161
 
        config_file = StringIO(sample_config_text.encode('utf-8'))
1162
 
        my_config._get_global_config()._get_parser(config_file)
1163
1416
        self.assertEqual('gnome-gpg', my_config.gpg_signing_command())
1164
1417
 
1165
1418
    def test_get_user_option_global(self):
1166
 
        branch = FakeBranch()
1167
 
        my_config = config.BranchConfig(branch)
1168
 
        config_file = StringIO(sample_config_text.encode('utf-8'))
1169
 
        (my_config._get_global_config()._get_parser(config_file))
 
1419
        my_config = self.get_branch_config(global_config=sample_config_text)
1170
1420
        self.assertEqual('something',
1171
1421
                         my_config.get_user_option('user_global_option'))
1172
1422
 
1173
1423
    def test_post_commit_default(self):
1174
 
        branch = FakeBranch()
1175
 
        my_config = self.get_branch_config(sample_config_text, '/a/c',
1176
 
                                           sample_branches_text)
 
1424
        my_config = self.get_branch_config(global_config=sample_config_text,
 
1425
                                      location='/a/c',
 
1426
                                      location_config=sample_branches_text)
1177
1427
        self.assertEqual(my_config.branch.base, '/a/c')
1178
1428
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1179
1429
                         my_config.post_commit())
1180
1430
        my_config.set_user_option('post_commit', 'rmtree_root')
1181
 
        # post-commit is ignored when bresent in branch data
 
1431
        # post-commit is ignored when present in branch data
1182
1432
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1183
1433
                         my_config.post_commit())
1184
1434
        my_config.set_user_option('post_commit', 'rmtree_root',
1186
1436
        self.assertEqual('rmtree_root', my_config.post_commit())
1187
1437
 
1188
1438
    def test_config_precedence(self):
 
1439
        # FIXME: eager test, luckily no persitent config file makes it fail
 
1440
        # -- vila 20100716
1189
1441
        my_config = self.get_branch_config(global_config=precedence_global)
1190
1442
        self.assertEqual(my_config.get_user_option('option'), 'global')
1191
1443
        my_config = self.get_branch_config(global_config=precedence_global,
1192
 
                                      branch_data_config=precedence_branch)
 
1444
                                           branch_data_config=precedence_branch)
1193
1445
        self.assertEqual(my_config.get_user_option('option'), 'branch')
1194
 
        my_config = self.get_branch_config(global_config=precedence_global,
1195
 
                                      branch_data_config=precedence_branch,
1196
 
                                      location_config=precedence_location)
 
1446
        my_config = self.get_branch_config(
 
1447
            global_config=precedence_global,
 
1448
            branch_data_config=precedence_branch,
 
1449
            location_config=precedence_location)
1197
1450
        self.assertEqual(my_config.get_user_option('option'), 'recurse')
1198
 
        my_config = self.get_branch_config(global_config=precedence_global,
1199
 
                                      branch_data_config=precedence_branch,
1200
 
                                      location_config=precedence_location,
1201
 
                                      location='http://example.com/specific')
 
1451
        my_config = self.get_branch_config(
 
1452
            global_config=precedence_global,
 
1453
            branch_data_config=precedence_branch,
 
1454
            location_config=precedence_location,
 
1455
            location='http://example.com/specific')
1202
1456
        self.assertEqual(my_config.get_user_option('option'), 'exact')
1203
1457
 
1204
1458
    def test_get_mail_client(self):
1332
1586
        self.assertIs(None, bzrdir_config.get_default_stack_on())
1333
1587
 
1334
1588
 
 
1589
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
 
1590
 
 
1591
    def setUp(self):
 
1592
        super(TestConfigGetOptions, self).setUp()
 
1593
        create_configs(self)
 
1594
 
 
1595
    # One variable in none of the above
 
1596
    def test_no_variable(self):
 
1597
        # Using branch should query branch, locations and bazaar
 
1598
        self.assertOptions([], self.branch_config)
 
1599
 
 
1600
    def test_option_in_bazaar(self):
 
1601
        self.bazaar_config.set_user_option('file', 'bazaar')
 
1602
        self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
 
1603
                           self.bazaar_config)
 
1604
 
 
1605
    def test_option_in_locations(self):
 
1606
        self.locations_config.set_user_option('file', 'locations')
 
1607
        self.assertOptions(
 
1608
            [('file', 'locations', self.tree.basedir, 'locations')],
 
1609
            self.locations_config)
 
1610
 
 
1611
    def test_option_in_branch(self):
 
1612
        self.branch_config.set_user_option('file', 'branch')
 
1613
        self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
 
1614
                           self.branch_config)
 
1615
 
 
1616
    def test_option_in_bazaar_and_branch(self):
 
1617
        self.bazaar_config.set_user_option('file', 'bazaar')
 
1618
        self.branch_config.set_user_option('file', 'branch')
 
1619
        self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
 
1620
                            ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
 
1621
                           self.branch_config)
 
1622
 
 
1623
    def test_option_in_branch_and_locations(self):
 
1624
        # Hmm, locations override branch :-/
 
1625
        self.locations_config.set_user_option('file', 'locations')
 
1626
        self.branch_config.set_user_option('file', 'branch')
 
1627
        self.assertOptions(
 
1628
            [('file', 'locations', self.tree.basedir, 'locations'),
 
1629
             ('file', 'branch', 'DEFAULT', 'branch'),],
 
1630
            self.branch_config)
 
1631
 
 
1632
    def test_option_in_bazaar_locations_and_branch(self):
 
1633
        self.bazaar_config.set_user_option('file', 'bazaar')
 
1634
        self.locations_config.set_user_option('file', 'locations')
 
1635
        self.branch_config.set_user_option('file', 'branch')
 
1636
        self.assertOptions(
 
1637
            [('file', 'locations', self.tree.basedir, 'locations'),
 
1638
             ('file', 'branch', 'DEFAULT', 'branch'),
 
1639
             ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
 
1640
            self.branch_config)
 
1641
 
 
1642
 
 
1643
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
 
1644
 
 
1645
    def setUp(self):
 
1646
        super(TestConfigRemoveOption, self).setUp()
 
1647
        create_configs_with_file_option(self)
 
1648
 
 
1649
    def test_remove_in_locations(self):
 
1650
        self.locations_config.remove_user_option('file', self.tree.basedir)
 
1651
        self.assertOptions(
 
1652
            [('file', 'branch', 'DEFAULT', 'branch'),
 
1653
             ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
 
1654
            self.branch_config)
 
1655
 
 
1656
    def test_remove_in_branch(self):
 
1657
        self.branch_config.remove_user_option('file')
 
1658
        self.assertOptions(
 
1659
            [('file', 'locations', self.tree.basedir, 'locations'),
 
1660
             ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
 
1661
            self.branch_config)
 
1662
 
 
1663
    def test_remove_in_bazaar(self):
 
1664
        self.bazaar_config.remove_user_option('file')
 
1665
        self.assertOptions(
 
1666
            [('file', 'locations', self.tree.basedir, 'locations'),
 
1667
             ('file', 'branch', 'DEFAULT', 'branch'),],
 
1668
            self.branch_config)
 
1669
 
 
1670
 
 
1671
class TestConfigGetSections(tests.TestCaseWithTransport):
 
1672
 
 
1673
    def setUp(self):
 
1674
        super(TestConfigGetSections, self).setUp()
 
1675
        create_configs(self)
 
1676
 
 
1677
    def assertSectionNames(self, expected, conf, name=None):
 
1678
        """Check which sections are returned for a given config.
 
1679
 
 
1680
        If fallback configurations exist their sections can be included.
 
1681
 
 
1682
        :param expected: A list of section names.
 
1683
 
 
1684
        :param conf: The configuration that will be queried.
 
1685
 
 
1686
        :param name: An optional section name that will be passed to
 
1687
            get_sections().
 
1688
        """
 
1689
        sections = list(conf._get_sections(name))
 
1690
        self.assertLength(len(expected), sections)
 
1691
        self.assertEqual(expected, [name for name, _, _ in sections])
 
1692
 
 
1693
    def test_bazaar_default_section(self):
 
1694
        self.assertSectionNames(['DEFAULT'], self.bazaar_config)
 
1695
 
 
1696
    def test_locations_default_section(self):
 
1697
        # No sections are defined in an empty file
 
1698
        self.assertSectionNames([], self.locations_config)
 
1699
 
 
1700
    def test_locations_named_section(self):
 
1701
        self.locations_config.set_user_option('file', 'locations')
 
1702
        self.assertSectionNames([self.tree.basedir], self.locations_config)
 
1703
 
 
1704
    def test_locations_matching_sections(self):
 
1705
        loc_config = self.locations_config
 
1706
        loc_config.set_user_option('file', 'locations')
 
1707
        # We need to cheat a bit here to create an option in sections above and
 
1708
        # below the 'location' one.
 
1709
        parser = loc_config._get_parser()
 
1710
        # locations.cong deals with '/' ignoring native os.sep
 
1711
        location_names = self.tree.basedir.split('/')
 
1712
        parent = '/'.join(location_names[:-1])
 
1713
        child = '/'.join(location_names + ['child'])
 
1714
        parser[parent] = {}
 
1715
        parser[parent]['file'] = 'parent'
 
1716
        parser[child] = {}
 
1717
        parser[child]['file'] = 'child'
 
1718
        self.assertSectionNames([self.tree.basedir, parent], loc_config)
 
1719
 
 
1720
    def test_branch_data_default_section(self):
 
1721
        self.assertSectionNames([None],
 
1722
                                self.branch_config._get_branch_data_config())
 
1723
 
 
1724
    def test_branch_default_sections(self):
 
1725
        # No sections are defined in an empty locations file
 
1726
        self.assertSectionNames([None, 'DEFAULT'],
 
1727
                                self.branch_config)
 
1728
        # Unless we define an option
 
1729
        self.branch_config._get_location_config().set_user_option(
 
1730
            'file', 'locations')
 
1731
        self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
 
1732
                                self.branch_config)
 
1733
 
 
1734
    def test_bazaar_named_section(self):
 
1735
        # We need to cheat as the API doesn't give direct access to sections
 
1736
        # other than DEFAULT.
 
1737
        self.bazaar_config.set_alias('bazaar', 'bzr')
 
1738
        self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
 
1739
 
 
1740
 
1335
1741
class TestAuthenticationConfigFile(tests.TestCase):
1336
1742
    """Test the authentication.conf file matching"""
1337
1743