~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_config.py

  • Committer: Martin Pool
  • Date: 2011-04-19 07:52:11 UTC
  • mto: This revision was merged to the branch mainline in revision 5833.
  • Revision ID: mbp@sourcefrog.net-20110419075211-3m94qorhr0rg3gzg
Add and test _RulesSearcher.get_single_value

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
    TestSkipped,
 
46
    scenarios,
 
47
    )
38
48
from bzrlib.util.configobj import configobj
39
49
 
40
50
 
 
51
def lockable_config_scenarios():
 
52
    return [
 
53
        ('global',
 
54
         {'config_class': config.GlobalConfig,
 
55
          'config_args': [],
 
56
          'config_section': 'DEFAULT'}),
 
57
        ('locations',
 
58
         {'config_class': config.LocationConfig,
 
59
          'config_args': ['.'],
 
60
          'config_section': '.'}),]
 
61
 
 
62
 
 
63
load_tests = scenarios.load_tests_apply_scenarios
 
64
 
 
65
 
41
66
sample_long_alias="log -r-15..-1 --line"
42
67
sample_config_text = u"""
43
68
[DEFAULT]
47
72
gpg_signing_command=gnome-gpg
48
73
log_format=short
49
74
user_global_option=something
 
75
bzr.mergetool.sometool=sometool {base} {this} {other} -o {result}
 
76
bzr.mergetool.funkytool=funkytool "arg with spaces" {this_temp}
 
77
bzr.default_mergetool=sometool
50
78
[ALIASES]
51
79
h=help
52
80
ll=""" + sample_long_alias + "\n"
105
133
"""
106
134
 
107
135
 
 
136
def create_configs(test):
 
137
    """Create configuration files for a given test.
 
138
 
 
139
    This requires creating a tree (and populate the ``test.tree`` attribute)
 
140
    and its associated branch and will populate the following attributes:
 
141
 
 
142
    - branch_config: A BranchConfig for the associated branch.
 
143
 
 
144
    - locations_config : A LocationConfig for the associated branch
 
145
 
 
146
    - bazaar_config: A GlobalConfig.
 
147
 
 
148
    The tree and branch are created in a 'tree' subdirectory so the tests can
 
149
    still use the test directory to stay outside of the branch.
 
150
    """
 
151
    tree = test.make_branch_and_tree('tree')
 
152
    test.tree = tree
 
153
    test.branch_config = config.BranchConfig(tree.branch)
 
154
    test.locations_config = config.LocationConfig(tree.basedir)
 
155
    test.bazaar_config = config.GlobalConfig()
 
156
 
 
157
 
 
158
def create_configs_with_file_option(test):
 
159
    """Create configuration files with a ``file`` option set in each.
 
160
 
 
161
    This builds on ``create_configs`` and add one ``file`` option in each
 
162
    configuration with a value which allows identifying the configuration file.
 
163
    """
 
164
    create_configs(test)
 
165
    test.bazaar_config.set_user_option('file', 'bazaar')
 
166
    test.locations_config.set_user_option('file', 'locations')
 
167
    test.branch_config.set_user_option('file', 'branch')
 
168
 
 
169
 
 
170
class TestOptionsMixin:
 
171
 
 
172
    def assertOptions(self, expected, conf):
 
173
        # We don't care about the parser (as it will make tests hard to write
 
174
        # and error-prone anyway)
 
175
        self.assertThat([opt[:4] for opt in conf._get_options()],
 
176
                        matchers.Equals(expected))
 
177
 
 
178
 
108
179
class InstrumentedConfigObj(object):
109
180
    """A config obj look-enough-alike to record calls made to it."""
110
181
 
129
200
        self._calls.append(('keys',))
130
201
        return []
131
202
 
 
203
    def reload(self):
 
204
        self._calls.append(('reload',))
 
205
 
132
206
    def write(self, arg):
133
207
        self._calls.append(('write',))
134
208
 
240
314
        """
241
315
        co = config.ConfigObj()
242
316
        co['test'] = 'foo#bar'
243
 
        lines = co.write()
 
317
        outfile = StringIO()
 
318
        co.write(outfile=outfile)
 
319
        lines = outfile.getvalue().splitlines()
244
320
        self.assertEqual(lines, ['test = "foo#bar"'])
245
321
        co2 = config.ConfigObj(lines)
246
322
        self.assertEqual(co2['test'], 'foo#bar')
247
323
 
 
324
    def test_triple_quotes(self):
 
325
        # Bug #710410: if the value string has triple quotes
 
326
        # then ConfigObj versions up to 4.7.2 will quote them wrong
 
327
        # and won't able to read them back
 
328
        triple_quotes_value = '''spam
 
329
""" that's my spam """
 
330
eggs'''
 
331
        co = config.ConfigObj()
 
332
        co['test'] = triple_quotes_value
 
333
        # While writing this test another bug in ConfigObj has been found:
 
334
        # method co.write() without arguments produces list of lines
 
335
        # one option per line, and multiline values are not split
 
336
        # across multiple lines,
 
337
        # and that breaks the parsing these lines back by ConfigObj.
 
338
        # This issue only affects test, but it's better to avoid
 
339
        # `co.write()` construct at all.
 
340
        # [bialix 20110222] bug report sent to ConfigObj's author
 
341
        outfile = StringIO()
 
342
        co.write(outfile=outfile)
 
343
        output = outfile.getvalue()
 
344
        # now we're trying to read it back
 
345
        co2 = config.ConfigObj(StringIO(output))
 
346
        self.assertEquals(triple_quotes_value, co2['test'])
 
347
 
248
348
 
