~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_config.py

  • Committer: Max Bowsher
  • Date: 2011-02-01 01:34:58 UTC
  • mto: This revision was merged to the branch mainline in revision 5639.
  • Revision ID: maxb@f2s.com-20110201013458-kkp1y64bvce58ziq
PPA documentation update.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2010 Canonical Ltd
 
1
# Copyright (C) 2005-2011 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 (
29
33
    errors,
30
34
    osutils,
31
35
    mail_client,
 
36
    mergetools,
32
37
    ui,
33
38
    urlutils,
34
39
    tests,
35
40
    trace,
36
41
    transport,
37
42
    )
 
43
from bzrlib.tests import (
 
44
    features,
 
45
    scenarios,
 
46
    )
38
47
from bzrlib.util.configobj import configobj
39
48
 
40
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
 
41
65
sample_long_alias="log -r-15..-1 --line"
42
66
sample_config_text = u"""
43
67
[DEFAULT]
47
71
gpg_signing_command=gnome-gpg
48
72
log_format=short
49
73
user_global_option=something
 
74
bzr.mergetool.sometool=sometool {base} {this} {other} -o {result}
 
75
bzr.mergetool.funkytool=funkytool "arg with spaces" {this_temp}
 
76
bzr.default_mergetool=sometool
50
77
[ALIASES]
51
78
h=help
52
79
ll=""" + sample_long_alias + "\n"
105
132
"""
106
133
 
107
134
 
 
135
def create_configs(test):
 
136
    """Create configuration files for a given test.
 
137
 
 
138
    This requires creating a tree (and populate the ``test.tree`` attribute)
 
139
    and its associated branch and will populate the following attributes:
 
140
 
 
141
    - branch_config: A BranchConfig for the associated branch.
 
142
 
 
143
    - locations_config : A LocationConfig for the associated branch
 
144
 
 
145
    - bazaar_config: A GlobalConfig.
 
146
 
 
147
    The tree and branch are created in a 'tree' subdirectory so the tests can
 
148
    still use the test directory to stay outside of the branch.
 
149
    """
 
150
    tree = test.make_branch_and_tree('tree')
 
151
    test.tree = tree
 
152
    test.branch_config = config.BranchConfig(tree.branch)
 
153
    test.locations_config = config.LocationConfig(tree.basedir)
 
154
    test.bazaar_config = config.GlobalConfig()
 
155
 
 
156
 
 
157
def create_configs_with_file_option(test):
 
158
    """Create configuration files with a ``file`` option set in each.
 
159
 
 
160
    This builds on ``create_configs`` and add one ``file`` option in each
 
161
    configuration with a value which allows identifying the configuration file.
 
