/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_config.py

  • Committer: Robert Collins
  • Date: 2007-04-19 02:27:44 UTC
  • mto: This revision was merged to the branch mainline in revision 2426.
  • Revision ID: robertc@robertcollins.net-20070419022744-pfdqz42kp1wizh43
``make docs`` now creates a man page at ``man1/bzr.1`` fixing bug 107388.
(Robert Collins)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005-2011 Canonical Ltd
 
1
# Copyright (C) 2005, 2006 Canonical Ltd
 
2
#   Authors: Robert Collins <robert.collins@canonical.com>
2
3
#
3
4
# This program is free software; you can redistribute it and/or modify
4
5
# it under the terms of the GNU General Public License as published by
12
13
#
13
14
# You should have received a copy of the GNU General Public License
14
15
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
17
 
17
18
"""Tests for finding and reading the bzr config file[s]."""
18
19
# import system imports here
 
20
from bzrlib.util.configobj.configobj import ConfigObj, ConfigObjError
19
21
from cStringIO import StringIO
20
22
import os
21
23
import sys
22
 
import threading
23
 
 
24
 
 
25
 
from testtools import matchers
26
24
 
27
25
#import bzrlib specific imports here
28
26
from bzrlib import (
29
 
    branch,
30
 
    bzrdir,
31
27
    config,
32
 
    diff,
33
28
    errors,
34
29
    osutils,
35
 
    mail_client,
36
 
    mergetools,
37
 
    ui,
38
30
    urlutils,
39
 
    tests,
40
 
    trace,
41
 
    transport,
42
 
    )
43
 
from bzrlib.tests import (
44
 
    features,
45
 
    TestSkipped,
46
 
    scenarios,
47
 
    )
48
 
from bzrlib.util.configobj import configobj
49
 
 
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
 
31
    )
 
32
from bzrlib.branch import Branch
 
33
from bzrlib.bzrdir import BzrDir
 
34
from bzrlib.tests import TestCase, TestCaseInTempDir, TestCaseWithTransport
64
35
 
65
36
 
66
37
sample_long_alias="log -r-15..-1 --line"
68
39
[DEFAULT]
69
40
email=Erik B\u00e5gfors <erik@bagfors.nu>
70
41
editor=vim
71
 
change_editor=vimdiff -of @new_path @old_path
72
42
gpg_signing_command=gnome-gpg
73
43
log_format=short
74
44
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
78
45
[ALIASES]
79
46
h=help
80
47
ll=""" + sample_long_alias + "\n"
133
100
"""
134
101
 
135
102
 
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
 
 
179
103
class InstrumentedConfigObj(object):
180
104
    """A config obj look-enough-alike to record calls made to it."""
181
105
 
200
124
        self._calls.append(('keys',))
201
125
        return []
202
126
 
203
 
    def reload(self):
204
 
        self._calls.append(('reload',))
205
 
 
206
127
    def write(self, arg):
207
128
        self._calls.append(('write',))
208
129
 
222
143
            self.base = "http://example.com/branches/demo"
223
144
        else:
224
145
            self.base = base
225
 
        self._transport = self.control_files = \
226
 
            FakeControlFilesAndTransport(user_id=user_id)
227
 
 
228
 
    def _get_config(self):
229
 
        return config.TransportConfig(self._transport, 'branch.conf')
 
146
        self.control_files = FakeControlFiles(user_id=user_id)
230
147
 
231
148
    def lock_write(self):
232
149
        pass
235
152
        pass
236
153
 
237
154
 
238
 
class FakeControlFilesAndTransport(object):
 
155
class FakeControlFiles(object):
239
156
 
240
157
    def __init__(self, user_id=None):
 
158
        self.email = user_id
241
159
        self.files = {}
242
 
        if user_id:
243
 
            self.files['email'] = user_id
244
 
        self._transport = self
245
160
 
246
161
    def get_utf8(self, filename):
247
 
        # from LockableFiles
248
 
        raise AssertionError("get_utf8 should no longer be used")
 
162
        if filename != 'email':
 
163
            raise NotImplementedError
 
164
        if self.email is not None:
 
165
            return StringIO(self.email)
 
166
        raise errors.NoSuchFile(filename)
249
167
 
250
168
    def get(self, filename):
251
 
        # from Transport
252
169
        try:
253
170
            return StringIO(self.files[filename])
254
171
        except KeyError:
255
172
            raise errors.NoSuchFile(filename)
256
173
 
257
 
    def get_bytes(self, filename):
258
 
        # from Transport
259
 
        try:
260
 
            return self.files[filename]
261
 
        except KeyError:
262
 
            raise errors.NoSuchFile(filename)
263
 
 
264
174
    def put(self, filename, fileobj):
265
175
        self.files[filename] = fileobj.read()
266
176
 
267
 
    def put_file(self, filename, fileobj):
268
 
        return self.put(filename, fileobj)
269
 
 
270
177
 
271
178
class InstrumentedConfig(config.Config):
272
179
    """An instrumented config that supplies stubs for template methods."""
273
 
 
 
180
    
274
181
    def __init__(self):
275
182
        super(InstrumentedConfig, self).__init__()
276
183
        self._calls = []
284
191
        self._calls.append('_get_signature_checking')
285
192
        return self._signatures
286
193
 
287
 
    def _get_change_editor(self):
288
 
        self._calls.append('_get_change_editor')
289
 
        return 'vimdiff -fo @new_path @old_path'
290
 
 
291
194
 
292
195
bool_config = """[DEFAULT]
293
196
active = true
296
199
active = True
297
200
nonactive = False
298
201
"""
299
 
 
300
 
 
301
 
class TestConfigObj(tests.TestCase):
302
 
 
 
202
class TestConfigObj(TestCase):
303
203
    def test_get_bool(self):
304
 
        co = config.ConfigObj(StringIO(bool_config))
 
204
        from bzrlib.config import ConfigObj
 
205
        co = ConfigObj(StringIO(bool_config))
305
206
        self.assertIs(co.get_bool('DEFAULT', 'active'), True)
306
207
        self.assertIs(co.get_bool('DEFAULT', 'inactive'), False)
307
208
        self.assertIs(co.get_bool('UPPERCASE', 'active'), True)
308
209
        self.assertIs(co.get_bool('UPPERCASE', 'nonactive'), False)
309
210
 
310
 
    def test_hash_sign_in_value(self):
311
 
        """
312
 
        Before 4.5.0, ConfigObj did not quote # signs in values, so they'd be
313
 
        treated as comments when read in again. (#86838)
314
 
        """
315
 
        co = config.ConfigObj()
316
 
        co['test'] = 'foo#bar'
317
 
        outfile = StringIO()
318
 
        co.write(outfile=outfile)
319
 
        lines = outfile.getvalue().splitlines()
320
 
        self.assertEqual(lines, ['test = "foo#bar"'])
321
 
        co2 = config.ConfigObj(lines)
322
 
        self.assertEqual(co2['test'], 'foo#bar')
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
 
 
348
 
 
349
 
erroneous_config = """[section] # line 1
350
 
good=good # line 2
351
 
[section] # line 3
352
 
whocares=notme # line 4
353
 
"""
354
 
 
355
 
 
356
 
class TestConfigObjErrors(tests.TestCase):
357
 
 
358
 
    def test_duplicate_section_name_error_line(self):
359
 
        try:
360
 
            co = configobj.ConfigObj(StringIO(erroneous_config),
361
 
                                     raise_errors=True)
362
 
        except config.configobj.DuplicateError, e:
363
 
            self.assertEqual(3, e.line_number)
364
 
        else:
365
 
            self.fail('Error in config file not detected')
366
 
 
367
 
 
368
 
class TestConfig(tests.TestCase):
 
211
 
 
212
class TestConfig(TestCase):
369
213
 
370
214
    def test_constructs(self):
371
215
        config.Config()
372
 
 
 
216
 
373
217
    def test_no_default_editor(self):
374
218
        self.assertRaises(NotImplementedError, config.Config().get_editor)
375
219
 
420
264
        my_config = config.Config()
421
265
        self.assertEqual('long', my_config.log_format())
422
266
 
423
 
    def test_get_change_editor(self):
424
 
        my_config = InstrumentedConfig()
425
 
        change_editor = my_config.get_change_editor('old_tree', 'new_tree')
426
 
        self.assertEqual(['_get_change_editor'], my_config._calls)
427
 
        self.assertIs(diff.DiffFromTool, change_editor.__class__)
428
 
        self.assertEqual(['vimdiff', '-fo', '@new_path', '@old_path'],
429
 
                         change_editor.command_template)
430
 
 
431
 
 
432
 
class TestConfigPath(tests.TestCase):
 
267
 
 
268
class TestConfigPath(TestCase):
433
269
 
434
270
    def setUp(self):
435
271
        super(TestConfigPath, self).setUp()
436
 
        self.overrideEnv('HOME', '/home/bogus')
437
 
        self.overrideEnv('XDG_CACHE_DIR', '')
 
272
        os.environ['HOME'] = '/home/bogus'
438
273
        if sys.platform == 'win32':
439
 
            self.overrideEnv(
440
 
                'BZR_HOME', r'C:\Documents and Settings\bogus\Application Data')
441
 
            self.bzr_home = \
442
 
                'C:/Documents and Settings/bogus/Application Data/bazaar/2.0'
443
 
        else:
444
 
            self.bzr_home = '/home/bogus/.bazaar'
 
274
            os.environ['BZR_HOME'] = \
 
275
                r'C:\Documents and Settings\bogus\Application Data'
445
276
 
446
277
    def test_config_dir(self):
447
 
        self.assertEqual(config.config_dir(), self.bzr_home)
 
278
        if sys.platform == 'win32':
 