249
349
erroneous_config = """[section] # line 1
250
350
good=good # line 2
333
433
 
334
434
    def setUp(self):
335
435
        super(TestConfigPath, self).setUp()
336
 
        os.environ['HOME'] = '/home/bogus'
337
 
        os.environ['XDG_CACHE_DIR'] = ''
 
436
        self.overrideEnv('HOME', '/home/bogus')
 
437
        self.overrideEnv('XDG_CACHE_DIR', '')
338
438
        if sys.platform == 'win32':
339
 
            os.environ['BZR_HOME'] = \
340
 
                r'C:\Documents and Settings\bogus\Application Data'
 
439
            self.overrideEnv(
 
440
                'BZR_HOME', r'C:\Documents and Settings\bogus\Application Data')
341
441
            self.bzr_home = \
342
442
                'C:/Documents and Settings/bogus/Application Data/bazaar/2.0'
343
443
        else:
350
450
        self.assertEqual(config.config_filename(),
351
451
                         self.bzr_home + '/bazaar.conf')
352
452
 
353
 
    def test_branches_config_filename(self):
354
 
        self.assertEqual(config.branches_config_filename(),
355
 
                         self.bzr_home + '/branches.conf')
356
 
 
357
453
    def test_locations_config_filename(self):
358
454
        self.assertEqual(config.locations_config_filename(),
359
455
                         self.bzr_home + '/locations.conf')
367
463
            '/home/bogus/.cache')
368
464
 
369
465
 
370
 
class TestIniConfig(tests.TestCase):
 
466
class TestXDGConfigDir(tests.TestCaseInTempDir):
 
467
    # must be in temp dir because config tests for the existence of the bazaar
 
468
    # subdirectory of $XDG_CONFIG_HOME
 
469
 
 
470
    def setUp(self):
 
471
        if sys.platform in ('darwin', 'win32'):
 
472
            raise tests.TestNotApplicable(
 
473
                'XDG config dir not used on this platform')
 
474
        super(TestXDGConfigDir, self).setUp()
 
475
        self.overrideEnv('HOME', self.test_home_dir)
 
476
        # BZR_HOME overrides everything we want to test so unset it.
 
477
        self.overrideEnv('BZR_HOME', None)
 
478
 
 
479
    def test_xdg_config_dir_exists(self):
 
480
        """When ~/.config/bazaar exists, use it as the config dir."""
 
481
        newdir = osutils.pathjoin(self.test_home_dir, '.config', 'bazaar')
 
482
        os.makedirs(newdir)
 
483
        self.assertEqual(config.config_dir(), newdir)
 
484
 
 
485
    def test_xdg_config_home(self):
 
486
        """When XDG_CONFIG_HOME is set, use it."""
 
487
        xdgconfigdir = osutils.pathjoin(self.test_home_dir, 'xdgconfig')
 
488
        self.overrideEnv('XDG_CONFIG_HOME', xdgconfigdir)
 
489
        newdir = osutils.pathjoin(xdgconfigdir, 'bazaar')
 
490
        os.makedirs(newdir)
 
491
        self.assertEqual(config.config_dir(), newdir)
 
492
 
 
493
 
 
494
class TestIniConfig(tests.TestCaseInTempDir):
371
495
 
372
496
    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
 
497
        conf = config.IniBasedConfig.from_string(s)
 
498
        return conf, conf._get_parser()
376
499
 
377
500
 
378
501
class TestIniConfigBuilding(TestIniConfig):
379
502
 
380
503
    def test_contructs(self):
381
 
        my_config = config.IniBasedConfig("nothing")
 
504
        my_config = config.IniBasedConfig()
382
505
 
383
506
    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))
 
507
        my_config = config.IniBasedConfig.from_string(sample_config_text)
 
508
        self.assertIsInstance(my_config._get_parser(), configobj.ConfigObj)
389
509
 
390
510
    def test_cached(self):
 
511
        my_config = config.IniBasedConfig.from_string(sample_config_text)
 
512
        parser = my_config._get_parser()
 
513
        self.assertTrue(my_config._get_parser() is parser)
 
514
 
 
515
    def _dummy_chown(self, path, uid, gid):
 
516
        self.path, self.uid, self.gid = path, uid, gid
 
517
 
 
518
    def test_ini_config_ownership(self):
 
519
        """Ensure that chown is happening during _write_config_file"""
 
520
        self.requireFeature(features.chown_feature)
 
521
        self.overrideAttr(os, 'chown', self._dummy_chown)
 
522
        self.path = self.uid = self.gid = None
 
523
        conf = config.IniBasedConfig(file_name='./foo.conf')
 
524
        conf._write_config_file()
 
525
        self.assertEquals(self.path, './foo.conf')
 
526
        self.assertTrue(isinstance(self.uid, int))
 
527
        self.assertTrue(isinstance(self.gid, int))
 
528
 
 
529
    def test_get_filename_parameter_is_deprecated_(self):
 
530
        conf = self.callDeprecated([
 
531
            'IniBasedConfig.__init__(get_filename) was deprecated in 2.3.'
 
532
            ' Use file_name instead.'],
 
533
            config.IniBasedConfig, lambda: 'ini.conf')
 
534
        self.assertEqual('ini.conf', conf.file_name)
 
535
 
 
536
    def test_get_parser_file_parameter_is_deprecated_(self):
391
537
        config_file = StringIO(sample_config_text.encode('utf-8'))
392
 
        my_config = config.IniBasedConfig(None)
393
 
        parser = my_config._get_parser(file=config_file)
394
 
        self.failUnless(my_config._get_parser() is parser)
 
538
        conf = config.IniBasedConfig.from_string(sample_config_text)
 
539
        conf = self.callDeprecated([
 
540
            'IniBasedConfig._get_parser(file=xxx) was deprecated in 2.3.'
 
541
            ' Use IniBasedConfig(_content=xxx) instead.'],
 
542
            conf._get_parser, file=config_file)
 
543
 
 
544
 
 
545
class TestIniConfigSaving(tests.TestCaseInTempDir):
 
546
 
 
547
    def test_cant_save_without_a_file_name(self):
 
548
        conf = config.IniBasedConfig()
 
549
        self.assertRaises(AssertionError, conf._write_config_file)
 
550
 
 
551
    def test_saved_with_content(self):
 
552
        content = 'foo = bar\n'
 
553
        conf = config.IniBasedConfig.from_string(
 
554
            content, file_name='./test.conf', save=True)
 
555
        self.assertFileEqual(content, 'test.conf')
 
556
 
 
557
 
 
558
class TestIniConfigOptionExpansionDefaultValue(tests.TestCaseInTempDir):
 
559
    """What is the default value of expand for config options.
 
560
 
 
561
    This is an opt-in beta feature used to evaluate whether or not option
 
562
    references can appear in dangerous place raising exceptions, disapearing
 
563
    (and as such corrupting data) or if it's safe to activate the option by
 
564
    default.
 
565
 
 
566
    Note that these tests relies on config._expand_default_value being already
 
567
    overwritten in the parent class setUp.
 
568
    """
 
569
 
 
570
    def setUp(self):
 
571
        super(TestIniConfigOptionExpansionDefaultValue, self).setUp()
 
572
        self.config = None
 
573
        self.warnings = []
 
574
        def warning(*args):
 
575
            self.warnings.append(args[0] % args[1:])
 
576
        self.overrideAttr(trace, 'warning', warning)
 
577
 
 
578
    def get_config(self, expand):
 
579
        c = config.GlobalConfig.from_string('bzr.config.expand=%s' % (expand,),
 
580
                                            save=True)
 
581
        return c
 
582
 
 
583
    def assertExpandIs(self, expected):
 
584
        actual = config._get_expand_default_value()
 
585
        #self.config.get_user_option_as_bool('bzr.config.expand')
 
586
        self.assertEquals(expected, actual)
 
587
 
 
588
    def test_default_is_None(self):
 
589
        self.assertEquals(None, config._expand_default_value)
 
590
 
 
591
    def test_default_is_False_even_if_None(self):
 
592
        self.config = self.get_config(None)
 
593
        self.assertExpandIs(False)
 
594
 
 
595
    def test_default_is_False_even_if_invalid(self):
 
596
        self.config = self.get_config('<your choice>')
 
597
        self.assertExpandIs(False)
 
598
        # ...
 
599
        # Huh ? My choice is False ? Thanks, always happy to hear that :D
 
600
        # Wait, you've been warned !
 
601
        self.assertLength(1, self.warnings)
 
602
        self.assertEquals(
 
603
            'Value "<your choice>" is not a boolean for "bzr.config.expand"',
 
604
            self.warnings[0])
 
605
 
 
606
    def test_default_is_True(self):
 
607
        self.config = self.get_config(True)
 
608
        self.assertExpandIs(True)
 
609
        
 
610
    def test_default_is_False(self):
 
611
        self.config = self.get_config(False)
 
612
        self.assertExpandIs(False)
 
613
        
 
614
 
 
615
class TestIniConfigOptionExpansion(tests.TestCase):
 
616
    """Test option expansion from the IniConfig level.
 
617
 
 
618
    What we really want here is to test the Config level, but the class being
 
619
    abstract as far as storing values is concerned, this can't be done
 
620
    properly (yet).
 