162
    """
 
163
    create_configs(test)
 
164
    test.bazaar_config.set_user_option('file', 'bazaar')
 
165
    test.locations_config.set_user_option('file', 'locations')
 
166
    test.branch_config.set_user_option('file', 'branch')
 
167
 
 
168
 
 
169
class TestOptionsMixin:
 
170
 
 
171
    def assertOptions(self, expected, conf):
 
172
        # We don't care about the parser (as it will make tests hard to write
 
173
        # and error-prone anyway)
 
174
        self.assertThat([opt[:4] for opt in conf._get_options()],
 
175
                        matchers.Equals(expected))
 
176
 
 
177
 
108
178
class InstrumentedConfigObj(object):
109
179
    """A config obj look-enough-alike to record calls made to it."""
110
180
 
129
199
        self._calls.append(('keys',))
130
200
        return []
131
201
 
 
202
    def reload(self):
 
203
        self._calls.append(('reload',))
 
204
 
132
205
    def write(self, arg):
133
206
        self._calls.append(('write',))
134
207
 
333
406
 
334
407
    def setUp(self):
335
408
        super(TestConfigPath, self).setUp()
336
 
        os.environ['HOME'] = '/home/bogus'
337
 
        os.environ['XDG_CACHE_DIR'] = ''
 
409
        self.overrideEnv('HOME', '/home/bogus')
 
410
        self.overrideEnv('XDG_CACHE_DIR', '')
338
411
        if sys.platform == 'win32':
339
 
            os.environ['BZR_HOME'] = \
340
 
                r'C:\Documents and Settings\bogus\Application Data'
 
412
            self.overrideEnv(
 
413
                'BZR_HOME', r'C:\Documents and Settings\bogus\Application Data')
341
414
            self.bzr_home = \
342
415
                'C:/Documents and Settings/bogus/Application Data/bazaar/2.0'
343
416
        else:
350
423
        self.assertEqual(config.config_filename(),
351
424
                         self.bzr_home + '/bazaar.conf')
352
425
 
353
 
    def test_branches_config_filename(self):
354
 
        self.assertEqual(config.branches_config_filename(),
355
 
                         self.bzr_home + '/branches.conf')
356
 
 
357
426
    def test_locations_config_filename(self):
358
427
        self.assertEqual(config.locations_config_filename(),
359
428
                         self.bzr_home + '/locations.conf')
367
436
            '/home/bogus/.cache')
368
437
 
369
438
 
370
 
class TestIniConfig(tests.TestCase):
 
439
class TestXDGConfigDir(tests.TestCaseInTempDir):
 
440
    # must be in temp dir because config tests for the existence of the bazaar
 
441
    # subdirectory of $XDG_CONFIG_HOME
 
442
 
 
443
    def setUp(self):
 
444
        if sys.platform in ('darwin', 'win32'):
 
445
            raise tests.TestNotApplicable(
 
446
                'XDG config dir not used on this platform')
 
447
        super(TestXDGConfigDir, self).setUp()
 
448
        self.overrideEnv('HOME', self.test_home_dir)
 
449
        # BZR_HOME overrides everything we want to test so unset it.
 
450
        self.overrideEnv('BZR_HOME', None)
 
451
 
 
452
    def test_xdg_config_dir_exists(self):
 
453
        """When ~/.config/bazaar exists, use it as the config dir."""
 
454
        newdir = osutils.pathjoin(self.test_home_dir, '.config', 'bazaar')
 
455
        os.makedirs(newdir)
 
456
        self.assertEqual(config.config_dir(), newdir)
 
457
 
 
458
    def test_xdg_config_home(self):
 
459
        """When XDG_CONFIG_HOME is set, use it."""
 
460
        xdgconfigdir = osutils.pathjoin(self.test_home_dir, 'xdgconfig')
 
461
        self.overrideEnv('XDG_CONFIG_HOME', xdgconfigdir)
 
462
        newdir = osutils.pathjoin(xdgconfigdir, 'bazaar')
 
463
        os.makedirs(newdir)
 
464
        self.assertEqual(config.config_dir(), newdir)
 
465
 
 
466
 
 
467
class TestIniConfig(tests.TestCaseInTempDir):
371
468
 
372
469
    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
 
470
        conf = config.IniBasedConfig.from_string(s)
 
471
        return conf, conf._get_parser()
376
472
 
377
473
 
378
474
class TestIniConfigBuilding(TestIniConfig):
379
475
 
380
476
    def test_contructs(self):
381
 
        my_config = config.IniBasedConfig("nothing")
 
477
        my_config = config.IniBasedConfig()
382
478
 
383
479
    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))
 
480
        my_config = config.IniBasedConfig.from_string(sample_config_text)
 
481
        self.assertIsInstance(my_config._get_parser(), configobj.ConfigObj)
389
482
 
390
483
    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)
 
484
        my_config = config.IniBasedConfig.from_string(sample_config_text)
 
485
        parser = my_config._get_parser()
394
486
        self.failUnless(my_config._get_parser() is parser)
395
487
 
 
488
    def _dummy_chown(self, path, uid, gid):
 
489
        self.path, self.uid, self.gid = path, uid, gid
 
490
 
 
491
    def test_ini_config_ownership(self):
 
492
        """Ensure that chown is happening during _write_config_file"""
 
493
        self.requireFeature(features.chown_feature)
 
494
        self.overrideAttr(os, 'chown', self._dummy_chown)
 
495
        self.path = self.uid = self.gid = None
 
496
        conf = config.IniBasedConfig(file_name='./foo.conf')
 
497
        conf._write_config_file()
 
498
        self.assertEquals(self.path, './foo.conf')
 
499
        self.assertTrue(isinstance(self.uid, int))
 
500
        self.assertTrue(isinstance(self.gid, int))
 
501
 
 
502
    def test_get_filename_parameter_is_deprecated_(self):
 
503
        conf = self.callDeprecated([
 
504
            'IniBasedConfig.__init__(get_filename) was deprecated in 2.3.'
 
505
            ' Use file_name instead.'],
 
506
            config.IniBasedConfig, lambda: 'ini.conf')
 
507
        self.assertEqual('ini.conf', conf.file_name)
 
508
 
 
509
    def test_get_parser_file_parameter_is_deprecated_(self):
 
510
        config_file = StringIO(sample_config_text.encode('utf-8'))
 
511
        conf = config.IniBasedConfig.from_string(sample_config_text)
 
512
        conf = self.callDeprecated([
 
513
            'IniBasedConfig._get_parser(file=xxx) was deprecated in 2.3.'
 
514
            ' Use IniBasedConfig(_content=xxx) instead.'],
 
515
            conf._get_parser, file=config_file)
 
516
 
 
517
class TestIniConfigSaving(tests.TestCaseInTempDir):
 
518
 
 
519
    def test_cant_save_without_a_file_name(self):
 
520
        conf = config.IniBasedConfig()
 
521
        self.assertRaises(AssertionError, conf._write_config_file)
 
522
 
 
523
    def test_saved_with_content(self):
 
524
        content = 'foo = bar\n'
 
525
        conf = config.IniBasedConfig.from_string(
 
526
            content, file_name='./test.conf', save=True)
 
527
        self.assertFileEqual(content, 'test.conf')
 
528
 
 
529
 
 
530
class TestIniBaseConfigOnDisk(tests.TestCaseInTempDir):
 
531
 
 
532
    def test_cannot_reload_without_name(self):
 
533
        conf = config.IniBasedConfig.from_string(sample_config_text)
 
534
        self.assertRaises(AssertionError, conf.reload)
 
535
 
 
536
    def test_reload_see_new_value(self):
 
537
        c1 = config.IniBasedConfig.from_string('editor=vim\n',
 
538
                                               file_name='./test/conf')
 
539
        c1._write_config_file()
 
540
        c2 = config.IniBasedConfig.from_string('editor=emacs\n',
 
541
                                               file_name='./test/conf')
 
542
        c2._write_config_file()
 
543
        self.assertEqual('vim', c1.get_user_option('editor'))
 
544
        self.assertEqual('emacs', c2.get_user_option('editor'))
 
545
        # Make sure we get the Right value
 
546
        c1.reload()
 
547
        self.assertEqual('emacs', c1.get_user_option('editor'))
 
548
 
 
549
 
 
550
class TestLockableConfig(tests.TestCaseInTempDir):
 
551
 
 
552
    scenarios = lockable_config_scenarios()
 
553
 
 
554
    # Set by load_tests
 
555
    config_class = None
 
556
    config_args = None
 
557
    config_section = None
 
558
 
 
559
    def setUp(self):
 
560
        super(TestLockableConfig, self).setUp()
 
561
        self._content = '[%s]\none=1\ntwo=2\n' % (self.config_section,)
 
562
        self.config = self.create_config(self._content)
 
563
 
 
564
    def get_existing_config(self):
 
565
        return self.config_class(*self.config_args)
 
566
 
 
567
    def create_config(self, content):
 
568
        kwargs = dict(save=True)
 
569
        c = self.config_class.from_string(content, *self.config_args, **kwargs)
 
570
        return c
 
571
 
 
572
    def test_simple_read_access(self):
 
573
        self.assertEquals('1', self.config.get_user_option('one'))
 
574
 
 
575
    def test_simple_write_access(self):
 
576
        self.config.set_user_option('one', 'one')
 
577
        self.assertEquals('one', self.config.get_user_option('one'))
 
578
 
 
579
    def test_listen_to_the_last_speaker(self):
 
580
        c1 = self.config
 
581
        c2 = self.get_existing_config()
 
582
        c1.set_user_option('one', 'ONE')
 
583
        c2.set_user_option('two', 'TWO')
 
584
        self.assertEquals('ONE', c1.get_user_option('one'))
 
585
        self.assertEquals('TWO', c2.get_user_option('two'))
 
586
        # The second update respect the first one
 
587
        self.assertEquals('ONE', c2.get_user_option('one'))
 
588
 
 
589
    def test_last_speaker_wins(self):
 
590
        # If the same config is not shared, the same variable modified twice
 
591
        # can only see a single result.
 
592
        c1 = self.config
 
593
        c2 = self.get_existing_config()
 
594
        c1.set_user_option('one', 'c1')
 
595
        c2.set_user_option('one', 'c2')
 
596
        self.assertEquals('c2', c2._get_user_option('one'))
 
597
        # The first modification is still available until another refresh
 
598
        # occur
 
599
        self.assertEquals('c1', c1._get_user_option('one'))
 
600
        c1.set_user_option('two', 'done')
 
601
        self.assertEquals('c2', c1._get_user_option('one'))
 
602
 
 
603
    def test_writes_are_serialized(self):
 
604
        c1 = self.config
 
605
        c2 = self.get_existing_config()
 
606
 
 
607
        # We spawn a thread that will pause *during* the write
 
608
        before_writing = threading.Event()
 
609
        after_writing = threading.Event()
 
610
        writing_done = threading.Event()
 
611
        c1_orig = c1._write_config_file
 
612
        def c1_write_config_file():
 
613
            before_writing.set()
 
614
            c1_orig()
 
615
            # The lock is held we wait for the main thread to decide when to
 
616
            # continue
 
617
            after_writing.wait()
 
618
        c1._write_config_file = c1_write_config_file
 
619
        def c1_set_option():
 
620
            c1.set_user_option('one', 'c1')
 
621
            writing_done.set()
 
622
        t1 = threading.Thread(target=c1_set_option)
 
623
        # Collect the thread after the test
 
624
        self.addCleanup(t1.join)
 
625
        # Be ready to unblock the thread if the test goes wrong
 
626
        self.addCleanup(after_writing.set)
 
627
        t1.start()
 
628
        before_writing.wait()
 
629
        self.assertTrue(c1._lock.is_held)
 
630
        self.assertRaises(errors.LockContention,
 
631
                          c2.set_user_option, 'one', 'c2')
 
632
        self.assertEquals('c1', c1.get_user_option('one'))
 
633
        # Let the lock be released
 
634
        after_writing.set()
 
635
        writing_done.wait()
 
636
        c2.set_user_option('one', 'c2')
 
637
        self.assertEquals('c2', c2.get_user_option('one'))
 
638
 
 
639
    def test_read_while_writing(self):
 
640
       c1 = self.config
 
641
       # We spawn a thread that will pause *during* the write
 
642
       ready_to_write = threading.Event()
 
643
       do_writing = threading.Event()
 
644
       writing_done = threading.Event()
 
645
       c1_orig = c1._write_config_file
 
646
       def c1_write_config_file():
 
647
           ready_to_write.set()
 
648
           # The lock is held we wait for the main thread to decide when to
 
649
           # continue
 
650
           do_writing.wait()
 
651
           c1_orig()
 
652
           writing_done.set()
 
653
       c1._write_config_file = c1_write_config_file
 
654
       def c1_set_option():
 
655
           c1.set_user_option('one', 'c1')
 
656
       t1 = threading.Thread(target=c1_set_option)
 
657
       # Collect the thread after the test
 
658
       self.addCleanup(t1.join)
 
659
       # Be ready to unblock the thread if the test goes wrong
 
660
       self.addCleanup(do_writing.set)
 
661
       t1.start()
 
662
       # Ensure the thread is ready to write
 
663
       ready_to_write.wait()
 
664
       self.assertTrue(c1._lock.is_held)
 
665
       self.assertEquals('c1', c1.get_user_option('one'))
 
666
       # If we read during the write, we get the old value
 
667
       c2 = self.get_existing_config()
 
668
       self.assertEquals('1', c2.get_user_option('one'))
 
669
       # Let the writing occur and ensure it occurred
 
670
       do_writing.set()
 
671
       writing_done.wait()
 
672
       # Now we get the updated value
 
673
       c3 = self.get_existing_config()
 
674
       self.assertEquals('c1', c3.get_user_option('one'))
 
675
 
396
676
 
397
677
class TestGetUserOptionAs(TestIniConfig):
398
678
 
406
686
        get_bool = conf.get_user_option_as_bool
407
687
        self.assertEqual(True, get_bool('a_true_bool'))
408
688
        self.assertEqual(False, get_bool('a_false_bool'))
 
689
        warnings = []
 
690
        def warning(*args):
 
691
            warnings.append(args[0] % args[1:])
 
692
        self.overrideAttr(trace, 'warning', warning)
 
693
        msg = 'Value "%s" is not a boolean for "%s"'
409
694
        self.assertIs(None, get_bool('an_invalid_bool'))
 
695
        self.assertEquals(msg % ('maybe', 'an_invalid_bool'), warnings[0])
 
696
        warnings = []
410
697
        self.assertIs(None, get_bool('not_defined_in_this_config'))
411
 
 
 
698
        self.assertEquals([], warnings)
412
699
 
413
700
    def test_get_user_option_as_list(self):
414
701
        conf, parser = self.make_config_parser("""