279
            self.assertEqual(config.config_dir(), 
 
280
                'C:/Documents and Settings/bogus/Application Data/bazaar/2.0')
 
281
        else:
 
282
            self.assertEqual(config.config_dir(), '/home/bogus/.bazaar')
448
283
 
449
284
    def test_config_filename(self):
450
 
        self.assertEqual(config.config_filename(),
451
 
                         self.bzr_home + '/bazaar.conf')
 
285
        if sys.platform == 'win32':
 
286
            self.assertEqual(config.config_filename(), 
 
287
                'C:/Documents and Settings/bogus/Application Data/bazaar/2.0/bazaar.conf')
 
288
        else:
 
289
            self.assertEqual(config.config_filename(),
 
290
                             '/home/bogus/.bazaar/bazaar.conf')
 
291
 
 
292
    def test_branches_config_filename(self):
 
293
        if sys.platform == 'win32':
 
294
            self.assertEqual(config.branches_config_filename(), 
 
295
                'C:/Documents and Settings/bogus/Application Data/bazaar/2.0/branches.conf')
 
296
        else:
 
297
            self.assertEqual(config.branches_config_filename(),
 
298
                             '/home/bogus/.bazaar/branches.conf')
452
299
 
453
300
    def test_locations_config_filename(self):
454
 
        self.assertEqual(config.locations_config_filename(),
455
 
                         self.bzr_home + '/locations.conf')
456
 
 
457
 
    def test_authentication_config_filename(self):
458
 
        self.assertEqual(config.authentication_config_filename(),
459
 
                         self.bzr_home + '/authentication.conf')
460
 
 
461
 
    def test_xdg_cache_dir(self):
462
 
        self.assertEqual(config.xdg_cache_dir(),
463
 
            '/home/bogus/.cache')
464
 
 
465
 
 
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):
495
 
 
496
 
    def make_config_parser(self, s):
497
 
        conf = config.IniBasedConfig.from_string(s)
498
 
        return conf, conf._get_parser()
499
 
 
500
 
 
501
 
class TestIniConfigBuilding(TestIniConfig):
 
301
        if sys.platform == 'win32':
 
302
            self.assertEqual(config.locations_config_filename(), 
 
303
                'C:/Documents and Settings/bogus/Application Data/bazaar/2.0/locations.conf')
 
304
        else:
 
305
            self.assertEqual(config.locations_config_filename(),
 
306
                             '/home/bogus/.bazaar/locations.conf')
 
307
 
 
308
class TestIniConfig(TestCase):
502
309
 
503
310
    def test_contructs(self):
504
 
        my_config = config.IniBasedConfig()
 
311
        my_config = config.IniBasedConfig("nothing")
505
312
 
506
313
    def test_from_fp(self):
507
 
        my_config = config.IniBasedConfig.from_string(sample_config_text)
508
 
        self.assertIsInstance(my_config._get_parser(), configobj.ConfigObj)
 
314
        config_file = StringIO(sample_config_text.encode('utf-8'))
 
315
        my_config = config.IniBasedConfig(None)
 
316
        self.failUnless(
 
317
            isinstance(my_config._get_parser(file=config_file),
 
318
                        ConfigObj))
509
319
 
510
320
    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):
537
321
        config_file = StringIO(sample_config_text.encode('utf-8'))
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'))
895
 
 
896
 
 
897
 
class TestGetUserOptionAs(TestIniConfig):
898
 
 
899
 
    def test_get_user_option_as_bool(self):
900
 
        conf, parser = self.make_config_parser("""
901
 
a_true_bool = true
902
 
a_false_bool = 0
903
 
an_invalid_bool = maybe
904
 
a_list = hmm, who knows ? # This is interpreted as a list !
905
 
""")
906
 
        get_bool = conf.get_user_option_as_bool
907
 
        self.assertEqual(True, get_bool('a_true_bool'))
908
 
        self.assertEqual(False, get_bool('a_false_bool'))
909
 
        warnings = []
910
 
        def warning(*args):
911
 
            warnings.append(args[0] % args[1:])
912
 
        self.overrideAttr(trace, 'warning', warning)
913
 
        msg = 'Value "%s" is not a boolean for "%s"'
914
 
        self.assertIs(None, get_bool('an_invalid_bool'))
915
 
        self.assertEquals(msg % ('maybe', 'an_invalid_bool'), warnings[0])
916
 
        warnings = []
917
 
        self.assertIs(None, get_bool('not_defined_in_this_config'))
918
 
        self.assertEquals([], warnings)
919
 
 
920
 
    def test_get_user_option_as_list(self):
921
 
        conf, parser = self.make_config_parser("""
922
 
a_list = a,b,c
923
 
length_1 = 1,
924
 
one_item = x
925
 
""")
926
 
        get_list = conf.get_user_option_as_list
927
 
        self.assertEqual(['a', 'b', 'c'], get_list('a_list'))
928
 
        self.assertEqual(['1'], get_list('length_1'))
929
 
        self.assertEqual('x', conf.get_user_option('one_item'))
930
 
        # automatically cast to list
931
 
        self.assertEqual(['x'], get_list('one_item'))
932
 
 
933
 
 
934
 
class TestSupressWarning(TestIniConfig):
935
 
 
936
 
    def make_warnings_config(self, s):
937
 
        conf, parser = self.make_config_parser(s)
938
 
        return conf.suppress_warning
939
 
 
940
 
    def test_suppress_warning_unknown(self):
941
 
        suppress_warning = self.make_warnings_config('')
942
 
        self.assertEqual(False, suppress_warning('unknown_warning'))
943
 
 
944
 
    def test_suppress_warning_known(self):
945
 
        suppress_warning = self.make_warnings_config('suppress_warnings=a,b')
946
 
        self.assertEqual(False, suppress_warning('c'))
947
 
        self.assertEqual(True, suppress_warning('a'))
948
 
        self.assertEqual(True, suppress_warning('b'))
949
 
 
950
 
 
951
 
class TestGetConfig(tests.TestCase):
 
322
        my_config = config.IniBasedConfig(None)
 
323
        parser = my_config._get_parser(file=config_file)
 
324
        self.failUnless(my_config._get_parser() is parser)
 
325
 
 
326
 
 
327
class TestGetConfig(TestCase):
952
328
 
953
329
    def test_constructs(self):
954
330
        my_config = config.GlobalConfig()
955
331
 
956
332
    def test_calls_read_filenames(self):
957
 
        # replace the class that is constructed, to check its parameters
 
333
        # replace the class that is constructured, to check its parameters
958
334
        oldparserclass = config.ConfigObj
959
335
        config.ConfigObj = InstrumentedConfigObj
960
336
        my_config = config.GlobalConfig()
962
338
            parser = my_config._get_parser()
963
339
        finally:
964
340
            config.ConfigObj = oldparserclass
965
 
        self.assertIsInstance(parser, InstrumentedConfigObj)
 
341
        self.failUnless(isinstance(parser, InstrumentedConfigObj))
966
342
        self.assertEqual(parser._calls, [('__init__', config.config_filename(),
967
343
                                          'utf-8')])
968
344
 
969
345
 
970
 
class TestBranchConfig(tests.TestCaseWithTransport):
 
346
class TestBranchConfig(TestCaseWithTransport):
971
347
 
972
348
    def test_constructs(self):
973
349
        branch = FakeBranch()
979
355
        my_config = config.BranchConfig(branch)
980
356
        location_config = my_config._get_location_config()
981
357
        self.assertEqual(branch.base, location_config.location)
982
 
        self.assertIs(location_config, my_config._get_location_config())
 
358
        self.failUnless(location_config is my_config._get_location_config())
983
359
 
984
360
    def test_get_config(self):
985
361
        """The Branch.get_config method works properly"""
986
 
        b = bzrdir.BzrDir.create_standalone_workingtree('.').branch
 
362
        b = BzrDir.create_standalone_workingtree('.').branch
987
363
        my_config = b.get_config()
988
364
        self.assertIs(my_config.get_user_option('wacky'), None)
989
365
        my_config.set_user_option('wacky', 'unlikely')
990
366
        self.assertEqual(my_config.get_user_option('wacky'), 'unlikely')
991
367
 
992
368
        # Ensure we get the same thing if we start again
993
 
        b2 = branch.Branch.open('.')
 
369
        b2 = Branch.open('.')
994
370
        my_config2 = b2.get_config()
995
371
        self.assertEqual(my_config2.get_user_option('wacky'), 'unlikely')
996
372
 
1005
381
        branch = self.make_branch('branch')
1006
382
        self.assertEqual('branch', branch.nick)
1007
383
 
 
384
        locations = config.locations_config_filename()
 
385
        config.ensure_config_dir_exists()
1008
386
        local_url = urlutils.local_path_to_url('branch')
1009
 
        conf = config.LocationConfig.from_string(
1010
 
            '[%s]\nnickname = foobar' % (local_url,),
1011
 
            local_url, save=True)
 
387
        open(locations, 'wb').write('[%s]\nnickname = foobar' 
 
388
                                    % (local_url,))
1012
389
        self.assertEqual('foobar', branch.nick)
1013
390
 
1014
391
    def test_config_local_path(self):
1016
393
        branch = self.make_branch('branch')
1017
394
        self.assertEqual('branch', branch.nick)
1018
395
 
1019
 
        local_path = osutils.getcwd().encode('utf8')
1020
 
        conf = config.LocationConfig.from_string(
1021
 
            '[%s/branch]\nnickname = barry' % (local_path,),
1022
 
            'branch',  save=True)
 
396
        locations = config.locations_config_filename()
 
397
        config.ensure_config_dir_exists()
 
398
        open(locations, 'wb').write('[%s/branch]\nnickname = barry' 
 
399
                                    % (osutils.getcwd().encode('utf8'),))