621
    """
 
622
    # FIXME: This should be rewritten when all configs share a storage
 
623
    # implementation -- vila 2011-02-18
 
624
 
 
625
    def get_config(self, string=None):
 
626
        if string is None:
 
627
            string = ''
 
628
        c = config.IniBasedConfig.from_string(string)
 
629
        return c
 
630
 
 
631
    def assertExpansion(self, expected, conf, string, env=None):
 
632
        self.assertEquals(expected, conf.expand_options(string, env))
 
633
 
 
634
    def test_no_expansion(self):
 
635
        c = self.get_config('')
 
636
        self.assertExpansion('foo', c, 'foo')
 
637
 
 
638
    def test_env_adding_options(self):
 
639
        c = self.get_config('')
 
640
        self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
 
641
 
 
642
    def test_env_overriding_options(self):
 
643
        c = self.get_config('foo=baz')
 
644
        self.assertExpansion('bar', c, '{foo}', {'foo': 'bar'})
 
645
 
 
646
    def test_simple_ref(self):
 
647
        c = self.get_config('foo=xxx')
 
648
        self.assertExpansion('xxx', c, '{foo}')
 
649
 
 
650
    def test_unknown_ref(self):
 
651
        c = self.get_config('')
 
652
        self.assertRaises(errors.ExpandingUnknownOption,
 
653
                          c.expand_options, '{foo}')
 
654
 
 
655
    def test_indirect_ref(self):
 
656
        c = self.get_config('''
 
657
foo=xxx
 
658
bar={foo}
 
659
''')
 
660
        self.assertExpansion('xxx', c, '{bar}')
 
661
 
 
662
    def test_embedded_ref(self):
 
663
        c = self.get_config('''
 
664
foo=xxx
 
665
bar=foo
 
666
''')
 
667
        self.assertExpansion('xxx', c, '{{bar}}')
 
668
 
 
669
    def test_simple_loop(self):
 
670
        c = self.get_config('foo={foo}')
 
671
        self.assertRaises(errors.OptionExpansionLoop, c.expand_options, '{foo}')
 
672
 
 
673
    def test_indirect_loop(self):
 
674
        c = self.get_config('''
 
675
foo={bar}
 
676
bar={baz}
 
677
baz={foo}''')
 
678
        e = self.assertRaises(errors.OptionExpansionLoop,
 
679
                              c.expand_options, '{foo}')
 
680
        self.assertEquals('foo->bar->baz', e.refs)
 
681
        self.assertEquals('{foo}', e.string)
 
682
 
 
683
    def test_list(self):
 
684
        conf = self.get_config('''
 
685
foo=start
 
686
bar=middle
 
687
baz=end
 
688
list={foo},{bar},{baz}
 
689
''')
 
690
        self.assertEquals(['start', 'middle', 'end'],
 
691
                           conf.get_user_option('list', expand=True))
 
692
 
 
693
    def test_cascading_list(self):
 
694
        conf = self.get_config('''
 
695
foo=start,{bar}
 
696
bar=middle,{baz}
 
697
baz=end
 
698
list={foo}
 
699
''')
 
700
        self.assertEquals(['start', 'middle', 'end'],
 
701
                           conf.get_user_option('list', expand=True))
 
702
 
 
703
    def test_pathological_hidden_list(self):
 
704
        conf = self.get_config('''
 
705
foo=bin
 
706
bar=go
 
707
start={foo
 
708
middle=},{
 
709
end=bar}
 
710
hidden={start}{middle}{end}
 
711
''')
 
712
        # Nope, it's either a string or a list, and the list wins as soon as a
 
713
        # ',' appears, so the string concatenation never occur.
 
714
        self.assertEquals(['{foo', '}', '{', 'bar}'],
 
715
                          conf.get_user_option('hidden', expand=True))
 
716
 
 
717
class TestLocationConfigOptionExpansion(tests.TestCaseInTempDir):
 
718
 
 
719
    def get_config(self, location, string=None):
 
720
        if string is None:
 
721
            string = ''
 
722
        # Since we don't save the config we won't strictly require to inherit
 
723
        # from TestCaseInTempDir, but an error occurs so quickly...
 
724
        c = config.LocationConfig.from_string(string, location)
 
725
        return c
 
726
 
 
727
    def test_dont_cross_unrelated_section(self):
 
728
        c = self.get_config('/another/branch/path','''
 
729
[/one/branch/path]
 
730
foo = hello
 
731
bar = {foo}/2
 
732
 
 
733
[/another/branch/path]
 
734
bar = {foo}/2
 
735
''')
 
736
        self.assertRaises(errors.ExpandingUnknownOption,
 
737
                          c.get_user_option, 'bar', expand=True)
 
738
 
 
739
    def test_cross_related_sections(self):
 
740
        c = self.get_config('/project/branch/path','''
 
741
[/project]
 
742
foo = qu
 
743
 
 
744
[/project/branch/path]
 
745
bar = {foo}ux
 
746
''')
 
747
        self.assertEquals('quux', c.get_user_option('bar', expand=True))
 
748
 
 
749
 
 
750
class TestIniBaseConfigOnDisk(tests.TestCaseInTempDir):
 
751
 
 
752
    def test_cannot_reload_without_name(self):
 
753
        conf = config.IniBasedConfig.from_string(sample_config_text)
 
754
        self.assertRaises(AssertionError, conf.reload)
 
755
 
 
756
    def test_reload_see_new_value(self):
 
757
        c1 = config.IniBasedConfig.from_string('editor=vim\n',
 
758
                                               file_name='./test/conf')
 
759
        c1._write_config_file()
 
760
        c2 = config.IniBasedConfig.from_string('editor=emacs\n',
 
761
                                               file_name='./test/conf')
 
762
        c2._write_config_file()
 
763
        self.assertEqual('vim', c1.get_user_option('editor'))
 
764
        self.assertEqual('emacs', c2.get_user_option('editor'))
 
765
        # Make sure we get the Right value
 
766
        c1.reload()
 
767
        self.assertEqual('emacs', c1.get_user_option('editor'))
 
768
 
 
769
 
 
770
class TestLockableConfig(tests.TestCaseInTempDir):
 
771
 
 
772
    scenarios = lockable_config_scenarios()
 
773
 
 
774
    # Set by load_tests
 
775
    config_class = None
 
776
    config_args = None
 
777
    config_section = None
 
778
 
 
779
    def setUp(self):
 
780
        super(TestLockableConfig, self).setUp()
 
781
        self._content = '[%s]\none=1\ntwo=2\n' % (self.config_section,)
 
782
        self.config = self.create_config(self._content)
 
783
 
 
784
    def get_existing_config(self):
 
785
        return self.config_class(*self.config_args)
 
786
 
 
787
    def create_config(self, content):
 
788
        kwargs = dict(save=True)
 
789
        c = self.config_class.from_string(content, *self.config_args, **kwargs)
 
790
        return c
 
791
 
 
792
    def test_simple_read_access(self):
 
793
        self.assertEquals('1', self.config.get_user_option('one'))
 
794
 
 
795
    def test_simple_write_access(self):
 
796
        self.config.set_user_option('one', 'one')
 
797
        self.assertEquals('one', self.config.get_user_option('one'))
 
798
 
 
799
    def test_listen_to_the_last_speaker(self):
 
800
        c1 = self.config
 
801
        c2 = self.get_existing_config()
 
802
        c1.set_user_option('one', 'ONE')
 
803
        c2.set_user_option('two', 'TWO')
 
804
        self.assertEquals('ONE', c1.get_user_option('one'))
 
805
        self.assertEquals('TWO', c2.get_user_option('two'))
 
806
        # The second update respect the first one
 
807
        self.assertEquals('ONE', c2.get_user_option('one'))
 
808
 
 
809
    def test_last_speaker_wins(self):
 
810
        # If the same config is not shared, the same variable modified twice
 
811
        # can only see a single result.
 
812
        c1 = self.config
 
813
        c2 = self.get_existing_config()
 
814
        c1.set_user_option('one', 'c1')
 
815
        c2.set_user_option('one', 'c2')
 
816
        self.assertEquals('c2', c2._get_user_option('one'))
 
817
        # The first modification is still available until another refresh
 
818
        # occur
 
819
        self.assertEquals('c1', c1._get_user_option('one'))
 
820
        c1.set_user_option('two', 'done')
 
821
        self.assertEquals('c2', c1._get_user_option('one'))
 
822
 
 
823
    def test_writes_are_serialized(self):
 
824
        c1 = self.config
 
825
        c2 = self.get_existing_config()
 
826
 
 
827
        # We spawn a thread that will pause *during* the write
 
828
        before_writing = threading.Event()
 
829
        after_writing = threading.Event()
 
830
        writing_done = threading.Event()
 
831
        c1_orig = c1._write_config_file
 
832
        def c1_write_config_file():
 
833
            before_writing.set()
 
834
            c1_orig()
 
835
            # The lock is held we wait for the main thread to decide when to
 
836
            # continue
 
837
            after_writing.wait()
 
838
        c1._write_config_file = c1_write_config_file
 
839
        def c1_set_option():
 
840
            c1.set_user_option('one', 'c1')
 
841
            writing_done.set()
 
842
        t1 = threading.Thread(target=c1_set_option)
 
843
        # Collect the thread after the test
 
844
        self.addCleanup(t1.join)
 
845
        # Be ready to unblock the thread if the test goes wrong
 
846
        self.addCleanup(after_writing.set)
 
847
        t1.start()
 
848
        before_writing.wait()
 
849
        self.assertTrue(c1._lock.is_held)
 
850
        self.assertRaises(errors.LockContention,
 
851
                          c2.set_user_option, 'one', 'c2')
 
852
        self.assertEquals('c1', c1.get_user_option('one'))
 
853
        # Let the lock be released
 
854
        after_writing.set()
 
855
        writing_done.wait()
 
856
        c2.set_user_option('one', 'c2')
 
857
        self.assertEquals('c2', c2.get_user_option('one'))
 
858
 
 
859
    def test_read_while_writing(self):
 
860
       c1 = self.config
 
861
       # We spawn a thread that will pause *during* the write
 
862
       ready_to_write = threading.Event()
 
863
       do_writing = threading.Event()
 
864
       writing_done = threading.Event()
 
865
       c1_orig = c1._write_config_file
 
866
       def c1_write_config_file():
 
867
           ready_to_write.set()
 
868
           # The lock is held we wait for the main thread to decide when to
 
869
           # continue
 
870
           do_writing.wait()
 
871
           c1_orig()
 
872
           writing_done.set()
 
873
       c1._write_config_file = c1_write_config_file
 
874
       def c1_set_option():
 
875
           c1.set_user_option('one', 'c1')
 
876
       t1 = threading.Thread(target=c1_set_option)
 
877
       # Collect the thread after the test
 
878
       self.addCleanup(t1.join)
 
879
       # Be ready to unblock the thread if the test goes wrong
 
880
       self.addCleanup(do_writing.set)
 
881
       t1.start()
 
882
       # Ensure the thread is ready to write
 
883
       ready_to_write.wait()
 
884
       self.assertTrue(c1._lock.is_held)
 
885
       self.assertEquals('c1', c1.get_user_option('one'))
 
886
       # If we read during the write, we get the old value
 
887
       c2 = self.get_existing_config()
 
888
       self.assertEquals('1', c2.get_user_option('one'))
 
889
       # Let the writing occur and ensure it occurred
 
890
       do_writing.set()
 
891
       writing_done.wait()
 
892
       # Now we get the updated value
 
893
       c3 = self.get_existing_config()
 
894
       self.assertEquals('c1', c3.get_user_option('one'))
395
895
 
396
896
 
397
897
class TestGetUserOptionAs(TestIniConfig):
462
962
            parser = my_config._get_parser()
463
963
        finally:
464
964
            config.ConfigObj = oldparserclass
465
 
        self.failUnless(isinstance(parser, InstrumentedConfigObj))
 
965
        self.assertIsInstance(parser, InstrumentedConfigObj)
466
966
        self.assertEqual(parser._calls, [('__init__', config.config_filename(),
467
967
                                          'utf-8')])
468
968
 
479
979
        my_config = config.BranchConfig(branch)
480
980
        location_config = my_config._get_location_config()
481
981
        self.assertEqual(branch.base, location_config.location)
482
 
        self.failUnless(location_config is my_config._get_location_config())
 
982
        self.assertIs(location_config, my_config._get_location_config())
483
983
 
484
984
    def test_get_config(self):
485
985
        """The Branch.get_config method works properly"""
505
1005
        branch = self.make_branch('branch')
506
1006
        self.assertEqual('branch', branch.nick)
507
1007
 
508
 
        locations = config.locations_config_filename()
509
 
        config.ensure_config_dir_exists()
510
1008
        local_url = urlutils.local_path_to_url('branch')
511
 
        open(locations, 'wb').write('[%s]\nnickname = foobar'
512
 
                                    % (local_url,))
 
1009
        conf = config.LocationConfig.from_string(
 
1010
            '[%s]\nnickname = foobar' % (local_url,),
 
1011
            local_url, save=True)
513
1012
        self.assertEqual('foobar', branch.nick)
514
1013
 
515
1014
    def test_config_local_path(self):
517
1016
        branch = self.make_branch('branch')
518
1017
        self.assertEqual('branch', branch.nick)
519
1018
 
520
 
        locations = config.locations_config_filename()
521
 
        config.ensure_config_dir_exists()
522
 
        open(locations, 'wb').write('[%s/branch]\nnickname = barry'
523
 
                                    % (osutils.getcwd().encode('utf8'),))
 
1019
        local_path = osutils.getcwd().encode('utf8')
 
1020
        conf = config.LocationConfig.from_string(
 
1021
            '[%s/branch]\nnickname = barry' % (local_path,),
 
1022
            'branch',  save=True)
524
1023
        self.assertEqual('barry', branch.nick)
525
1024
 
526
1025
    def test_config_creates_local(self):
527
1026
        """Creating a new entry in config uses a local path."""
528
1027
        branch = self.make_branch('branch', format='knit')
529
1028
        branch.set_push_location('http://foobar')
530
 
        locations = config.locations_config_filename()
531
1029
        local_path = osutils.getcwd().encode('utf8')
532
1030
        # Surprisingly ConfigObj doesn't create a trailing newline
533
 
        self.check_file_contents(locations,
 
1031
        self.check_file_contents(config.locations_config_filename(),
534
1032
                                 '[%s/branch]\n'
535
1033
                                 'push_location = http://foobar\n'
536
1034
                                 'push_location:policy = norecurse\n'
541
1039
        self.assertEqual('!repo', b.get_config().get_nickname())
542
1040
 
543
1041
    def test_warn_if_masked(self):
544
 
        _warning = trace.warning
545
1042
        warnings = []
546
1043
        def warning(*args):
547
1044
            warnings.append(args[0] % args[1:])
 
1045
        self.overrideAttr(trace, 'warning', warning)
548
1046
 
549
1047
        def set_option(store, warn_masked=True):
550
1048
            warnings[:] = []
556
1054
            else:
557
1055
                self.assertEqual(1, len(warnings))
558
1056
                self.assertEqual(warning, warnings[0])
559
 
        trace.warning = warning
560
 
        try:
561
 
            branch = self.make_branch('.')
562
 
            conf = branch.get_config()
563
 
            set_option(config.STORE_GLOBAL)
564
 
            assertWarning(None)
565
 
            set_option(config.STORE_BRANCH)
566
 
            assertWarning(None)
567
 
            set_option(config.STORE_GLOBAL)
568
 
            assertWarning('Value "4" is masked by "3" from branch.conf')
569
 
            set_option(config.STORE_GLOBAL, warn_masked=False)
570
 
            assertWarning(None)
571
 
            set_option(config.STORE_LOCATION)
572
 
            assertWarning(None)
573
 
            set_option(config.STORE_BRANCH)
574
 
            assertWarning('Value "3" is masked by "0" from locations.conf')
575
 
            set_option(config.STORE_BRANCH, warn_masked=False)
576
 
            assertWarning(None)
577
 
        finally:
578
 
            trace.warning = _warning
579
 
 
580
 
 
581
 
class TestGlobalConfigItems(tests.TestCase):
 
1057
        branch = self.make_branch('.')
 
1058
        conf = branch.get_config()
 
1059
        set_option(config.STORE_GLOBAL)
 
1060
        assertWarning(None)
 
1061
        set_option(config.STORE_BRANCH)
 
1062
        assertWarning(None)
 
1063
        set_option(config.STORE_GLOBAL)
 
1064
        assertWarning('Value "4" is masked by "3" from branch.conf')
 
1065
        set_option(config.STORE_GLOBAL, warn_masked=False)
 
1066
        assertWarning(None)
 
1067
        set_option(config.STORE_LOCATION)
 
1068
        assertWarning(None)
 
1069
        set_option(config.STORE_BRANCH)
 
1070
        assertWarning('Value "3" is masked by "0" from locations.conf')
 
1071
        set_option(config.STORE_BRANCH, warn_masked=False)
 
1072
        assertWarning(None)
 
1073
 
 
1074
 
 
1075
class TestGlobalConfigItems(tests.TestCaseInTempDir):
582
1076
 
583
1077
    def test_user_id(self):
584
 
        config_file = StringIO(sample_config_text.encode('utf-8'))
585
 
        my_config = config.GlobalConfig()
586
 
        my_config._parser = my_config._get_parser(file=config_file)
 
1078
        my_config = config.GlobalConfig.from_string(sample_config_text)
587
1079
        self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
588
1080
                         my_config._get_user_id())
589
1081
 
590
1082
    def test_absent_user_id(self):
591
 
        config_file = StringIO("")
592
1083
        my_config = config.GlobalConfig()
593
 
        my_config._parser = my_config._get_parser(file=config_file)
594
1084
        self.assertEqual(None, my_config._get_user_id())
595
1085
 
596
1086
    def test_configured_editor(self):
597
 
        config_file = StringIO(sample_config_text.encode('utf-8'))
598
 
        my_config = config.GlobalConfig()
599
 
        my_config._parser = my_config._get_parser(file=config_file)
 
1087
        my_config = config.GlobalConfig.from_string(sample_config_text)
600
1088
        self.assertEqual("vim", my_config.get_editor())
601
1089
 
602
1090
    def test_signatures_always(self):
603
 
        config_file = StringIO(sample_always_signatures)
604
 
        my_config = config.GlobalConfig()
605
 
        my_config._parser = my_config._get_parser(file=config_file)
 
1091
        my_config = config.GlobalConfig.from_string(sample_always_signatures)
606
1092
        self.assertEqual(config.CHECK_NEVER,
607
1093
                         my_config.signature_checking())
608
1094
        self.assertEqual(config.SIGN_ALWAYS,
610
1096
        self.assertEqual(True, my_config.signature_needed())
611
1097
 
612
1098
    def test_signatures_if_possible(self):
613
 
        config_file = StringIO(sample_maybe_signatures)
614
 
        my_config = config.GlobalConfig()
615
 
        my_config._parser = my_config._get_parser(file=config_file)
 
1099
        my_config = config.GlobalConfig.from_string(sample_maybe_signatures)
616
1100
        self.assertEqual(config.CHECK_NEVER,
617
1101
                         my_config.signature_checking())
618
1102
        self.assertEqual(config.SIGN_WHEN_REQUIRED,
620
1104
        self.assertEqual(False, my_config.signature_needed())
621
1105
 
622
1106
    def test_signatures_ignore(self):
623
 
        config_file = StringIO(sample_ignore_signatures)
624
 
        my_config = config.GlobalConfig()
625
 
        my_config._parser = my_config._get_parser(file=config_file)
 
1107
        my_config = config.GlobalConfig.from_string(sample_ignore_signatures)
626
1108
        self.assertEqual(config.CHECK_ALWAYS,
627
1109
                         my_config.signature_checking())
628
1110
        self.assertEqual(config.SIGN_NEVER,
630
1112
        self.assertEqual(False, my_config.signature_needed())
631
1113
 
632
1114
    def _get_sample_config(self):
633
 
        config_file = StringIO(sample_config_text.encode('utf-8'))
634
 
        my_config = config.GlobalConfig()
635
 
        my_config._parser = my_config._get_parser(file=config_file)
 
1115
        my_config = config.GlobalConfig.from_string(sample_config_text)
636
1116
        return my_config
637
1117
 
638
1118
    def test_gpg_signing_command(self):
641
1121
        self.assertEqual(False, my_config.signature_needed())
642
1122
 
643
1123
    def _get_empty_config(self):
644
 
        config_file = StringIO("")
645
1124
        my_config = config.GlobalConfig()
646
 
        my_config._parser = my_config._get_parser(file=config_file)
647
1125
        return my_config
648
1126
 
649
1127
    def test_gpg_signing_command_unset(self):
699
1177
        change_editor = my_config.get_change_editor('old', 'new')
700
1178
        self.assertIs(None, change_editor)
701
1179
 
 
1180
    def test_get_merge_tools(self):
 
1181
        conf = self._get_sample_config()
 
1182
        tools = conf.get_merge_tools()
 
1183
        self.log(repr(tools))
 
1184
        self.assertEqual(
 
1185
            {u'funkytool' : u'funkytool "arg with spaces" {this_temp}',
 
1186
            u'sometool' : u'sometool {base} {this} {other} -o {result}'},
 
1187
            tools)
 
1188
 
 
1189
    def test_get_merge_tools_empty(self):
 
1190
        conf = self._get_empty_config()
 
1191
        tools = conf.get_merge_tools()
 
1192
        self.assertEqual({}, tools)
 
1193
 
 
1194
    def test_find_merge_tool(self):
 
1195
        conf = self._get_sample_config()
 
1196
        cmdline = conf.find_merge_tool('sometool')
 
1197
        self.assertEqual('sometool {base} {this} {other} -o {result}', cmdline)
 
1198
 
 
1199
    def test_find_merge_tool_not_found(self):
 
1200
        conf = self._get_sample_config()
 
1201
        cmdline = conf.find_merge_tool('DOES NOT EXIST')
 
1202
        self.assertIs(cmdline, None)
 
1203
 
 
1204
    def test_find_merge_tool_known(self):
 
1205
        conf = self._get_empty_config()
 
1206
        cmdline = conf.find_merge_tool('kdiff3')
 
1207
        self.assertEquals('kdiff3 {base} {this} {other} -o {result}', cmdline)
 
1208
 
 
1209
    def test_find_merge_tool_override_known(self):
 
1210
        conf = self._get_empty_config()
 
1211
        conf.set_user_option('bzr.mergetool.kdiff3', 'kdiff3 blah')
 
1212
        cmdline = conf.find_merge_tool('kdiff3')
 
1213
        self.assertEqual('kdiff3 blah', cmdline)
 
1214
 
702
1215
 
703
1216
class TestGlobalConfigSavingOptions(tests.TestCaseInTempDir):
704
1217
 
722
1235
        self.assertIs(None, new_config.get_alias('commit'))
723
1236
 
724
1237
 
725
 
class TestLocationConfig(tests.TestCaseInTempDir):
 
1238
class TestLocationConfig(tests.TestCaseInTempDir, TestOptionsMixin):
726
1239
 
727
1240
    def test_constructs(self):
728
1241
        my_config = config.LocationConfig('http://example.com')
740
1253
            parser = my_config._get_parser()
741
1254
        finally:
742
1255
            config.ConfigObj = oldparserclass
743
 
        self.failUnless(isinstance(parser, InstrumentedConfigObj))
 
1256
        self.assertIsInstance(parser, InstrumentedConfigObj)
744
1257
        self.assertEqual(parser._calls,
745
1258
                         [('__init__', config.locations_config_filename(),
746
1259
                           'utf-8')])
747
 
        config.ensure_config_dir_exists()
748
 
        #os.mkdir(config.config_dir())
749
 
        f = file(config.branches_config_filename(), 'wb')
750
 
        f.write('')
751
 
        f.close()
752
 
        oldparserclass = config.ConfigObj
753
 
        config.ConfigObj = InstrumentedConfigObj
754
 
        try:
755
 
            my_config = config.LocationConfig('http://www.example.com')
756
 
            parser = my_config._get_parser()
757
 
        finally:
758
 
            config.ConfigObj = oldparserclass
759
1260
 
760
1261
    def test_get_global_config(self):
761
1262
        my_config = config.BranchConfig(FakeBranch('http://example.com'))
762
1263
        global_config = my_config._get_global_config()
763
 
        self.failUnless(isinstance(global_config, config.GlobalConfig))
764
 
        self.failUnless(global_config is my_config._get_global_config())
 
1264
        self.assertIsInstance(global_config, config.GlobalConfig)
 
1265
        self.assertIs(global_config, my_config._get_global_config())
 
1266
 
 
1267
    def assertLocationMatching(self, expected):
 
1268
        self.assertEqual(expected,
 
1269
                         list(self.my_location_config._get_matching_sections()))
765
1270
 
766
1271
    def test__get_matching_sections_no_match(self):
767
1272
        self.get_branch_config('/')
768
 
        self.assertEqual([], self.my_location_config._get_matching_sections())
 
1273
        self.assertLocationMatching([])
769
1274
 
770
1275
    def test__get_matching_sections_exact(self):
771
1276
        self.get_branch_config('http://www.example.com')
772
 
        self.assertEqual([('http://www.example.com', '')],
773
 
                         self.my_location_config._get_matching_sections())
 
1277
        self.assertLocationMatching([('http://www.example.com', '')])
774
1278
 
775
1279
    def test__get_matching_sections_suffix_does_not(self):
776
1280
        self.get_branch_config('http://www.example.com-com')
777
 
        self.assertEqual([], self.my_location_config._get_matching_sections())
 
1281
        self.assertLocationMatching([])
778
1282
 
779
1283
    def test__get_matching_sections_subdir_recursive(self):
780
1284
        self.get_branch_config('http://www.example.com/com')
781
 
        self.assertEqual([('http://www.example.com', 'com')],
782
 
                         self.my_location_config._get_matching_sections())
 
1285
        self.assertLocationMatching([('http://www.example.com', 'com')])
783
1286
 
784
1287
    def test__get_matching_sections_ignoreparent(self):
785
1288
        self.get_branch_config('http://www.example.com/ignoreparent')
786
 
        self.assertEqual([('http://www.example.com/ignoreparent', '')],
787
 
                         self.my_location_config._get_matching_sections())
 
1289
        self.assertLocationMatching([('http://www.example.com/ignoreparent',
 
1290
                                      '')])
788
1291
 
789
1292
    def test__get_matching_sections_ignoreparent_subdir(self):
790
1293
        self.get_branch_config(
791
1294
            'http://www.example.com/ignoreparent/childbranch')
792
 
        self.assertEqual([('http://www.example.com/ignoreparent',
793
 
                           'childbranch')],
794
 
                         self.my_location_config._get_matching_sections())
 
1295
        self.assertLocationMatching([('http://www.example.com/ignoreparent',
 
1296
                                      'childbranch')])
795
1297
 
796
1298
    def test__get_matching_sections_subdir_trailing_slash(self):
797
1299
        self.get_branch_config('/b')
798
 
        self.assertEqual([('/b/', '')],
799
 
                         self.my_location_config._get_matching_sections())
 
1300
        self.assertLocationMatching([('/b/', '')])
800
1301
 
801
1302
    def test__get_matching_sections_subdir_child(self):
802
1303
        self.get_branch_config('/a/foo')
803
 
        self.assertEqual([('/a/*', ''), ('/a/', 'foo')],
804
 
                         self.my_location_config._get_matching_sections())
 
1304
        self.assertLocationMatching([('/a/*', ''), ('/a/', 'foo')])
805
1305
 
806
1306
    def test__get_matching_sections_subdir_child_child(self):
807
1307
        self.get_branch_config('/a/foo/bar')
808
 
        self.assertEqual([('/a/*', 'bar'), ('/a/', 'foo/bar')],
809
 
                         self.my_location_config._get_matching_sections())
 
1308
        self.assertLocationMatching([('/a/*', 'bar'), ('/a/', 'foo/bar')])
810
1309
 
811
1310
    def test__get_matching_sections_trailing_slash_with_children(self):
812
1311
        self.get_branch_config('/a/')
813
 
        self.assertEqual([('/a/', '')],
814
 
                         self.my_location_config._get_matching_sections())
 
1312
        self.assertLocationMatching([('/a/', '')])
815
1313
 
816
1314
    def test__get_matching_sections_explicit_over_glob(self):
817
1315
        # XXX: 2006-09-08 jamesh
819
1317
        # was a config section for '/a/?', it would get precedence
820
1318
        # over '/a/c'.
821
1319
        self.get_branch_config('/a/c')
822
 
        self.assertEqual([('/a/c', ''), ('/a/*', ''), ('/a/', 'c')],
823
 
                         self.my_location_config._get_matching_sections())
 
1320
        self.assertLocationMatching([('/a/c', ''), ('/a/*', ''), ('/a/', 'c')])
824
1321
 
825
1322
    def test__get_option_policy_normal(self):
826
1323
        self.get_branch_config('http://www.example.com')
848
1345
            'http://www.example.com', 'appendpath_option'),
849
1346
            config.POLICY_APPENDPATH)
850
1347
 
 
1348
    def test__get_options_with_policy(self):
 
1349
        self.get_branch_config('/dir/subdir',
 
1350
                               location_config="""\
 