498
785
        branch = self.make_branch('branch')
499
786
        self.assertEqual('branch', branch.nick)
500
787
 
501
 
        locations = config.locations_config_filename()
502
 
        config.ensure_config_dir_exists()
503
788
        local_url = urlutils.local_path_to_url('branch')
504
 
        open(locations, 'wb').write('[%s]\nnickname = foobar'
505
 
                                    % (local_url,))
 
789
        conf = config.LocationConfig.from_string(
 
790
            '[%s]\nnickname = foobar' % (local_url,),
 
791
            local_url, save=True)
506
792
        self.assertEqual('foobar', branch.nick)
507
793
 
508
794
    def test_config_local_path(self):
510
796
        branch = self.make_branch('branch')
511
797
        self.assertEqual('branch', branch.nick)
512
798
 
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'),))
 
799
        local_path = osutils.getcwd().encode('utf8')
 
800
        conf = config.LocationConfig.from_string(
 
801
            '[%s/branch]\nnickname = barry' % (local_path,),
 
802
            'branch',  save=True)
517
803
        self.assertEqual('barry', branch.nick)
518
804
 
519
805
    def test_config_creates_local(self):
520
806
        """Creating a new entry in config uses a local path."""
521
807
        branch = self.make_branch('branch', format='knit')
522
808
        branch.set_push_location('http://foobar')