1023
400
        self.assertEqual('barry', branch.nick)
1024
401
 
1025
402
    def test_config_creates_local(self):
1026
403
        """Creating a new entry in config uses a local path."""
1027
404
        branch = self.make_branch('branch', format='knit')
1028
405
        branch.set_push_location('http://foobar')
 
406
        locations = config.locations_config_filename()
1029
407
        local_path = osutils.getcwd().encode('utf8')
1030
408
        # Surprisingly ConfigObj doesn't create a trailing newline
1031
 
        self.check_file_contents(config.locations_config_filename(),
1032
 
                                 '[%s/branch]\n'
1033
 
                                 'push_location = http://foobar\n'
1034
 
                                 'push_location:policy = norecurse\n'
1035
 
                                 % (local_path,))
 
409
        self.check_file_contents(locations,
 
410
            '[%s/branch]\npush_location = http://foobar\npush_location:policy = norecurse' % (local_path,))
1036
411
 
1037
412
    def test_autonick_urlencoded(self):
1038
413
        b = self.make_branch('!repo')
1039
414
        self.assertEqual('!repo', b.get_config().get_nickname())
1040
415
 
1041
 
    def test_warn_if_masked(self):
1042
 
        warnings = []
1043
 
        def warning(*args):
1044
 
            warnings.append(args[0] % args[1:])
1045
 
        self.overrideAttr(trace, 'warning', warning)
1046
 
 
1047
 
        def set_option(store, warn_masked=True):
1048
 
            warnings[:] = []
1049
 
            conf.set_user_option('example_option', repr(store), store=store,
1050
 
                                 warn_masked=warn_masked)
1051
 
        def assertWarning(warning):
1052
 
            if warning is None:
1053
 
                self.assertEqual(0, len(warnings))
1054
 
            else:
1055
 
                self.assertEqual(1, len(warnings))
1056
 
                self.assertEqual(warning, warnings[0])
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):
 
416
 
 
417
class TestGlobalConfigItems(TestCase):
1076
418
 
1077
419
    def test_user_id(self):
1078
 
        my_config = config.GlobalConfig.from_string(sample_config_text)
 
420
        config_file = StringIO(sample_config_text.encode('utf-8'))
 
421
        my_config = config.GlobalConfig()
 
422
        my_config._parser = my_config._get_parser(file=config_file)
1079
423
        self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
1080
424
                         my_config._get_user_id())
1081
425
 
1082
426
    def test_absent_user_id(self):
 
427
        config_file = StringIO("")
1083
428
        my_config = config.GlobalConfig()
 
429
        my_config._parser = my_config._get_parser(file=config_file)
1084
430
        self.assertEqual(None, my_config._get_user_id())
1085
431
 
1086
432
    def test_configured_editor(self):
1087
 
        my_config = config.GlobalConfig.from_string(sample_config_text)
 
433
        config_file = StringIO(sample_config_text.encode('utf-8'))
 
434
        my_config = config.GlobalConfig()
 
435
        my_config._parser = my_config._get_parser(file=config_file)
1088
436
        self.assertEqual("vim", my_config.get_editor())
1089
437
 
1090
438
    def test_signatures_always(self):
1091
 
        my_config = config.GlobalConfig.from_string(sample_always_signatures)
 
439
        config_file = StringIO(sample_always_signatures)
 
440
        my_config = config.GlobalConfig()
 
441
        my_config._parser = my_config._get_parser(file=config_file)
1092
442
        self.assertEqual(config.CHECK_NEVER,
1093
443
                         my_config.signature_checking())
1094
444
        self.assertEqual(config.SIGN_ALWAYS,
1096
446
        self.assertEqual(True, my_config.signature_needed())
1097
447
 
1098
448
    def test_signatures_if_possible(self):
1099
 
        my_config = config.GlobalConfig.from_string(sample_maybe_signatures)
 
449
        config_file = StringIO(sample_maybe_signatures)
 
450
        my_config = config.GlobalConfig()
 
451
        my_config._parser = my_config._get_parser(file=config_file)
1100
452
        self.assertEqual(config.CHECK_NEVER,
1101
453
                         my_config.signature_checking())
1102
454
        self.assertEqual(config.SIGN_WHEN_REQUIRED,
1104
456
        self.assertEqual(False, my_config.signature_needed())
1105
457
 
1106
458
    def test_signatures_ignore(self):
1107
 
        my_config = config.GlobalConfig.from_string(sample_ignore_signatures)
 
459
        config_file = StringIO(sample_ignore_signatures)
 
460
        my_config = config.GlobalConfig()
 
461
        my_config._parser = my_config._get_parser(file=config_file)
1108
462
        self.assertEqual(config.CHECK_ALWAYS,
1109
463
                         my_config.signature_checking())
1110
464
        self.assertEqual(config.SIGN_NEVER,
1112
466
        self.assertEqual(False, my_config.signature_needed())
1113
467
 
1114
468
    def _get_sample_config(self):
1115
 
        my_config = config.GlobalConfig.from_string(sample_config_text)
 
469
        config_file = StringIO(sample_config_text.encode('utf-8'))
 
470
        my_config = config.GlobalConfig()
 
471
        my_config._parser = my_config._get_parser(file=config_file)
1116
472
        return my_config
1117
473
 
1118
474
    def test_gpg_signing_command(self):
1121
477
        self.assertEqual(False, my_config.signature_needed())
1122
478
 
1123
479
    def _get_empty_config(self):
 
480
        config_file = StringIO("")
1124
481
        my_config = config.GlobalConfig()
 
482
        my_config._parser = my_config._get_parser(file=config_file)
1125
483
        return my_config
1126
484
 
1127
485
    def test_gpg_signing_command_unset(self):
1136
494
        my_config = self._get_sample_config()
1137
495
        self.assertEqual("something",
1138
496
                         my_config.get_user_option('user_global_option'))
1139
 
 
 
497
        
1140
498
    def test_post_commit_default(self):
1141
499
        my_config = self._get_sample_config()
1142
500
        self.assertEqual(None, my_config.post_commit())
1149
507
        my_config = self._get_sample_config()
1150
508
        self.assertEqual('help', my_config.get_alias('h'))
1151
509
 
1152
 
    def test_get_aliases(self):
1153
 
        my_config = self._get_sample_config()
1154
 
        aliases = my_config.get_aliases()
1155
 
        self.assertEqual(2, len(aliases))
1156
 
        sorted_keys = sorted(aliases)
1157
 
        self.assertEqual('help', aliases[sorted_keys[0]])
1158
 
        self.assertEqual(sample_long_alias, aliases[sorted_keys[1]])
1159
 
 
1160
510
    def test_get_no_alias(self):
1161
511
        my_config = self._get_sample_config()
1162
512
        self.assertEqual(None, my_config.get_alias('foo'))
1165
515
        my_config = self._get_sample_config()
1166
516
        self.assertEqual(sample_long_alias, my_config.get_alias('ll'))
1167
517
 
1168
 
    def test_get_change_editor(self):
1169
 
        my_config = self._get_sample_config()
1170
 
        change_editor = my_config.get_change_editor('old', 'new')
1171
 
        self.assertIs(diff.DiffFromTool, change_editor.__class__)
1172
 
        self.assertEqual('vimdiff -of @new_path @old_path',
1173
 
                         ' '.join(change_editor.command_template))
1174
 
 
1175
 
    def test_get_no_change_editor(self):
1176
 
        my_config = self._get_empty_config()
1177
 
        change_editor = my_config.get_change_editor('old', 'new')
1178
 
        self.assertIs(None, change_editor)
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
 
 
1215
 
 
1216
 
class TestGlobalConfigSavingOptions(tests.TestCaseInTempDir):
1217
 
 
1218
 
    def test_empty(self):
1219
 
        my_config = config.GlobalConfig()
1220
 
        self.assertEqual(0, len(my_config.get_aliases()))
1221
 
 
1222
 
    def test_set_alias(self):
1223
 
        my_config = config.GlobalConfig()
1224
 
        alias_value = 'commit --strict'
1225
 
        my_config.set_alias('commit', alias_value)
1226
 
        new_config = config.GlobalConfig()
1227
 
        self.assertEqual(alias_value, new_config.get_alias('commit'))
1228
 
 
1229
 
    def test_remove_alias(self):
1230
 
        my_config = config.GlobalConfig()
1231
 
        my_config.set_alias('commit', 'commit --strict')
1232
 
        # Now remove the alias again.
1233
 
        my_config.unset_alias('commit')
1234
 
        new_config = config.GlobalConfig()
1235
 
        self.assertIs(None, new_config.get_alias('commit'))
1236
 
 
1237
 
 
1238
 
class TestLocationConfig(tests.TestCaseInTempDir, TestOptionsMixin):
 
518
 
 
519
class TestLocationConfig(TestCaseInTempDir):
1239
520
 
1240
521
    def test_constructs(self):
1241
522
        my_config = config.LocationConfig('http://example.com')
1245
526
        # This is testing the correct file names are provided.
1246
527
        # TODO: consolidate with the test for GlobalConfigs filename checks.
1247
528
        #
1248
 
        # replace the class that is constructed, to check its parameters
 
529
        # replace the class that is constructured, to check its parameters
1249
530
        oldparserclass = config.ConfigObj
1250
531
        config.ConfigObj = InstrumentedConfigObj
1251
532
        try:
1253
534
            parser = my_config._get_parser()
1254
535
        finally:
1255
536
            config.ConfigObj = oldparserclass
1256
 
        self.assertIsInstance(parser, InstrumentedConfigObj)
 
537
        self.failUnless(isinstance(parser, InstrumentedConfigObj))
1257
538
        self.assertEqual(parser._calls,
1258
539
                         [('__init__', config.locations_config_filename(),
1259
540
                           'utf-8')])
 
541
        config.ensure_config_dir_exists()
 
542
        #os.mkdir(config.config_dir())
 
543
        f = file(config.branches_config_filename(), 'wb')
 
544
        f.write('')
 
545
        f.close()
 
546
        oldparserclass = config.ConfigObj
 
547
        config.ConfigObj = InstrumentedConfigObj
 
548
        try:
 
549
            my_config = config.LocationConfig('http://www.example.com')
 
550
            parser = my_config._get_parser()
 
551
        finally:
 
552
            config.ConfigObj = oldparserclass
1260
553
 
1261
554
    def test_get_global_config(self):
1262
555
        my_config = config.BranchConfig(FakeBranch('http://example.com'))
1263
556
        global_config = 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()))
 
557
        self.failUnless(isinstance(global_config, config.GlobalConfig))
 
558
        self.failUnless(global_config is my_config._get_global_config())
1270
559
 
1271
560
    def test__get_matching_sections_no_match(self):
1272
561
        self.get_branch_config('/')
1273
 
        self.assertLocationMatching([])
1274
 
 
 
562
        self.assertEqual([], self.my_location_config._get_matching_sections())
 
563
        
1275
564
    def test__get_matching_sections_exact(self):
1276
565
        self.get_branch_config('http://www.example.com')
1277
 
        self.assertLocationMatching([('http://www.example.com', '')])
1278
 
 
 
566
        self.assertEqual([('http://www.example.com', '')],
 
567
                         self.my_location_config._get_matching_sections())
 
568
   
1279
569
    def test__get_matching_sections_suffix_does_not(self):
1280
570
        self.get_branch_config('http://www.example.com-com')
1281
 
        self.assertLocationMatching([])
 
571
        self.assertEqual([], self.my_location_config._get_matching_sections())
1282
572
 
1283
573
    def test__get_matching_sections_subdir_recursive(self):
1284
574
        self.get_branch_config('http://www.example.com/com')
1285
 
        self.assertLocationMatching([('http://www.example.com', 'com')])
 
575
        self.assertEqual([('http://www.example.com', 'com')],
 
576
                         self.my_location_config._get_matching_sections())
1286
577
 
1287
578
    def test__get_matching_sections_ignoreparent(self):
1288
579
        self.get_branch_config('http://www.example.com/ignoreparent')
1289
 
        self.assertLocationMatching([('http://www.example.com/ignoreparent',
1290
 
                                      '')])
 
580
        self.assertEqual([('http://www.example.com/ignoreparent', '')],
 
581
                         self.my_location_config._get_matching_sections())
1291
582
 
1292
583
    def test__get_matching_sections_ignoreparent_subdir(self):
1293
584
        self.get_branch_config(
1294
585
            'http://www.example.com/ignoreparent/childbranch')
1295
 
        self.assertLocationMatching([('http://www.example.com/ignoreparent',
1296
 
                                      'childbranch')])
 
586
        self.assertEqual([('http://www.example.com/ignoreparent', 'childbranch')],
 
587
                         self.my_location_config._get_matching_sections())
1297
588
 
1298
589
    def test__get_matching_sections_subdir_trailing_slash(self):
1299
590
        self.get_branch_config('/b')
1300
 
        self.assertLocationMatching([('/b/', '')])
 
591
        self.assertEqual([('/b/', '')],
 
592
                         self.my_location_config._get_matching_sections())
1301
593
 
1302
594
    def test__get_matching_sections_subdir_child(self):
1303
595
        self.get_branch_config('/a/foo')
1304
 
        self.assertLocationMatching([('/a/*', ''), ('/a/', 'foo')])
 
596
        self.assertEqual([('/a/*', ''), ('/a/', 'foo')],
 
597
                         self.my_location_config._get_matching_sections())
1305
598
 
1306
599
    def test__get_matching_sections_subdir_child_child(self):
1307
600
        self.get_branch_config('/a/foo/bar')
1308
 
        self.assertLocationMatching([('/a/*', 'bar'), ('/a/', 'foo/bar')])
 
601
        self.assertEqual([('/a/*', 'bar'), ('/a/', 'foo/bar')],
 
602
                         self.my_location_config._get_matching_sections())
1309
603
 
1310
604
    def test__get_matching_sections_trailing_slash_with_children(self):
1311
605
        self.get_branch_config('/a/')
1312
 
        self.assertLocationMatching([('/a/', '')])
 
606
        self.assertEqual([('/a/', '')],
 
607
                         self.my_location_config._get_matching_sections())
1313
608
 
1314
609
    def test__get_matching_sections_explicit_over_glob(self):
1315
610
        # XXX: 2006-09-08 jamesh
1317
612
        # was a config section for '/a/?', it would get precedence
1318
613
        # over '/a/c'.
1319
614
        self.get_branch_config('/a/c')
1320
 
        self.assertLocationMatching([('/a/c', ''), ('/a/*', ''), ('/a/', 'c')])
 
615
        self.assertEqual([('/a/c', ''), ('/a/*', ''), ('/a/', 'c')],
 
616
                         self.my_location_config._get_matching_sections())
1321
617
 
1322
618
    def test__get_option_policy_normal(self):
1323
619
        self.get_branch_config('http://www.example.com')
1345
641
            'http://www.example.com', 'appendpath_option'),