1351
[/dir]
 
1352
other_url = /other-dir
 
1353
other_url:policy = appendpath
 
1354
[/dir/subdir]
 
1355
other_url = /other-subdir
 
1356
""")
 
1357
        self.assertOptions(
 
1358
            [(u'other_url', u'/other-subdir', u'/dir/subdir', 'locations'),
 
1359
             (u'other_url', u'/other-dir', u'/dir', 'locations'),
 
1360
             (u'other_url:policy', u'appendpath', u'/dir', 'locations')],
 
1361
            self.my_location_config)
 
1362
 
851
1363
    def test_location_without_username(self):
852
1364
        self.get_branch_config('http://www.example.com/ignoreparent')
853
1365
        self.assertEqual(u'Erik B\u00e5gfors <erik@bagfors.nu>',
989
1501
        self.assertEqual('bzrlib.tests.test_config.post_commit',
990
1502
                         self.my_config.post_commit())
991
1503
 
992
 
    def get_branch_config(self, location, global_config=None):
 
1504
    def get_branch_config(self, location, global_config=None,
 
1505
                          location_config=None):
 
1506
        my_branch = FakeBranch(location)
993
1507
        if global_config is None:
994
 
            global_file = StringIO(sample_config_text.encode('utf-8'))
995
 
        else:
996
 
            global_file = StringIO(global_config.encode('utf-8'))
997
 
        branches_file = StringIO(sample_branches_text.encode('utf-8'))
998
 
        self.my_config = config.BranchConfig(FakeBranch(location))
999
 
        # Force location config to use specified file
1000
 
        self.my_location_config = self.my_config._get_location_config()
1001
 
        self.my_location_config._get_parser(branches_file)
1002
 
        # Force global config to use specified file
1003
 
        self.my_config._get_global_config()._get_parser(global_file)
 
1508
            global_config = sample_config_text
 
1509
        if location_config is None:
 
1510
            location_config = sample_branches_text
 
1511
 
 
1512
        my_global_config = config.GlobalConfig.from_string(global_config,
 
1513
                                                           save=True)
 
1514
        my_location_config = config.LocationConfig.from_string(
 
1515
            location_config, my_branch.base, save=True)
 
1516
        my_config = config.BranchConfig(my_branch)
 
1517
        self.my_config = my_config
 
1518
        self.my_location_config = my_config._get_location_config()
1004
1519
 
1005
1520
    def test_set_user_setting_sets_and_saves(self):
1006
1521
        self.get_branch_config('/a/c')
1007
1522
        record = InstrumentedConfigObj("foo")
1008
1523
        self.my_location_config._parser = record
1009
1524
 
1010
 
        real_mkdir = os.mkdir
1011
 
        self.created = False
1012
 
        def checked_mkdir(path, mode=0777):
1013
 
            self.log('making directory: %s', path)
1014
 
            real_mkdir(path, mode)
1015
 
            self.created = True
1016
 
 
1017
 
        os.mkdir = checked_mkdir
1018
 
        try:
1019
 
            self.callDeprecated(['The recurse option is deprecated as of '
1020
 
                                 '0.14.  The section "/a/c" has been '
1021
 
                                 'converted to use policies.'],
1022
 
                                self.my_config.set_user_option,
1023
 
                                'foo', 'bar', store=config.STORE_LOCATION)
1024
 
        finally:
1025
 
            os.mkdir = real_mkdir
1026
 
 
1027
 
        self.failUnless(self.created, 'Failed to create ~/.bazaar')
1028
 
        self.assertEqual([('__contains__', '/a/c'),
 
1525
        self.callDeprecated(['The recurse option is deprecated as of '
 
1526
                             '0.14.  The section "/a/c" has been '
 
1527
                             'converted to use policies.'],
 
1528
                            self.my_config.set_user_option,
 
1529
                            'foo', 'bar', store=config.STORE_LOCATION)
 
1530
        self.assertEqual([('reload',),
 
1531
                          ('__contains__', '/a/c'),
1029
1532
                          ('__contains__', '/a/c/'),
1030
1533
                          ('__setitem__', '/a/c', {}),
1031
1534
                          ('__getitem__', '/a/c'),
1060
1563
        self.assertEqual('bzr', my_config.get_bzr_remote_path())
1061
1564
        my_config.set_user_option('bzr_remote_path', '/path-bzr')
1062
1565
        self.assertEqual('/path-bzr', my_config.get_bzr_remote_path())
1063
 
        os.environ['BZR_REMOTE_PATH'] = '/environ-bzr'
 
1566
        self.overrideEnv('BZR_REMOTE_PATH', '/environ-bzr')
1064
1567
        self.assertEqual('/environ-bzr', my_config.get_bzr_remote_path())
1065
1568
 
1066
1569
 
1074
1577
option = exact
1075
1578
"""
1076
1579
 