523
 
        locations = config.locations_config_filename()
524
809
        local_path = osutils.getcwd().encode('utf8')
525
810
        # Surprisingly ConfigObj doesn't create a trailing newline
526
 
        self.check_file_contents(locations,
 
811
        self.check_file_contents(config.locations_config_filename(),
527
812
                                 '[%s/branch]\n'
528
813
                                 'push_location = http://foobar\n'
529
814
                                 'push_location:policy = norecurse\n'
534
819
        self.assertEqual('!repo', b.get_config().get_nickname())
535
820
 
536
821
    def test_warn_if_masked(self):
537
 
        _warning = trace.warning
538
822
        warnings = []
539
823
        def warning(*args):
540
824
            warnings.append(args[0] % args[1:])
 
825
        self.overrideAttr(trace, 'warning', warning)
541
826
 
542
827
        def set_option(store, warn_masked=True):
543
828
            warnings[:] = []
549
834
            else:
550
835
                self.assertEqual(1, len(warnings))
551
836
                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):
 
837
        branch = self.make_branch('.')
 
838
        conf = branch.get_config()
 
839
        set_option(config.STORE_GLOBAL)
 
840
        assertWarning(None)
 
841
        set_option(config.STORE_BRANCH)
 
842
        assertWarning(None)
 
843
        set_option(config.STORE_GLOBAL)
 
844
        assertWarning('Value "4" is masked by "3" from branch.conf')
 
845
        set_option(config.STORE_GLOBAL, warn_masked=False)
 
846
        assertWarning(None)
 
847
        set_option(config.STORE_LOCATION)
 
848
        assertWarning(None)
 
849
        set_option(config.STORE_BRANCH)
 
850
        assertWarning('Value "3" is masked by "0" from locations.conf')
 
851
        set_option(config.STORE_BRANCH, warn_masked=False)
 
852
        assertWarning(None)
 
853
 
 
854
 
 
855
class TestGlobalConfigItems(tests.TestCaseInTempDir):
575
856
 
576
857
    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)
 