1346
642
            config.POLICY_APPENDPATH)
1347
643
 
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
 
 
1363
644
    def test_location_without_username(self):
1364
645
        self.get_branch_config('http://www.example.com/ignoreparent')
1365
646
        self.assertEqual(u'Erik B\u00e5gfors <erik@bagfors.nu>',
1388
669
        self.get_branch_config('/a/c')
1389
670
        self.assertEqual(config.CHECK_NEVER,
1390
671
                         self.my_config.signature_checking())
1391
 
 
 
672
        
1392
673
    def test_signatures_when_available(self):
1393
674
        self.get_branch_config('/a/', global_config=sample_ignore_signatures)
1394
675
        self.assertEqual(config.CHECK_IF_POSSIBLE,
1395
676
                         self.my_config.signature_checking())
1396
 
 
 
677
        
1397
678
    def test_signatures_always(self):
1398
679
        self.get_branch_config('/b')
1399
680
        self.assertEqual(config.CHECK_ALWAYS,
1400
681
                         self.my_config.signature_checking())
1401
 
 
 
682
        
1402
683
    def test_gpg_signing_command(self):
1403
684
        self.get_branch_config('/b')
1404
685
        self.assertEqual("gnome-gpg", self.my_config.gpg_signing_command())
1495
776
            self.my_location_config._get_option_policy(
1496
777
            'http://www.example.com/norecurse', 'normal_option'),
1497
778
            config.POLICY_NORECURSE)
 
779
        
1498
780
 
1499
781
    def test_post_commit_default(self):
1500
782
        self.get_branch_config('/a/c')
1501
783
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1502
784
                         self.my_config.post_commit())
1503
785
 
1504
 
    def get_branch_config(self, location, global_config=None,
1505
 
                          location_config=None):
1506
 
        my_branch = FakeBranch(location)
 
786
    def get_branch_config(self, location, global_config=None):
1507
787
        if global_config is None:
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()
 
788
            global_file = StringIO(sample_config_text.encode('utf-8'))
 
789
        else:
 
790
            global_file = StringIO(global_config.encode('utf-8'))
 
791
        branches_file = StringIO(sample_branches_text.encode('utf-8'))
 
792
        self.my_config = config.BranchConfig(FakeBranch(location))
 
793
        # Force location config to use specified file
 
794
        self.my_location_config = self.my_config._get_location_config()
 
795
        self.my_location_config._get_parser(branches_file)
 
796
        # Force global config to use specified file
 
797
        self.my_config._get_global_config()._get_parser(global_file)
1519
798
 
1520
799
    def test_set_user_setting_sets_and_saves(self):
1521
800
        self.get_branch_config('/a/c')
1522
801
        record = InstrumentedConfigObj("foo")
1523
802
        self.my_location_config._parser = record
1524
803
 
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'),
 
804
        real_mkdir = os.mkdir
 
805
        self.created = False
 
806
        def checked_mkdir(path, mode=0777):
 
807
            self.log('making directory: %s', path)
 
808
            real_mkdir(path, mode)
 
809
            self.created = True
 
810
 
 
811
        os.mkdir = checked_mkdir
 
812
        try:
 
813
            self.callDeprecated(['The recurse option is deprecated as of '
 
814
                                 '0.14.  The section "/a/c" has been '
 
815
                                 'converted to use policies.'],
 
816
                                self.my_config.set_user_option,
 
817
                                'foo', 'bar', store=config.STORE_LOCATION)
 
818
        finally:
 
819
            os.mkdir = real_mkdir
 
820
 
 
821
        self.failUnless(self.created, 'Failed to create ~/.bazaar')
 