1077
 
 
1078
1580
class TestBranchConfigItems(tests.TestCaseInTempDir):
1079
1581
 
1080
1582
    def get_branch_config(self, global_config=None, location=None,
1081
1583
                          location_config=None, branch_data_config=None):
1082
 
        my_config = config.BranchConfig(FakeBranch(location))
 
1584
        my_branch = FakeBranch(location)
1083
1585
        if global_config is not None:
1084
 
            global_file = StringIO(global_config.encode('utf-8'))
1085
 
            my_config._get_global_config()._get_parser(global_file)
1086
 
        self.my_location_config = my_config._get_location_config()
 
1586
            my_global_config = config.GlobalConfig.from_string(global_config,
 
1587
                                                               save=True)
1087
1588
        if location_config is not None:
1088
 
            location_file = StringIO(location_config.encode('utf-8'))
1089
 
            self.my_location_config._get_parser(location_file)
 
1589
            my_location_config = config.LocationConfig.from_string(
 
1590
                location_config, my_branch.base, save=True)
 
1591
        my_config = config.BranchConfig(my_branch)
1090
1592
        if branch_data_config is not None:
1091
1593
            my_config.branch.control_files.files['branch.conf'] = \
1092
1594
                branch_data_config
1106
1608
                         my_config.username())