858
        my_config = config.GlobalConfig.from_string(sample_config_text)
580
859
        self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
581
860
                         my_config._get_user_id())
582
861
 
583
862
    def test_absent_user_id(self):
584
 
        config_file = StringIO("")
585
863
        my_config = config.GlobalConfig()
586
 
        my_config._parser = my_config._get_parser(file=config_file)
587
864
        self.assertEqual(None, my_config._get_user_id())
588
865
 
589
866
    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)
 
867
        my_config = config.GlobalConfig.from_string(sample_config_text)
593
868
        self.assertEqual("vim", my_config.get_editor())
594
869
 
595
870
    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)
 
871
        my_config = config.GlobalConfig.from_string(sample_always_signatures)
599
872
        self.assertEqual(config.CHECK_NEVER,
600
873
                         my_config.signature_checking())
601
874
        self.assertEqual(config.SIGN_ALWAYS,
603
876
        self.assertEqual(True, my_config.signature_needed())
604
877
 
605
878
    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)
 
879
        my_config = config.GlobalConfig.from_string(sample_maybe_signatures)
609
880
        self.assertEqual(config.CHECK_NEVER,
610
881
                         my_config.signature_checking())
611
882
        self.assertEqual(config.SIGN_WHEN_REQUIRED,
613
884
        self.assertEqual(False, my_config.signature_needed())
614
885
 
615
886
    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)
 
887
        my_config = config.GlobalConfig.from_string(sample_ignore_signatures)
619
888
        self.assertEqual(config.CHECK_ALWAYS,
620
889
                         my_config.signature_checking())
621
890
        self.assertEqual(config.SIGN_NEVER,
623
892
        self.assertEqual(False, my_config.signature_needed())
624
893
 
625
894
    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)
 
895
        my_config = config.GlobalConfig.from_string(sample_config_text)
629
896
        return my_config
630
897
 
631
898
    def test_gpg_signing_command(self):
634
901
        self.assertEqual(False, my_config.signature_needed())
635
902
 
636
903
    def _get_empty_config(self):
637
 
        config_file = StringIO("")
638
904
        my_config = config.GlobalConfig()
639
 
        my_config._parser = my_config._get_parser(file=config_file)
640
905
        return my_config
641
906
 
642
907
    def test_gpg_signing_command_unset(self):
692
957
        change_editor = my_config.get_change_editor('old', 'new')
693
958
        self.assertIs(None, change_editor)
694
959
 
 
960
    def test_get_merge_tools(self):
 
961
        conf = self._get_sample_config()
 
962
        tools = conf.get_merge_tools()
 
963
        self.log(repr(tools))
 
964
        self.assertEqual(
 
965
            {u'funkytool' : u'funkytool "arg with spaces" {this_temp}',
 
966
            u'sometool' : u'sometool {base} {this} {other} -o {result}'},
 
967
            tools)
 
968
 
 
969
    def test_get_merge_tools_empty(self):
 
970
        conf = self._get_empty_config()
 
971
        tools = conf.get_merge_tools()
 
972
        self.assertEqual({}, tools)
 
973
 
 
974
    def test_find_merge_tool(self):
 
975
        conf = self._get_sample_config()
 
976
        cmdline = conf.find_merge_tool('sometool')
 
977
        self.assertEqual('sometool {base} {this} {other} -o {result}', cmdline)
 
978
 
 
979
    def test_find_merge_tool_not_found(self):
 
980
        conf = self._get_sample_config()
 
981
        cmdline = conf.find_merge_tool('DOES NOT EXIST')
 
982
        self.assertIs(cmdline, None)
 
983
 
 
984
    def test_find_merge_tool_known(self):
 
985
        conf = self._get_empty_config()
 
986
        cmdline = conf.find_merge_tool('kdiff3')
 
987
        self.assertEquals('kdiff3 {base} {this} {other} -o {result}', cmdline)
 
988
        
 
989
    def test_find_merge_tool_override_known(self):
 
990
        conf = self._get_empty_config()
 
991
        conf.set_user_option('bzr.mergetool.kdiff3', 'kdiff3 blah')
 
992
        cmdline = conf.find_merge_tool('kdiff3')
 
993
        self.assertEqual('kdiff3 blah', cmdline)
 
994
 
695
995
 
696
996
class TestGlobalConfigSavingOptions(tests.TestCaseInTempDir):
697
997
 
715
1015
        self.assertIs(None, new_config.get_alias('commit'))
716
1016
 
717
1017
 
718
 
class TestLocationConfig(tests.TestCaseInTempDir):
 
1018
class TestLocationConfig(tests.TestCaseInTempDir, TestOptionsMixin):
719
1019
 
720
1020
    def test_constructs(self):
721
1021
        my_config = config.LocationConfig('http://example.com')