822
        self.assertEqual([('__contains__', '/a/c'),
1532
823
                          ('__contains__', '/a/c/'),
1533
824
                          ('__setitem__', '/a/c', {}),
1534
825
                          ('__getitem__', '/a/c'),
1549
840
        self.assertIs(self.my_config.get_user_option('foo'), None)
1550
841
        self.my_config.set_user_option('foo', 'bar')
1551
842
        self.assertEqual(
1552
 
            self.my_config.branch.control_files.files['branch.conf'].strip(),
 
843
            self.my_config.branch.control_files.files['branch.conf'], 
1553
844
            'foo = bar')
1554
845
        self.assertEqual(self.my_config.get_user_option('foo'), 'bar')
1555
846
        self.my_config.set_user_option('foo', 'baz',
1557
848
        self.assertEqual(self.my_config.get_user_option('foo'), 'baz')
1558
849
        self.my_config.set_user_option('foo', 'qux')
1559
850
        self.assertEqual(self.my_config.get_user_option('foo'), 'baz')
1560
 
 
1561
 
    def test_get_bzr_remote_path(self):
1562
 
        my_config = config.LocationConfig('/a/c')
1563
 
        self.assertEqual('bzr', my_config.get_bzr_remote_path())
1564
 
        my_config.set_user_option('bzr_remote_path', '/path-bzr')
1565
 
        self.assertEqual('/path-bzr', my_config.get_bzr_remote_path())
1566
 
        self.overrideEnv('BZR_REMOTE_PATH', '/environ-bzr')
1567
 
        self.assertEqual('/environ-bzr', my_config.get_bzr_remote_path())
1568
 
 
 
851
        
1569
852
 
1570
853
precedence_global = 'option = global'
1571
854
precedence_branch = 'option = branch'
1577
860
option = exact
1578
861
"""
1579
862
 
1580
 
class TestBranchConfigItems(tests.TestCaseInTempDir):
1581
 
 
1582
 
    def get_branch_config(self, global_config=None, location=None,
 
863
 
 
864
class TestBranchConfigItems(TestCaseInTempDir):
 
865
 
 
866
    def get_branch_config(self, global_config=None, location=None, 
1583
867
                          location_config=None, branch_data_config=None):
1584
 
        my_branch = FakeBranch(location)
 
868
        my_config = config.BranchConfig(FakeBranch(location))
1585
869
        if global_config is not None:
1586
 
            my_global_config = config.GlobalConfig.from_string(global_config,
1587
 
                                                               save=True)
 
870
            global_file = StringIO(global_config.encode('utf-8'))
 
871
            my_config._get_global_config()._get_parser(global_file)
 
872
        self.my_location_config = my_config._get_location_config()
1588
873
        if location_config is not None:
1589
 
            my_location_config = config.LocationConfig.from_string(
1590
 
                location_config, my_branch.base, save=True)
1591
 
        my_config = config.BranchConfig(my_branch)
 
874
            location_file = StringIO(location_config.encode('utf-8'))
 
875
            self.my_location_config._get_parser(location_file)
1592
876
        if branch_data_config is not None:
1593
877
            my_config.branch.control_files.files['branch.conf'] = \
1594
878
                branch_data_config
1599
883
        my_config = config.BranchConfig(branch)
1600
884
        self.assertEqual("Robert Collins <robertc@example.net>",
1601
885
                         my_config.username())
1602
 
        my_config.branch.control_files.files['email'] = "John"
1603
 
        my_config.set_user_option('email',
 
886
        branch.control_files.email = "John"
 
887
        my_config.set_user_option('email', 
1604
888
                                  "Robert Collins <robertc@example.org>")
1605
889
        self.assertEqual("John", my_config.username())
1606
 
        del my_config.branch.control_files.files['email']
 
890
        branch.control_files.email = None
1607
891
        self.assertEqual("Robert Collins <robertc@example.org>",
1608
892
                         my_config.username())
1609
893
 
1610
894
    def test_not_set_in_branch(self):
1611
 
        my_config = self.get_branch_config(global_config=sample_config_text)
 
895
        my_config = self.get_branch_config(sample_config_text)
 
896
        my_config.branch.control_files.email = None
1612
897
        self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
1613
898
                         my_config._get_user_id())
1614
 
        my_config.branch.control_files.files['email'] = "John"
 
899
        my_config.branch.control_files.email = "John"
1615
900
        self.assertEqual("John", my_config._get_user_id())
1616
901
 
1617
902
    def test_BZR_EMAIL_OVERRIDES(self):
1618
 
        self.overrideEnv('BZR_EMAIL', "Robert Collins <robertc@example.org>")
 
903
        os.environ['BZR_EMAIL'] = "Robert Collins <robertc@example.org>"
1619
904
        branch = FakeBranch()
1620
905
        my_config = config.BranchConfig(branch)
1621
906
        self.assertEqual("Robert Collins <robertc@example.org>",
1622
907
                         my_config.username())
1623
 
 
 
908
    
1624
909
    def test_signatures_forced(self):
1625
910
        my_config = self.get_branch_config(
1626
911
            global_config=sample_always_signatures)
1638
923
 
1639
924
    def test_gpg_signing_command(self):
1640
925
        my_config = self.get_branch_config(
1641
 
            global_config=sample_config_text,
1642
926
            # branch data cannot set gpg_signing_command
1643
927
            branch_data_config="gpg_signing_command=pgp")
 
928
        config_file = StringIO(sample_config_text.encode('utf-8'))
 
929
        my_config._get_global_config()._get_parser(config_file)
1644
930
        self.assertEqual('gnome-gpg', my_config.gpg_signing_command())
1645
931
 
1646
932
    def test_get_user_option_global(self):
1647
 
        my_config = self.get_branch_config(global_config=sample_config_text)
 
933
        branch = FakeBranch()
 
934
        my_config = config.BranchConfig(branch)
 
935
        config_file = StringIO(sample_config_text.encode('utf-8'))
 
936
        (my_config._get_global_config()._get_parser(config_file))
1648
937
        self.assertEqual('something',
1649
938
                         my_config.get_user_option('user_global_option'))
1650
939
 
1651
940
    def test_post_commit_default(self):
1652
 
        my_config = self.get_branch_config(global_config=sample_config_text,
1653
 
                                      location='/a/c',
1654
 
                                      location_config=sample_branches_text)
 
941
        branch = FakeBranch()
 
942
        my_config = self.get_branch_config(sample_config_text, '/a/c',
 
943
                                           sample_branches_text)
1655
944
        self.assertEqual(my_config.branch.base, '/a/c')
1656
945
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1657
946
                         my_config.post_commit())
1658
947
        my_config.set_user_option('post_commit', 'rmtree_root')
1659
 
        # post-commit is ignored when present in branch data
 
948
        # post-commit is ignored when bresent in branch data
1660
949
        self.assertEqual('bzrlib.tests.test_config.post_commit',
1661
950
                         my_config.post_commit())
1662
951
        my_config.set_user_option('post_commit', 'rmtree_root',
1664
953
        self.assertEqual('rmtree_root', my_config.post_commit())
1665
954
 
1666
955
    def test_config_precedence(self):
1667
 
        # FIXME: eager test, luckily no persitent config file makes it fail
1668
 
        # -- vila 20100716
1669
956
        my_config = self.get_branch_config(global_config=precedence_global)
1670
957
        self.assertEqual(my_config.get_user_option('option'), 'global')
1671
 
        my_config = self.get_branch_config(global_config=precedence_global,
1672
 
                                           branch_data_config=precedence_branch)
 
958
        my_config = self.get_branch_config(global_config=precedence_global, 
 
959
                                      branch_data_config=precedence_branch)
1673
960
        self.assertEqual(my_config.get_user_option('option'), 'branch')
1674
 
        my_config = self.get_branch_config(
1675
 
            global_config=precedence_global,
1676
 
            branch_data_config=precedence_branch,
1677
 
            location_config=precedence_location)
 
961
        my_config = self.get_branch_config(global_config=precedence_global, 
 
962
                                      branch_data_config=precedence_branch,
 
963
                                      location_config=precedence_location)
1678
964
        self.assertEqual(my_config.get_user_option('option'), 'recurse')
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')
 
965
        my_config = self.get_branch_config(global_config=precedence_global, 
 
966
                                      branch_data_config=precedence_branch,
 
967
                                      location_config=precedence_location,
 
968
                                      location='http://example.com/specific')
1684
969
        self.assertEqual(my_config.get_user_option('option'), 'exact')
1685
970
 
1686
 
    def test_get_mail_client(self):
1687
 
        config = self.get_branch_config()
1688
 
        client = config.get_mail_client()
1689
 
        self.assertIsInstance(client, mail_client.DefaultMail)
1690
 
 
1691
 
        # Specific clients
1692
 
        config.set_user_option('mail_client', 'evolution')
1693
 
        client = config.get_mail_client()
1694
 
        self.assertIsInstance(client, mail_client.Evolution)
1695
 
 
1696
 
        config.set_user_option('mail_client', 'kmail')
1697
 
        client = config.get_mail_client()
1698
 
        self.assertIsInstance(client, mail_client.KMail)
1699
 
 
1700
 
        config.set_user_option('mail_client', 'mutt')
1701
 
        client = config.get_mail_client()
1702
 
        self.assertIsInstance(client, mail_client.Mutt)
1703
 
 
1704
 
        config.set_user_option('mail_client', 'thunderbird')
1705
 
        client = config.get_mail_client()
1706
 
        self.assertIsInstance(client, mail_client.Thunderbird)
1707
 
 
1708
 
        # Generic options
1709
 
        config.set_user_option('mail_client', 'default')
1710
 
        client = config.get_mail_client()
1711
 
        self.assertIsInstance(client, mail_client.DefaultMail)
1712
 
 
1713
 
        config.set_user_option('mail_client', 'editor')
1714
 
        client = config.get_mail_client()
1715
 
        self.assertIsInstance(client, mail_client.Editor)
1716
 
 
1717
 
        config.set_user_option('mail_client', 'mapi')
1718
 
        client = config.get_mail_client()
1719
 
        self.assertIsInstance(client, mail_client.MAPIClient)
1720
 
 
1721
 
        config.set_user_option('mail_client', 'xdg-email')
1722
 
        client = config.get_mail_client()
1723
 
        self.assertIsInstance(client, mail_client.XDGEmail)
1724
 
 
1725
 
        config.set_user_option('mail_client', 'firebird')
1726
 
        self.assertRaises(errors.UnknownMailClient, config.get_mail_client)
1727
 
 
1728
 
 
1729
 
class TestMailAddressExtraction(tests.TestCase):
 
971
 
 
972
class TestMailAddressExtraction(TestCase):
1730
973
 
1731
974
    def test_extract_email_address(self):
1732
975
        self.assertEqual('jane@test.com',
1733
976
                         config.extract_email_address('Jane <jane@test.com>'))
1734
977
        self.assertRaises(errors.NoEmailInUsername,
1735
978
                          config.extract_email_address, 'Jane Tester')
1736
 
 
1737
 
    def test_parse_username(self):
1738
 
        self.assertEqual(('', 'jdoe@example.com'),
1739
 
                         config.parse_username('jdoe@example.com'))
1740
 
        self.assertEqual(('', 'jdoe@example.com'),
1741
 
                         config.parse_username('<jdoe@example.com>'))
1742
 
        self.assertEqual(('John Doe', 'jdoe@example.com'),
1743
 
                         config.parse_username('John Doe <jdoe@example.com>'))
1744
 
        self.assertEqual(('John Doe', ''),
1745
 
                         config.parse_username('John Doe'))
1746
 
        self.assertEqual(('John Doe', 'jdoe@example.com'),
1747
 
                         config.parse_username('John Doe jdoe@example.com'))
1748
 
 
1749
 
class TestTreeConfig(tests.TestCaseWithTransport):
1750
 
 
1751
 
    def test_get_value(self):
1752
 
        """Test that retreiving a value from a section is possible"""
1753
 
        branch = self.make_branch('.')
1754
 
        tree_config = config.TreeConfig(branch)
1755
 
        tree_config.set_option('value', 'key', 'SECTION')
1756
 
        tree_config.set_option('value2', 'key2')
1757
 
        tree_config.set_option('value3-top', 'key3')
1758
 
        tree_config.set_option('value3-section', 'key3', 'SECTION')
1759
 
        value = tree_config.get_option('key', 'SECTION')
1760
 
        self.assertEqual(value, 'value')
1761
 
        value = tree_config.get_option('key2')
1762
 
        self.assertEqual(value, 'value2')
1763
 
        self.assertEqual(tree_config.get_option('non-existant'), None)
1764
 
        value = tree_config.get_option('non-existant', 'SECTION')
1765
 
        self.assertEqual(value, None)
1766
 
        value = tree_config.get_option('non-existant', default='default')
1767
 
        self.assertEqual(value, 'default')
1768
 
        self.assertEqual(tree_config.get_option('key2', 'NOSECTION'), None)
1769
 
        value = tree_config.get_option('key2', 'NOSECTION', default='default')
1770
 
        self.assertEqual(value, 'default')
1771
 
        value = tree_config.get_option('key3')
1772
 
        self.assertEqual(value, 'value3-top')
1773
 
        value = tree_config.get_option('key3', 'SECTION')
1774
 
        self.assertEqual(value, 'value3-section')
1775
 
 
1776
 
 
1777
 
class TestTransportConfig(tests.TestCaseWithTransport):
1778
 
 
1779
 
    def test_get_value(self):
1780
 
        """Test that retreiving a value from a section is possible"""
1781
 
        bzrdir_config = config.TransportConfig(transport.get_transport('.'),
1782
 
                                               'control.conf')
1783
 
        bzrdir_config.set_option('value', 'key', 'SECTION')
1784
 
        bzrdir_config.set_option('value2', 'key2')
1785
 
        bzrdir_config.set_option('value3-top', 'key3')
1786
 
        bzrdir_config.set_option('value3-section', 'key3', 'SECTION')
1787
 
        value = bzrdir_config.get_option('key', 'SECTION')
1788
 
        self.assertEqual(value, 'value')
1789
 
        value = bzrdir_config.get_option('key2')
1790
 
        self.assertEqual(value, 'value2')
1791
 
        self.assertEqual(bzrdir_config.get_option('non-existant'), None)
1792
 
        value = bzrdir_config.get_option('non-existant', 'SECTION')
1793
 
        self.assertEqual(value, None)
1794
 
        value = bzrdir_config.get_option('non-existant', default='default')
1795
 
        self.assertEqual(value, 'default')
1796
 
        self.assertEqual(bzrdir_config.get_option('key2', 'NOSECTION'), None)
1797
 
        value = bzrdir_config.get_option('key2', 'NOSECTION',
1798
 
                                         default='default')
1799
 
        self.assertEqual(value, 'default')
1800
 
        value = bzrdir_config.get_option('key3')
1801
 
        self.assertEqual(value, 'value3-top')
1802
 
        value = bzrdir_config.get_option('key3', 'SECTION')
1803
 
        self.assertEqual(value, 'value3-section')
1804
 
 
1805
 
    def test_set_unset_default_stack_on(self):
1806
 
        my_dir = self.make_bzrdir('.')
1807
 
        bzrdir_config = config.BzrDirConfig(my_dir)
1808
 
        self.assertIs(None, bzrdir_config.get_default_stack_on())
1809
 
        bzrdir_config.set_default_stack_on('Foo')
1810
 
        self.assertEqual('Foo', bzrdir_config._config.get_option(
1811
 
                         'default_stack_on'))
1812
 
        self.assertEqual('Foo', bzrdir_config.get_default_stack_on())
1813
 
        bzrdir_config.set_default_stack_on(None)
1814
 
        self.assertIs(None, bzrdir_config.get_default_stack_on())
1815
 
 
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
 
 
1969
 
class TestAuthenticationConfigFile(tests.TestCase):
1970
 
    """Test the authentication.conf file matching"""
1971
 
 
1972
 
    def _got_user_passwd(self, expected_user, expected_password,
1973
 
                         config, *args, **kwargs):
1974
 
        credentials = config.get_credentials(*args, **kwargs)
1975
 
        if credentials is None:
1976
 
            user = None
1977
 
            password = None
1978
 
        else:
1979
 
            user = credentials['user']
1980
 
            password = credentials['password']
1981
 
        self.assertEquals(expected_user, user)
1982
 
        self.assertEquals(expected_password, password)
1983
 
 
1984
 
    def test_empty_config(self):
1985
 
        conf = config.AuthenticationConfig(_file=StringIO())
1986
 
        self.assertEquals({}, conf._get_config())
1987
 
        self._got_user_passwd(None, None, conf, 'http', 'foo.net')
1988
 
 
1989
 
    def test_missing_auth_section_header(self):
1990
 
        conf = config.AuthenticationConfig(_file=StringIO('foo = bar'))
1991
 
        self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
1992
 
 
1993
 
    def test_auth_section_header_not_closed(self):
1994
 
        conf = config.AuthenticationConfig(_file=StringIO('[DEF'))
1995
 
        self.assertRaises(errors.ParseConfigError, conf._get_config)
1996
 
 
1997
 
    def test_auth_value_not_boolean(self):
1998
 
        conf = config.AuthenticationConfig(_file=StringIO(
1999
 
                """[broken]
2000
 
scheme=ftp
2001
 
user=joe
2002
 
verify_certificates=askme # Error: Not a boolean
2003
 
"""))
2004
 
        self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
2005
 
 
2006
 
    def test_auth_value_not_int(self):
2007
 
        conf = config.AuthenticationConfig(_file=StringIO(
2008
 
                """[broken]
2009
 
scheme=ftp
2010
 
user=joe
2011
 
port=port # Error: Not an int
2012
 
"""))
2013
 
        self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
2014
 
 
2015
 
    def test_unknown_password_encoding(self):
2016
 
        conf = config.AuthenticationConfig(_file=StringIO(
2017
 
                """[broken]
2018
 
scheme=ftp
2019
 
user=joe
2020
 
password_encoding=unknown
2021
 
"""))
2022
 
        self.assertRaises(ValueError, conf.get_password,
2023
 
                          'ftp', 'foo.net', 'joe')
2024
 
 
2025
 
    def test_credentials_for_scheme_host(self):
2026
 
        conf = config.AuthenticationConfig(_file=StringIO(
2027
 
                """# Identity on foo.net
2028
 
[ftp definition]
2029
 
scheme=ftp
2030
 
host=foo.net
2031
 
user=joe
2032
 
password=secret-pass
2033
 
"""))
2034
 
        # Basic matching
2035
 
        self._got_user_passwd('joe', 'secret-pass', conf, 'ftp', 'foo.net')
2036
 
        # different scheme
2037
 
        self._got_user_passwd(None, None, conf, 'http', 'foo.net')
2038
 
        # different host
2039
 
        self._got_user_passwd(None, None, conf, 'ftp', 'bar.net')
2040
 
 
2041
 
    def test_credentials_for_host_port(self):
2042
 
        conf = config.AuthenticationConfig(_file=StringIO(
2043
 
                """# Identity on foo.net
2044
 
[ftp definition]
2045
 
scheme=ftp
2046
 
port=10021
2047
 
host=foo.net
2048
 
user=joe
2049
 
password=secret-pass
2050
 
"""))
2051
 
        # No port
2052
 
        self._got_user_passwd('joe', 'secret-pass',
2053
 
                              conf, 'ftp', 'foo.net', port=10021)
2054
 
        # different port
2055
 
        self._got_user_passwd(None, None, conf, 'ftp', 'foo.net')
2056
 
 
2057
 
    def test_for_matching_host(self):
2058
 
        conf = config.AuthenticationConfig(_file=StringIO(
2059
 
                """# Identity on foo.net
2060
 
[sourceforge]
2061
 
scheme=bzr
2062
 
host=bzr.sf.net
2063
 
user=joe
2064
 
password=joepass
2065
 
[sourceforge domain]
2066
 
scheme=bzr
2067
 
host=.bzr.sf.net
2068
 
user=georges
2069
 
password=bendover
2070
 
"""))
2071
 
        # matching domain
2072
 
        self._got_user_passwd('georges', 'bendover',
2073
 
                              conf, 'bzr', 'foo.bzr.sf.net')
2074
 
        # phishing attempt
2075
 
        self._got_user_passwd(None, None,
2076
 
                              conf, 'bzr', 'bbzr.sf.net')
2077
 
 
2078
 
    def test_for_matching_host_None(self):
2079
 
        conf = config.AuthenticationConfig(_file=StringIO(
2080
 
                """# Identity on foo.net
2081
 
[catchup bzr]
2082
 
scheme=bzr
2083
 
user=joe
2084
 
password=joepass
2085
 
[DEFAULT]
2086
 
user=georges
2087
 
password=bendover
2088
 
"""))
2089
 
        # match no host
2090
 
        self._got_user_passwd('joe', 'joepass',
2091
 
                              conf, 'bzr', 'quux.net')
2092
 
        # no host but different scheme
2093
 
        self._got_user_passwd('georges', 'bendover',
2094
 
                              conf, 'ftp', 'quux.net')
2095
 
 
2096
 
    def test_credentials_for_path(self):
2097
 
        conf = config.AuthenticationConfig(_file=StringIO(
2098
 
                """
2099
 
[http dir1]
2100
 
scheme=http
2101
 
host=bar.org
2102
 
path=/dir1
2103
 
user=jim
2104
 
password=jimpass
2105
 
[http dir2]
2106
 
scheme=http
2107
 
host=bar.org
2108
 
path=/dir2
2109
 
user=georges
2110
 
password=bendover
2111
 
"""))
2112
 
        # no path no dice
2113
 
        self._got_user_passwd(None, None,
2114
 
                              conf, 'http', host='bar.org', path='/dir3')
2115
 
        # matching path
2116
 
        self._got_user_passwd('georges', 'bendover',
2117
 
                              conf, 'http', host='bar.org', path='/dir2')
2118
 
        # matching subdir
2119
 
        self._got_user_passwd('jim', 'jimpass',
2120
 
                              conf, 'http', host='bar.org',path='/dir1/subdir')
2121
 
 
2122
 
    def test_credentials_for_user(self):
2123
 
        conf = config.AuthenticationConfig(_file=StringIO(
2124
 
                """
2125
 
[with user]
2126
 
scheme=http
2127
 
host=bar.org
2128
 
user=jim
2129
 
password=jimpass
2130
 
"""))
2131
 
        # Get user
2132
 
        self._got_user_passwd('jim', 'jimpass',
2133
 
                              conf, 'http', 'bar.org')
2134
 
        # Get same user
2135
 
        self._got_user_passwd('jim', 'jimpass',
2136
 
                              conf, 'http', 'bar.org', user='jim')
2137
 
        # Don't get a different user if one is specified
2138
 
        self._got_user_passwd(None, None,
2139
 
                              conf, 'http', 'bar.org', user='georges')
2140
 
 
2141
 
    def test_credentials_for_user_without_password(self):
2142
 
        conf = config.AuthenticationConfig(_file=StringIO(
2143
 
                """
2144
 
[without password]
2145
 
scheme=http
2146
 
host=bar.org
2147
 
user=jim
2148
 
"""))
2149
 
        # Get user but no password
2150
 
        self._got_user_passwd('jim', None,
2151
 
                              conf, 'http', 'bar.org')
2152
 
 
2153
 
    def test_verify_certificates(self):
2154
 
        conf = config.AuthenticationConfig(_file=StringIO(
2155
 
                """
2156
 
[self-signed]
2157
 
scheme=https
2158
 
host=bar.org
2159
 
user=jim
2160
 
password=jimpass
2161
 
verify_certificates=False
2162
 
[normal]
2163
 
scheme=https
2164
 
host=foo.net
2165
 
user=georges
2166
 
password=bendover
2167
 
"""))
2168
 
        credentials = conf.get_credentials('https', 'bar.org')
2169
 
        self.assertEquals(False, credentials.get('verify_certificates'))
2170
 
        credentials = conf.get_credentials('https', 'foo.net')
2171
 
        self.assertEquals(True, credentials.get('verify_certificates'))
2172
 
 
2173
 
 
2174
 
class TestAuthenticationStorage(tests.TestCaseInTempDir):
2175
 
 
2176
 
    def test_set_credentials(self):
2177
 
        conf = config.AuthenticationConfig()
2178
 
        conf.set_credentials('name', 'host', 'user', 'scheme', 'password',
2179
 
        99, path='/foo', verify_certificates=False, realm='realm')
2180
 
        credentials = conf.get_credentials(host='host', scheme='scheme',
2181
 
                                           port=99, path='/foo',
2182
 
                                           realm='realm')
2183
 
        CREDENTIALS = {'name': 'name', 'user': 'user', 'password': 'password',
2184
 
                       'verify_certificates': False, 'scheme': 'scheme', 
2185
 
                       'host': 'host', 'port': 99, 'path': '/foo', 
2186
 
                       'realm': 'realm'}
2187
 
        self.assertEqual(CREDENTIALS, credentials)
2188
 
        credentials_from_disk = config.AuthenticationConfig().get_credentials(
2189
 
            host='host', scheme='scheme', port=99, path='/foo', realm='realm')
2190
 
        self.assertEqual(CREDENTIALS, credentials_from_disk)
2191
 
 
2192
 
    def test_reset_credentials_different_name(self):
2193
 
        conf = config.AuthenticationConfig()
2194
 
        conf.set_credentials('name', 'host', 'user', 'scheme', 'password'),
2195
 
        conf.set_credentials('name2', 'host', 'user2', 'scheme', 'password'),
2196
 
        self.assertIs(None, conf._get_config().get('name'))
2197
 
        credentials = conf.get_credentials(host='host', scheme='scheme')
2198
 
        CREDENTIALS = {'name': 'name2', 'user': 'user2', 'password':
2199
 
                       'password', 'verify_certificates': True, 
2200
 
                       'scheme': 'scheme', 'host': 'host', 'port': None, 
2201
 
                       'path': None, 'realm': None}
2202
 
        self.assertEqual(CREDENTIALS, credentials)
2203
 
 
2204
 
 
2205
 
class TestAuthenticationConfig(tests.TestCase):
2206
 
    """Test AuthenticationConfig behaviour"""
2207
 
 
2208
 
    def _check_default_password_prompt(self, expected_prompt_format, scheme,
2209
 
                                       host=None, port=None, realm=None,
2210
 
                                       path=None):
2211
 
        if host is None:
2212
 
            host = 'bar.org'
2213
 
        user, password = 'jim', 'precious'
2214
 
        expected_prompt = expected_prompt_format % {
2215
 
            'scheme': scheme, 'host': host, 'port': port,
2216
 
            'user': user, 'realm': realm}
2217
 
 
2218
 
        stdout = tests.StringIOWrapper()
2219
 
        stderr = tests.StringIOWrapper()
2220
 
        ui.ui_factory = tests.TestUIFactory(stdin=password + '\n',
2221
 
                                            stdout=stdout, stderr=stderr)
2222
 
        # We use an empty conf so that the user is always prompted
2223
 
        conf = config.AuthenticationConfig()
2224
 
        self.assertEquals(password,
2225
 
                          conf.get_password(scheme, host, user, port=port,
2226
 
                                            realm=realm, path=path))
2227
 
        self.assertEquals(expected_prompt, stderr.getvalue())
2228
 
        self.assertEquals('', stdout.getvalue())
2229
 
 
2230
 
    def _check_default_username_prompt(self, expected_prompt_format, scheme,
2231
 
                                       host=None, port=None, realm=None,
2232
 
                                       path=None):
2233
 
        if host is None:
2234
 
            host = 'bar.org'
2235
 
        username = 'jim'
2236
 
        expected_prompt = expected_prompt_format % {
2237
 
            'scheme': scheme, 'host': host, 'port': port,
2238
 
            'realm': realm}
2239
 
        stdout = tests.StringIOWrapper()
2240
 
        stderr = tests.StringIOWrapper()
2241
 
        ui.ui_factory = tests.TestUIFactory(stdin=username+ '\n',
2242
 
                                            stdout=stdout, stderr=stderr)
2243
 
        # We use an empty conf so that the user is always prompted
2244
 
        conf = config.AuthenticationConfig()
2245
 
        self.assertEquals(username, conf.get_user(scheme, host, port=port,
2246
 
                          realm=realm, path=path, ask=True))
2247
 
        self.assertEquals(expected_prompt, stderr.getvalue())
2248
 
        self.assertEquals('', stdout.getvalue())
2249
 
 
2250
 
    def test_username_defaults_prompts(self):
2251
 
        # HTTP prompts can't be tested here, see test_http.py
2252
 
        self._check_default_username_prompt('FTP %(host)s username: ', 'ftp')
2253
 
        self._check_default_username_prompt(
2254
 
            'FTP %(host)s:%(port)d username: ', 'ftp', port=10020)
2255
 
        self._check_default_username_prompt(
2256
 
            'SSH %(host)s:%(port)d username: ', 'ssh', port=12345)
2257
 
 
2258
 
    def test_username_default_no_prompt(self):
2259
 
        conf = config.AuthenticationConfig()
2260
 
        self.assertEquals(None,
2261
 
            conf.get_user('ftp', 'example.com'))
2262
 
        self.assertEquals("explicitdefault",
2263
 
            conf.get_user('ftp', 'example.com', default="explicitdefault"))
2264
 
 
2265
 
    def test_password_default_prompts(self):
2266
 
        # HTTP prompts can't be tested here, see test_http.py
2267
 
        self._check_default_password_prompt(
2268
 
            'FTP %(user)s@%(host)s password: ', 'ftp')
2269
 
        self._check_default_password_prompt(
2270
 
            'FTP %(user)s@%(host)s:%(port)d password: ', 'ftp', port=10020)
2271
 
        self._check_default_password_prompt(
2272
 
            'SSH %(user)s@%(host)s:%(port)d password: ', 'ssh', port=12345)
2273
 
        # SMTP port handling is a bit special (it's handled if embedded in the
2274
 
        # host too)
2275
 
        # FIXME: should we: forbid that, extend it to other schemes, leave
2276
 
        # things as they are that's fine thank you ?
2277
 
        self._check_default_password_prompt('SMTP %(user)s@%(host)s password: ',
2278
 
                                            'smtp')
2279
 
        self._check_default_password_prompt('SMTP %(user)s@%(host)s password: ',
2280
 
                                            'smtp', host='bar.org:10025')
2281
 
        self._check_default_password_prompt(
2282
 
            'SMTP %(user)s@%(host)s:%(port)d password: ',
2283
 
            'smtp', port=10025)
2284
 
 
2285
 
    def test_ssh_password_emits_warning(self):
2286
 
        conf = config.AuthenticationConfig(_file=StringIO(
2287
 
                """
2288
 
[ssh with password]
2289
 
scheme=ssh
2290
 
host=bar.org
2291
 
user=jim
2292
 
password=jimpass
2293
 
"""))
2294
 
        entered_password = 'typed-by-hand'
2295
 
        stdout = tests.StringIOWrapper()
2296
 
        stderr = tests.StringIOWrapper()
2297
 
        ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
2298
 
                                            stdout=stdout, stderr=stderr)
2299
 
 
2300
 
        # Since the password defined in the authentication config is ignored,
2301
 
        # the user is prompted
2302
 
        self.assertEquals(entered_password,
2303
 
                          conf.get_password('ssh', 'bar.org', user='jim'))
2304
 
        self.assertContainsRe(
2305
 
            self.get_log(),
2306
 
            'password ignored in section \[ssh with password\]')
2307
 
 
2308
 
    def test_ssh_without_password_doesnt_emit_warning(self):
2309
 
        conf = config.AuthenticationConfig(_file=StringIO(
2310
 
                """
2311
 
[ssh with password]
2312
 
scheme=ssh
2313
 
host=bar.org
2314
 
user=jim
2315
 
"""))
2316
 
        entered_password = 'typed-by-hand'
2317
 
        stdout = tests.StringIOWrapper()
2318
 
        stderr = tests.StringIOWrapper()
2319
 
        ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
2320
 
                                            stdout=stdout,
2321
 
                                            stderr=stderr)
2322
 
 
2323
 
        # Since the password defined in the authentication config is ignored,
2324
 
        # the user is prompted
2325
 
        self.assertEquals(entered_password,
2326
 
                          conf.get_password('ssh', 'bar.org', user='jim'))
2327
 
        # No warning shoud be emitted since there is no password. We are only
2328
 
        # providing "user".
2329
 
        self.assertNotContainsRe(
2330
 
            self.get_log(),
2331
 
            'password ignored in section \[ssh with password\]')
2332
 
 
2333
 
    def test_uses_fallback_stores(self):
2334
 
        self.overrideAttr(config, 'credential_store_registry',
2335
 
                          config.CredentialStoreRegistry())
2336
 
        store = StubCredentialStore()
2337
 
        store.add_credentials("http", "example.com", "joe", "secret")
2338
 
        config.credential_store_registry.register("stub", store, fallback=True)
2339
 
        conf = config.AuthenticationConfig(_file=StringIO())
2340
 
        creds = conf.get_credentials("http", "example.com")
2341
 
        self.assertEquals("joe", creds["user"])
2342
 
        self.assertEquals("secret", creds["password"])
2343
 
 
2344
 
 
2345
 
class StubCredentialStore(config.CredentialStore):
2346
 
 
2347
 
    def __init__(self):
2348
 
        self._username = {}
2349
 
        self._password = {}
2350
 
 
2351
 
    def add_credentials(self, scheme, host, user, password=None):
2352
 
        self._username[(scheme, host)] = user
2353
 
        self._password[(scheme, host)] = password
2354
 
 
2355
 
    def get_credentials(self, scheme, host, port=None, user=None,
2356
 
        path=None, realm=None):
2357
 
        key = (scheme, host)
2358
 
        if not key in self._username:
2359
 
            return None
2360
 
        return { "scheme": scheme, "host": host, "port": port,
2361
 
                "user": self._username[key], "password": self._password[key]}
2362
 
 
2363
 
 
2364
 
class CountingCredentialStore(config.CredentialStore):
2365
 
 
2366
 
    def __init__(self):
2367
 
        self._calls = 0
2368
 
 
2369
 
    def get_credentials(self, scheme, host, port=None, user=None,
2370
 
        path=None, realm=None):
2371
 
        self._calls += 1
2372
 
        return None
2373
 
 
2374
 
 
2375
 
class TestCredentialStoreRegistry(tests.TestCase):
2376
 
 
2377
 
    def _get_cs_registry(self):
2378
 
        return config.credential_store_registry
2379
 
 
2380
 
    def test_default_credential_store(self):
2381
 
        r = self._get_cs_registry()
2382
 
        default = r.get_credential_store(None)
2383
 
        self.assertIsInstance(default, config.PlainTextCredentialStore)
2384
 
 
2385
 
    def test_unknown_credential_store(self):
2386
 
        r = self._get_cs_registry()
2387
 
        # It's hard to imagine someone creating a credential store named
2388
 
        # 'unknown' so we use that as an never registered key.
2389
 
        self.assertRaises(KeyError, r.get_credential_store, 'unknown')
2390
 
 
2391
 
    def test_fallback_none_registered(self):
2392
 
        r = config.CredentialStoreRegistry()
2393
 
        self.assertEquals(None,
2394
 
                          r.get_fallback_credentials("http", "example.com"))
2395
 
 
2396
 
    def test_register(self):
2397
 
        r = config.CredentialStoreRegistry()
2398
 
        r.register("stub", StubCredentialStore(), fallback=False)
2399
 
        r.register("another", StubCredentialStore(), fallback=True)
2400
 
        self.assertEquals(["another", "stub"], r.keys())
2401
 
 
2402
 
    def test_register_lazy(self):
2403
 
        r = config.CredentialStoreRegistry()
2404
 
        r.register_lazy("stub", "bzrlib.tests.test_config",
2405
 
                        "StubCredentialStore", fallback=False)
2406
 
        self.assertEquals(["stub"], r.keys())
2407
 
        self.assertIsInstance(r.get_credential_store("stub"),
2408
 
                              StubCredentialStore)
2409
 
 
2410
 
    def test_is_fallback(self):
2411
 
        r = config.CredentialStoreRegistry()
2412
 
        r.register("stub1", None, fallback=False)
2413
 
        r.register("stub2", None, fallback=True)
2414
 
        self.assertEquals(False, r.is_fallback("stub1"))
2415
 
        self.assertEquals(True, r.is_fallback("stub2"))
2416
 
 
2417
 
    def test_no_fallback(self):
2418
 
        r = config.CredentialStoreRegistry()
2419
 
        store = CountingCredentialStore()
2420
 
        r.register("count", store, fallback=False)
2421
 
        self.assertEquals(None,
2422
 
                          r.get_fallback_credentials("http", "example.com"))
2423
 
        self.assertEquals(0, store._calls)
2424
 
 
2425
 
    def test_fallback_credentials(self):
2426
 
        r = config.CredentialStoreRegistry()
2427
 
        store = StubCredentialStore()
2428
 
        store.add_credentials("http", "example.com",
2429
 
                              "somebody", "geheim")
2430
 
        r.register("stub", store, fallback=True)
2431
 
        creds = r.get_fallback_credentials("http", "example.com")
2432
 
        self.assertEquals("somebody", creds["user"])
2433
 
        self.assertEquals("geheim", creds["password"])
2434
 
 
2435
 
    def test_fallback_first_wins(self):
2436
 
        r = config.CredentialStoreRegistry()
2437
 
        stub1 = StubCredentialStore()
2438
 
        stub1.add_credentials("http", "example.com",
2439
 
                              "somebody", "stub1")
2440
 
        r.register("stub1", stub1, fallback=True)
2441
 
        stub2 = StubCredentialStore()
2442
 
        stub2.add_credentials("http", "example.com",
2443
 
                              "somebody", "stub2")
2444
 
        r.register("stub2", stub1, fallback=True)
2445
 
        creds = r.get_fallback_credentials("http", "example.com")
2446
 
        self.assertEquals("somebody", creds["user"])
2447
 
        self.assertEquals("stub1", creds["password"])
2448
 
 
2449
 
 
2450
 
class TestPlainTextCredentialStore(tests.TestCase):
2451
 
 
2452
 
    def test_decode_password(self):
2453
 
        r = config.credential_store_registry
2454
 
        plain_text = r.get_credential_store()
2455
 
        decoded = plain_text.decode_password(dict(password='secret'))
2456
 
        self.assertEquals('secret', decoded)
2457
 
 
2458
 
 
2459
 
# FIXME: Once we have a way to declare authentication to all test servers, we
2460
 
# can implement generic tests.
2461
 
# test_user_password_in_url
2462
 
# test_user_in_url_password_from_config
2463
 
# test_user_in_url_password_prompted
2464
 
# test_user_in_config
2465
 
# test_user_getpass.getuser
2466
 
# test_user_prompted ?
2467
 
class TestAuthenticationRing(tests.TestCaseWithTransport):
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.assertIsNot(None, realname)
2487
 
            self.assertIsNot(None, address)
2488
 
        else:
2489
 
            self.assertEquals((None, None), (realname, address))
2490