1107
1609
 
1108
1610
    def test_not_set_in_branch(self):
1109
 
        my_config = self.get_branch_config(sample_config_text)
 
1611
        my_config = self.get_branch_config(global_config=sample_config_text)
1110
1612
        self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
1111
1613
                         my_config._get_user_id())
1112
1614
        my_config.branch.control_files.files['email'] = "John"
1113
1615
        self.assertEqual("John", my_config._get_user_id())
1114
1616
 
1115
1617
    def test_BZR_EMAIL_OVERRIDES(self):
1116
 
        os.environ['BZR_EMAIL'] = "Robert Collins <robertc@example.org>"
 
1618
        self.overrideEnv('BZR_EMAIL', "Robert Collins <robertc@example.org>")
1117
1619
        branch = FakeBranch()
1118
1620
        my_config = config.BranchConfig(branch)
1119
1621
        self.assertEqual("Robert Collins <robertc@example.org>",
1136
1638
 
1137
1639
    def test_gpg_signing_command(self):
1138
1640
        my_config = self.get_branch_config(
 
1641
            global_config=sample_config_text,
1139
1642
            # branch data cannot set gpg_signing_command
1140
1643
            branch_data_config="gpg_signing_command=pgp")
1141
 
        config_file = StringIO(sample_config_text.encode('utf-8'))
1142
 
        my_config._get_global_config()._get_parser(config_file)
1143
1644
        self.assertEqual('gnome-gpg', my_config.gpg_signing_command())
1144
1645
 
1145
1646
    def test_get_user_option_global(self):
1146
 
        branch = FakeBranch()
1147
 
        my_config = config.BranchConfig(branch)
1148
 
        config_file = StringIO(sample_config_text.encode('utf-8'))
1149
 
        (my_config._get_global_config()._get_parser(config_file))
 
1647
        my_config = self.get_branch_config(global_config=sample_config_text)
1150
1648
        self.assertEqual('something',
1151
1649
                         my_config.get_user_option('user_global_option'))
1152
1650
 
1153
1651
    def test_post_commit_default(self):
1154
 
        branch = FakeBranch()
1155
 
        my_config = self.get_branch_config(sample_config_text, '/a/c',
1156
 
                                           sample_branches_text)
 
1652
        my_config = self.get_branch_config(global_config=sample_config_text,
 
1653
                                      location='/a/c',
 
1654
                                      location_config=sample_branches_text)
1157
1655
        self.assertEqual(my_config.branch.base, '/a/c')
1158
1656
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1159
1657
                         my_config.post_commit())