737
1037
        self.assertEqual(parser._calls,
738
1038
                         [('__init__', config.locations_config_filename(),
739
1039
                           '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
1040
 
753
1041
    def test_get_global_config(self):
754
1042
        my_config = config.BranchConfig(FakeBranch('http://example.com'))
841
1129
            'http://www.example.com', 'appendpath_option'),
842
1130
            config.POLICY_APPENDPATH)
843
1131
 
 
1132
    def test__get_options_with_policy(self):
 
1133
        self.get_branch_config('/dir/subdir',
 
1134
                               location_config="""\
 
1135
[/dir]
 
1136
other_url = /other-dir
 
1137
other_url:policy = appendpath
 
1138
[/dir/subdir]
 
1139
other_url = /other-subdir
 
1140
""")
 
1141
        self.assertOptions(
 
1142
            [(u'other_url', u'/other-subdir', u'/dir/subdir', 'locations'),
 
1143
             (u'other_url', u'/other-dir', u'/dir', 'locations'),
 
1144
             (u'other_url:policy', u'appendpath', u'/dir', 'locations')],
 
1145
            self.my_location_config)
 
1146
 
844
1147
    def test_location_without_username(self):
845
1148
        self.get_branch_config('http://www.example.com/ignoreparent')
846
1149
        self.assertEqual(u'Erik B\u00e5gfors <erik@bagfors.nu>',
982
1285
        self.assertEqual('bzrlib.tests.test_config.post_commit',
983
1286
                         self.my_config.post_commit())
984
1287
 
985
 
    def get_branch_config(self, location, global_config=None):
 
1288
    def get_branch_config(self, location, global_config=None,
 
1289
                          location_config=None):
 
1290
        my_branch = FakeBranch(location)
986
1291
        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)
 
1292
            global_config = sample_config_text
 
1293
        if location_config is None:
 
1294
            location_config = sample_branches_text
 
1295
 
 
1296
        my_global_config = config.GlobalConfig.from_string(global_config,
 
1297
                                                           save=True)
 
1298
        my_location_config = config.LocationConfig.from_string(
 
1299
            location_config, my_branch.base, save=True)
 
1300
        my_config = config.BranchConfig(my_branch)
 
1301
        self.my_config = my_config
 
1302
        self.my_location_config = my_config._get_location_config()
997
1303
 
998
1304
    def test_set_user_setting_sets_and_saves(self):
999
1305
        self.get_branch_config('/a/c')
1000
1306
        record = InstrumentedConfigObj("foo")
1001
1307
        self.my_location_config._parser = record
1002
1308
 
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'),
 
1309
        self.callDeprecated(['The recurse option is deprecated as of '
 
1310
                             '0.14.  The section "/a/c" has been '
 
1311
                             'converted to use policies.'],
 
1312
                            self.my_config.set_user_option,
 
1313
                            'foo', 'bar', store=config.STORE_LOCATION)
 
1314
        self.assertEqual([('reload',),
 
1315
                          ('__contains__', '/a/c'),
1022
1316
                          ('__contains__', '/a/c/'),
1023
1317
                          ('__setitem__', '/a/c', {}),
1024
1318
                          ('__getitem__', '/a/c'),
1053
1347
        self.assertEqual('bzr', my_config.get_bzr_remote_path())
1054
1348
        my_config.set_user_option('bzr_remote_path', '/path-bzr')
1055
1349
        self.assertEqual('/path-bzr', my_config.get_bzr_remote_path())
1056
 
        os.environ['BZR_REMOTE_PATH'] = '/environ-bzr'
 
1350
        self.overrideEnv('BZR_REMOTE_PATH', '/environ-bzr')
1057
1351
        self.assertEqual('/environ-bzr', my_config.get_bzr_remote_path())
1058
1352
 
1059
1353
 
1067
1361
option = exact
1068
1362
"""
1069
1363
 
1070
 
 
1071
1364
class TestBranchConfigItems(tests.TestCaseInTempDir):
1072
1365
 
1073
1366
    def get_branch_config(self, global_config=None, location=None,
1074
1367
                          location_config=None, branch_data_config=None):
1075
 
        my_config = config.BranchConfig(FakeBranch(location))
 
1368
        my_branch = FakeBranch(location)
1076
1369
        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()
 
1370
            my_global_config = config.GlobalConfig.from_string(global_config,
 
1371
                                                               save=True)
1080
1372
        if location_config is not None:
1081
 
            location_file = StringIO(location_config.encode('utf-8'))
1082
 
            self.my_location_config._get_parser(location_file)
 
1373
            my_location_config = config.LocationConfig.from_string(
 
1374
                location_config, my_branch.base, save=True)
 
1375
        my_config = config.BranchConfig(my_branch)
1083
1376
        if branch_data_config is not None:
1084
1377
            my_config.branch.control_files.files['branch.conf'] = \
1085
1378
                branch_data_config
1099
1392
                         my_config.username())
1100
1393
 
1101
1394
    def test_not_set_in_branch(self):
1102
 
        my_config = self.get_branch_config(sample_config_text)
 
1395
        my_config = self.get_branch_config(global_config=sample_config_text)
1103
1396
        self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
1104
1397
                         my_config._get_user_id())
1105
1398
        my_config.branch.control_files.files['email'] = "John"
1106
1399
        self.assertEqual("John", my_config._get_user_id())
1107
1400
 
1108
1401
    def test_BZR_EMAIL_OVERRIDES(self):
1109
 
        os.environ['BZR_EMAIL'] = "Robert Collins <robertc@example.org>"
 
1402
        self.overrideEnv('BZR_EMAIL', "Robert Collins <robertc@example.org>")
1110
1403
        branch = FakeBranch()
1111
1404
        my_config = config.BranchConfig(branch)
1112
1405
        self.assertEqual("Robert Collins <robertc@example.org>",
1129
1422
 
1130
1423
    def test_gpg_signing_command(self):
1131
1424
        my_config = self.get_branch_config(
 
1425
            global_config=sample_config_text,
1132
1426
            # branch data cannot set gpg_signing_command
1133
1427
            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
1428
        self.assertEqual('gnome-gpg', my_config.gpg_signing_command())
1137
1429
 
1138
1430
    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))
 
1431
        my_config = self.get_branch_config(global_config=sample_config_text)
1143
1432
        self.assertEqual('something',
1144
1433
                         my_config.get_user_option('user_global_option'))
1145
1434
 
1146
1435
    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)
 
1436
        my_config = self.get_branch_config(global_config=sample_config_text,
 
1437
                                      location='/a/c',
 
1438
                                      location_config=sample_branches_text)
1150
1439
        self.assertEqual(my_config.branch.base, '/a/c')
1151
1440
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1152
1441
                         my_config.post_commit())
1153
1442
        my_config.set_user_option('post_commit', 'rmtree_root')
1154
 
        # post-commit is ignored when bresent in branch data
 
1443
        # post-commit is ignored when present in branch data
1155
1444
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1156
1445
                         my_config.post_commit())
1157
1446
        my_config.set_user_option('post_commit', 'rmtree_root',
1159
1448
        self.assertEqual('rmtree_root', my_config.post_commit())
1160
1449
 
1161
1450
    def test_config_precedence(self):
 
1451
        # FIXME: eager test, luckily no persitent config file makes it fail
 
1452
        # -- vila 20100716
1162
1453
        my_config = self.get_branch_config(global_config=precedence_global)
1163
1454
        self.assertEqual(my_config.get_user_option('option'), 'global')
1164
1455
        my_config = self.get_branch_config(global_config=precedence_global,
1165
 
                                      branch_data_config=precedence_branch)
 
1456
                                           branch_data_config=precedence_branch)
1166
1457
        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)
 
1458
        my_config = self.get_branch_config(
 
1459
            global_config=precedence_global,
 
1460
            branch_data_config=precedence_branch,
 
1461
            location_config=precedence_location)
1170
1462
        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')
 
1463
        my_config = self.get_branch_config(
 
1464
            global_config=precedence_global,
 
1465
            branch_data_config=precedence_branch,
 
1466
            location_config=precedence_location,
 
1467
            location='http://example.com/specific')
1175
1468
        self.assertEqual(my_config.get_user_option('option'), 'exact')
1176
1469
 
1177
1470
    def test_get_mail_client(self):
1305
1598
        self.assertIs(None, bzrdir_config.get_default_stack_on())
1306
1599
 
1307
1600
 
 
1601
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
 
1602
 
 
1603
    def setUp(self):
 
1604
        super(TestConfigGetOptions, self).setUp()
 
1605
        create_configs(self)
 
1606
 
 
1607
    # One variable in none of the above
 
1608
    def test_no_variable(self):
 
1609
        # Using branch should query branch, locations and bazaar
 
1610
        self.assertOptions([], self.branch_config)
 
1611
 
 
1612
    def test_option_in_bazaar(self):
 
1613
        self.bazaar_config.set_user_option('file', 'bazaar')
 
1614
        self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
 
1615
                           self.bazaar_config)
 
1616
 
 
1617
    def test_option_in_locations(self):
 
1618
        self.locations_config.set_user_option('file', 'locations')
 
1619
        self.assertOptions(
 
1620
            [('file', 'locations', self.tree.basedir, 'locations')],
 
1621
            self.locations_config)
 
1622
 
 
1623
    def test_option_in_branch(self):
 
1624
        self.branch_config.set_user_option('file', 'branch')
 
1625
        self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
 
1626
                           self.branch_config)
 
1627
 
 
1628
    def test_option_in_bazaar_and_branch(self):
 
1629
        self.bazaar_config.set_user_option('file', 'bazaar')
 
1630
        self.branch_config.set_user_option('file', 'branch')
 
1631
        self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
 
1632
                            ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
 
1633
                           self.branch_config)
 
1634
 
 
1635
    def test_option_in_branch_and_locations(self):
 
1636
        # Hmm, locations override branch :-/
 
1637
        self.locations_config.set_user_option('file', 'locations')
 
1638
        self.branch_config.set_user_option('file', 'branch')
 
1639
        self.assertOptions(
 
1640
            [('file', 'locations', self.tree.basedir, 'locations'),
 
1641
             ('file', 'branch', 'DEFAULT', 'branch'),],
 
1642
            self.branch_config)
 
1643
 
 
1644
    def test_option_in_bazaar_locations_and_branch(self):
 
1645
        self.bazaar_config.set_user_option('file', 'bazaar')
 
1646
        self.locations_config.set_user_option('file', 'locations')
 
1647
        self.branch_config.set_user_option('file', 'branch')
 
1648
        self.assertOptions(
 
1649
            [('file', 'locations', self.tree.basedir, 'locations'),
 
1650
             ('file', 'branch', 'DEFAULT', 'branch'),
 
1651
             ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
 
1652
            self.branch_config)
 
1653
 
 
1654
 
 
1655
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
 
1656
 
 
1657
    def setUp(self):
 
1658
        super(TestConfigRemoveOption, self).setUp()
 
1659
        create_configs_with_file_option(self)
 
1660
 
 
1661
    def test_remove_in_locations(self):
 
1662
        self.locations_config.remove_user_option('file', self.tree.basedir)
 
1663
        self.assertOptions(
 
1664
            [('file', 'branch', 'DEFAULT', 'branch'),
 
1665
             ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
 
1666
            self.branch_config)
 
1667
 
 
1668
    def test_remove_in_branch(self):
 
1669
        self.branch_config.remove_user_option('file')
 
1670
        self.assertOptions(
 
1671
            [('file', 'locations', self.tree.basedir, 'locations'),
 
1672
             ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
 
1673
            self.branch_config)
 
1674
 
 
1675
    def test_remove_in_bazaar(self):
 
1676
        self.bazaar_config.remove_user_option('file')
 
1677
        self.assertOptions(
 
1678
            [('file', 'locations', self.tree.basedir, 'locations'),
 
1679
             ('file', 'branch', 'DEFAULT', 'branch'),],
 
1680
            self.branch_config)
 
1681
 
 
1682
 
 
1683
class TestConfigGetSections(tests.TestCaseWithTransport):
 
1684
 
 
1685
    def setUp(self):
 
1686
        super(TestConfigGetSections, self).setUp()
 
1687
        create_configs(self)
 
1688
 
 
1689
    def assertSectionNames(self, expected, conf, name=None):
 
1690
        """Check which sections are returned for a given config.
 
1691
 
 
1692
        If fallback configurations exist their sections can be included.
 
1693
 
 
1694
        :param expected: A list of section names.
 
1695
 
 
1696
        :param conf: The configuration that will be queried.
 
1697
 
 
1698
        :param name: An optional section name that will be passed to
 
1699
            get_sections().
 
1700
        """
 
1701
        sections = list(conf._get_sections(name))
 
1702
        self.assertLength(len(expected), sections)
 
1703
        self.assertEqual(expected, [name for name, _, _ in sections])
 
1704
 
 
1705
    def test_bazaar_default_section(self):
 
1706
        self.assertSectionNames(['DEFAULT'], self.bazaar_config)
 
1707
 
 
1708
    def test_locations_default_section(self):
 
1709
        # No sections are defined in an empty file
 
1710
        self.assertSectionNames([], self.locations_config)
 
1711
 
 
1712
    def test_locations_named_section(self):
 
1713
        self.locations_config.set_user_option('file', 'locations')
 
1714
        self.assertSectionNames([self.tree.basedir], self.locations_config)
 
1715
 
 
1716
    def test_locations_matching_sections(self):
 
1717
        loc_config = self.locations_config
 
1718
        loc_config.set_user_option('file', 'locations')
 
1719
        # We need to cheat a bit here to create an option in sections above and
 
1720
        # below the 'location' one.
 
1721
        parser = loc_config._get_parser()
 
1722
        # locations.cong deals with '/' ignoring native os.sep
 
1723
        location_names = self.tree.basedir.split('/')
 
1724
        parent = '/'.join(location_names[:-1])
 
1725
        child = '/'.join(location_names + ['child'])
 
1726
        parser[parent] = {}
 
1727
        parser[parent]['file'] = 'parent'
 
1728
        parser[child] = {}
 
1729
        parser[child]['file'] = 'child'
 
1730
        self.assertSectionNames([self.tree.basedir, parent], loc_config)
 
1731
 
 
1732
    def test_branch_data_default_section(self):
 
1733
        self.assertSectionNames([None],
 
1734
                                self.branch_config._get_branch_data_config())
 
1735
 
 
1736
    def test_branch_default_sections(self):
 
1737
        # No sections are defined in an empty locations file
 
1738
        self.assertSectionNames([None, 'DEFAULT'],
 
1739
                                self.branch_config)
 
1740
        # Unless we define an option
 
1741
        self.branch_config._get_location_config().set_user_option(
 
1742
            'file', 'locations')
 
1743
        self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
 
1744
                                self.branch_config)
 
1745
 
 
1746
    def test_bazaar_named_section(self):
 
1747
        # We need to cheat as the API doesn't give direct access to sections
 
1748
        # other than DEFAULT.
 
1749
        self.bazaar_config.set_alias('bazaar', 'bzr')
 
1750
        self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
 
1751
 
 
1752
 
1308
1753
class TestAuthenticationConfigFile(tests.TestCase):
1309
1754
    """Test the authentication.conf file matching"""
1310
1755