1160
1658
        my_config.set_user_option('post_commit', 'rmtree_root')
1161
 
        # post-commit is ignored when bresent in branch data
 
1659
        # post-commit is ignored when present in branch data
1162
1660
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1163
1661
                         my_config.post_commit())
1164
1662
        my_config.set_user_option('post_commit', 'rmtree_root',
1166
1664
        self.assertEqual('rmtree_root', my_config.post_commit())
1167
1665
 
1168
1666
    def test_config_precedence(self):
 
1667
        # FIXME: eager test, luckily no persitent config file makes it fail
 
1668
        # -- vila 20100716
1169
1669
        my_config = self.get_branch_config(global_config=precedence_global)
1170
1670
        self.assertEqual(my_config.get_user_option('option'), 'global')
1171
1671
        my_config = self.get_branch_config(global_config=precedence_global,
1172
 
                                      branch_data_config=precedence_branch)
 
1672
                                           branch_data_config=precedence_branch)
1173
1673
        self.assertEqual(my_config.get_user_option('option'), 'branch')
1174
 
        my_config = self.get_branch_config(global_config=precedence_global,
1175
 
                                      branch_data_config=precedence_branch,
1176
 
                                      location_config=precedence_location)
 
1674
        my_config = self.get_branch_config(
 
1675
            global_config=precedence_global,
 
1676
            branch_data_config=precedence_branch,
 
1677
            location_config=precedence_location)
1177
1678
        self.assertEqual(my_config.get_user_option('option'), 'recurse')
1178
 
        my_config = self.get_branch_config(global_config=precedence_global,
1179
 
                                      branch_data_config=precedence_branch,
1180
 
                                      location_config=precedence_location,
1181
 
                                      location='http://example.com/specific')
 
1679
        my_config = self.get_branch_config(
 
1680
            global_config=precedence_global,
 
1681
            branch_data_config=precedence_branch,
 
1682
            location_config=precedence_location,
 
1683
            location='http://example.com/specific')
1182
1684
        self.assertEqual(my_config.get_user_option('option'), 'exact')
1183
1685
 
1184
1686
    def test_get_mail_client(self):
1312
1814
        self.assertIs(None, bzrdir_config.get_default_stack_on())
1313
1815
 
1314
1816
 
 
1817
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
 
1818
 
 
1819
    def setUp(self):
 
1820
        super(TestConfigGetOptions, self).setUp()
 
1821
        create_configs(self)
 
1822
 
 
1823
    # One variable in none of the above
 
1824
    def test_no_variable(self):
 
1825
        # Using branch should query branch, locations and bazaar
 
1826
        self.assertOptions([], self.branch_config)
 
1827
 
 
1828
    def test_option_in_bazaar(self):
 
1829
        self.bazaar_config.set_user_option('file', 'bazaar')
 
1830
        self.assertOptions([('file', 'bazaar', 'DEFAULT', 'bazaar')],
 
1831
                           self.bazaar_config)
 
1832
 
 
1833
    def test_option_in_locations(self):
 
1834
        self.locations_config.set_user_option('file', 'locations')
 
1835
        self.assertOptions(
 
1836
            [('file', 'locations', self.tree.basedir, 'locations')],
 
1837
            self.locations_config)
 
1838
 
 
1839
    def test_option_in_branch(self):
 
1840
        self.branch_config.set_user_option('file', 'branch')
 
1841
        self.assertOptions([('file', 'branch', 'DEFAULT', 'branch')],
 
1842
                           self.branch_config)
 
1843
 
 
1844
    def test_option_in_bazaar_and_branch(self):
 
1845
        self.bazaar_config.set_user_option('file', 'bazaar')
 
1846
        self.branch_config.set_user_option('file', 'branch')
 
1847
        self.assertOptions([('file', 'branch', 'DEFAULT', 'branch'),
 
1848
                            ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
 
1849
                           self.branch_config)
 
1850
 
 
1851
    def test_option_in_branch_and_locations(self):
 
1852
        # Hmm, locations override branch :-/
 
1853
        self.locations_config.set_user_option('file', 'locations')
 
1854
        self.branch_config.set_user_option('file', 'branch')
 
1855
        self.assertOptions(
 
1856
            [('file', 'locations', self.tree.basedir, 'locations'),
 
1857
             ('file', 'branch', 'DEFAULT', 'branch'),],
 
1858
            self.branch_config)
 
1859
 
 
1860
    def test_option_in_bazaar_locations_and_branch(self):
 
1861
        self.bazaar_config.set_user_option('file', 'bazaar')
 
1862
        self.locations_config.set_user_option('file', 'locations')
 
1863
        self.branch_config.set_user_option('file', 'branch')
 
1864
        self.assertOptions(
 
1865
            [('file', 'locations', self.tree.basedir, 'locations'),
 
1866
             ('file', 'branch', 'DEFAULT', 'branch'),
 
1867
             ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
 
1868
            self.branch_config)
 
1869
 
 
1870
 
 
1871
class TestConfigRemoveOption(tests.TestCaseWithTransport, TestOptionsMixin):
 
1872
 
 
1873
    def setUp(self):
 
1874
        super(TestConfigRemoveOption, self).setUp()
 
1875
        create_configs_with_file_option(self)
 
1876
 
 
1877
    def test_remove_in_locations(self):
 
1878
        self.locations_config.remove_user_option('file', self.tree.basedir)
 
1879
        self.assertOptions(
 
1880
            [('file', 'branch', 'DEFAULT', 'branch'),
 
1881
             ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
 
1882
            self.branch_config)
 
1883
 
 
1884
    def test_remove_in_branch(self):
 
1885
        self.branch_config.remove_user_option('file')
 
1886
        self.assertOptions(
 
1887
            [('file', 'locations', self.tree.basedir, 'locations'),
 
1888
             ('file', 'bazaar', 'DEFAULT', 'bazaar'),],
 
1889
            self.branch_config)
 
1890
 
 
1891
    def test_remove_in_bazaar(self):
 
1892
        self.bazaar_config.remove_user_option('file')
 
1893
        self.assertOptions(
 
1894
            [('file', 'locations', self.tree.basedir, 'locations'),
 
1895
             ('file', 'branch', 'DEFAULT', 'branch'),],
 
1896
            self.branch_config)
 
1897
 
 
1898
 
 
1899
class TestConfigGetSections(tests.TestCaseWithTransport):
 
1900
 
 
1901
    def setUp(self):
 
1902
        super(TestConfigGetSections, self).setUp()
 
1903
        create_configs(self)
 
1904
 
 
1905
    def assertSectionNames(self, expected, conf, name=None):
 
1906
        """Check which sections are returned for a given config.
 
1907
 
 
1908
        If fallback configurations exist their sections can be included.
 
1909
 
 
1910
        :param expected: A list of section names.
 
1911
 
 
1912
        :param conf: The configuration that will be queried.
 
1913
 
 
1914
        :param name: An optional section name that will be passed to
 
1915
            get_sections().
 
1916
        """
 
1917
        sections = list(conf._get_sections(name))
 
1918
        self.assertLength(len(expected), sections)
 
1919
        self.assertEqual(expected, [name for name, _, _ in sections])
 
1920
 
 
1921
    def test_bazaar_default_section(self):
 
1922
        self.assertSectionNames(['DEFAULT'], self.bazaar_config)
 
1923
 
 
1924
    def test_locations_default_section(self):
 
1925
        # No sections are defined in an empty file
 
1926
        self.assertSectionNames([], self.locations_config)
 
1927
 
 
1928
    def test_locations_named_section(self):
 
1929
        self.locations_config.set_user_option('file', 'locations')
 
1930
        self.assertSectionNames([self.tree.basedir], self.locations_config)
 
1931
 
 
1932
    def test_locations_matching_sections(self):
 
1933
        loc_config = self.locations_config
 
1934
        loc_config.set_user_option('file', 'locations')
 
1935
        # We need to cheat a bit here to create an option in sections above and
 
1936
        # below the 'location' one.
 
1937
        parser = loc_config._get_parser()
 
1938
        # locations.cong deals with '/' ignoring native os.sep
 
1939
        location_names = self.tree.basedir.split('/')
 
1940
        parent = '/'.join(location_names[:-1])
 
1941
        child = '/'.join(location_names + ['child'])
 
1942
        parser[parent] = {}
 
1943
        parser[parent]['file'] = 'parent'
 
1944
        parser[child] = {}
 
1945
        parser[child]['file'] = 'child'
 
1946
        self.assertSectionNames([self.tree.basedir, parent], loc_config)
 
1947
 
 
1948
    def test_branch_data_default_section(self):
 
1949
        self.assertSectionNames([None],
 
1950
                                self.branch_config._get_branch_data_config())
 
1951
 
 
1952
    def test_branch_default_sections(self):
 
1953
        # No sections are defined in an empty locations file
 
1954
        self.assertSectionNames([None, 'DEFAULT'],
 
1955
                                self.branch_config)
 
1956
        # Unless we define an option
 
1957
        self.branch_config._get_location_config().set_user_option(
 
1958
            'file', 'locations')
 
1959
        self.assertSectionNames([self.tree.basedir, None, 'DEFAULT'],
 
1960
                                self.branch_config)
 
1961
 
 
1962
    def test_bazaar_named_section(self):
 
1963
        # We need to cheat as the API doesn't give direct access to sections
 
1964
        # other than DEFAULT.
 
1965
        self.bazaar_config.set_alias('bazaar', 'bzr')
 
1966
        self.assertSectionNames(['ALIASES'], self.bazaar_config, 'ALIASES')
 
1967
 
 
1968
 
1315
1969
class TestAuthenticationConfigFile(tests.TestCase):
1316
1970
    """Test the authentication.conf file matching"""
1317
1971
 
1812
2466
# test_user_prompted ?
1813
2467
class TestAuthenticationRing(tests.TestCaseWithTransport):
1814
2468
    pass
 
2469
 
 
2470
 
 
2471
class TestAutoUserId(tests.TestCase):
 
2472
    """Test inferring an automatic user name."""
 
2473
 
 
2474
    def test_auto_user_id(self):
 
2475
        """Automatic inference of user name.
 
2476
        
 
2477
        This is a bit hard to test in an isolated way, because it depends on
 
2478
        system functions that go direct to /etc or perhaps somewhere else.
 
2479
        But it's reasonable to say that on Unix, with an /etc/mailname, we ought
 
2480
        to be able to choose a user name with no configuration.
 
2481
        """
 
2482
        if sys.platform == 'win32':
 
2483
            raise TestSkipped("User name inference not implemented on win32")
 
2484
        realname, address = config._auto_user_id()
 
2485
        if os.path.exists('/etc/mailname'):
 
2486
            self.assertTrue(realname)
 
2487
            self.assertTrue(address)
 
2488
        else:
 
2489
            self.assertEquals((None, None), (realname, address))
 
2490