~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_config.py

  • Committer: Martin Pool
  • Date: 2007-04-04 06:17:31 UTC
  • mto: This revision was merged to the branch mainline in revision 2397.
  • Revision ID: mbp@sourcefrog.net-20070404061731-tt2xrzllqhbodn83
Contents of TODO file moved into bug tracker

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2008 Canonical Ltd
 
1
# Copyright (C) 2005, 2006 Canonical Ltd
2
2
#   Authors: Robert Collins <robert.collins@canonical.com>
3
3
#
4
4
# This program is free software; you can redistribute it and/or modify
13
13
#
14
14
# You should have received a copy of the GNU General Public License
15
15
# along with this program; if not, write to the Free Software
16
 
# 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
17
17
 
18
18
"""Tests for finding and reading the bzr config file[s]."""
19
19
# import system imports here
 
20
from bzrlib.util.configobj.configobj import ConfigObj, ConfigObjError
20
21
from cStringIO import StringIO
21
 
import getpass
22
22
import os
23
23
import sys
24
24
 
25
25
#import bzrlib specific imports here
26
26
from bzrlib import (
27
 
    branch,
28
 
    bzrdir,
29
27
    config,
30
28
    errors,
31
29
    osutils,
32
 
    mail_client,
33
 
    ui,
34
30
    urlutils,
35
 
    tests,
36
 
    trace,
37
 
    transport,
38
31
    )
39
 
from bzrlib.util.configobj import configobj
 
32
from bzrlib.branch import Branch
 
33
from bzrlib.bzrdir import BzrDir
 
34
from bzrlib.tests import TestCase, TestCaseInTempDir, TestCaseWithTransport
40
35
 
41
36
 
42
37
sample_long_alias="log -r-15..-1 --line"
148
143
            self.base = "http://example.com/branches/demo"
149
144
        else:
150
145
            self.base = base
151
 
        self._transport = self.control_files = \
152
 
            FakeControlFilesAndTransport(user_id=user_id)
153
 
 
154
 
    def _get_config(self):
155
 
        return config.TransportConfig(self._transport, 'branch.conf')
 
146
        self.control_files = FakeControlFiles(user_id=user_id)
156
147
 
157
148
    def lock_write(self):
158
149
        pass
161
152
        pass
162
153
 
163
154
 
164
 
class FakeControlFilesAndTransport(object):
 
155
class FakeControlFiles(object):
165
156
 
166
157
    def __init__(self, user_id=None):
 
158
        self.email = user_id
167
159
        self.files = {}
168
 
        if user_id:
169
 
            self.files['email'] = user_id
170
 
        self._transport = self
171
160
 
172
161
    def get_utf8(self, filename):
173
 
        # from LockableFiles
174
 
        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)
175
167
 
176
168
    def get(self, filename):
177
 
        # from Transport
178
169
        try:
179
170
            return StringIO(self.files[filename])
180
171
        except KeyError:
181
172
            raise errors.NoSuchFile(filename)
182
173
 
183
 
    def get_bytes(self, filename):
184
 
        # from Transport
185
 
        try:
186
 
            return self.files[filename]
187
 
        except KeyError:
188
 
            raise errors.NoSuchFile(filename)
189
 
 
190
174
    def put(self, filename, fileobj):
191
175
        self.files[filename] = fileobj.read()
192
176
 
193
 
    def put_file(self, filename, fileobj):
194
 
        return self.put(filename, fileobj)
195
 
 
196
177
 
197
178
class InstrumentedConfig(config.Config):
198
179
    """An instrumented config that supplies stubs for template methods."""
199
 
 
 
180
    
200
181
    def __init__(self):
201
182
        super(InstrumentedConfig, self).__init__()
202
183
        self._calls = []
218
199
active = True
219
200
nonactive = False
220
201
"""
221
 
 
222
 
 
223
 
class TestConfigObj(tests.TestCase):
224
 
 
 
202
class TestConfigObj(TestCase):
225
203
    def test_get_bool(self):
226
 
        co = config.ConfigObj(StringIO(bool_config))
 
204
        from bzrlib.config import ConfigObj
 
205
        co = ConfigObj(StringIO(bool_config))
227
206
        self.assertIs(co.get_bool('DEFAULT', 'active'), True)
228
207
        self.assertIs(co.get_bool('DEFAULT', 'inactive'), False)
229
208
        self.assertIs(co.get_bool('UPPERCASE', 'active'), True)
230
209
        self.assertIs(co.get_bool('UPPERCASE', 'nonactive'), False)
231
210
 
232
 
    def test_hash_sign_in_value(self):
233
 
        """
234
 
        Before 4.5.0, ConfigObj did not quote # signs in values, so they'd be
235
 
        treated as comments when read in again. (#86838)
236
 
        """
237
 
        co = config.ConfigObj()
238
 
        co['test'] = 'foo#bar'
239
 
        lines = co.write()
240
 
        self.assertEqual(lines, ['test = "foo#bar"'])
241
 
        co2 = config.ConfigObj(lines)
242
 
        self.assertEqual(co2['test'], 'foo#bar')
243
 
 
244
 
 
245
 
erroneous_config = """[section] # line 1
246
 
good=good # line 2
247
 
[section] # line 3
248
 
whocares=notme # line 4
249
 
"""
250
 
 
251
 
 
252
 
class TestConfigObjErrors(tests.TestCase):
253
 
 
254
 
    def test_duplicate_section_name_error_line(self):
255
 
        try:
256
 
            co = configobj.ConfigObj(StringIO(erroneous_config),
257
 
                                     raise_errors=True)
258
 
        except config.configobj.DuplicateError, e:
259
 
            self.assertEqual(3, e.line_number)
260
 
        else:
261
 
            self.fail('Error in config file not detected')
262
 
 
263
 
 
264
 
class TestConfig(tests.TestCase):
 
211
 
 
212
class TestConfig(TestCase):
265
213
 
266
214
    def test_constructs(self):
267
215
        config.Config()
268
 
 
 
216
 
269
217
    def test_no_default_editor(self):
270
218
        self.assertRaises(NotImplementedError, config.Config().get_editor)
271
219
 
317
265
        self.assertEqual('long', my_config.log_format())
318
266
 
319
267
 
320
 
class TestConfigPath(tests.TestCase):
 
268
class TestConfigPath(TestCase):
321
269
 
322
270
    def setUp(self):
323
271
        super(TestConfigPath, self).setUp()
325
273
        if sys.platform == 'win32':
326
274
            os.environ['BZR_HOME'] = \
327
275
                r'C:\Documents and Settings\bogus\Application Data'
328
 
            self.bzr_home = \
329
 
                'C:/Documents and Settings/bogus/Application Data/bazaar/2.0'
330
 
        else:
331
 
            self.bzr_home = '/home/bogus/.bazaar'
332
276
 
333
277
    def test_config_dir(self):
334
 
        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')
335
283
 
336
284
    def test_config_filename(self):
337
 
        self.assertEqual(config.config_filename(),
338
 
                         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')
339
291
 
340
292
    def test_branches_config_filename(self):
341
 
        self.assertEqual(config.branches_config_filename(),
342
 
                         self.bzr_home + '/branches.conf')
 
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')
343
299
 
344
300
    def test_locations_config_filename(self):
345
 
        self.assertEqual(config.locations_config_filename(),
346
 
                         self.bzr_home + '/locations.conf')
347
 
 
348
 
    def test_authentication_config_filename(self):
349
 
        self.assertEqual(config.authentication_config_filename(),
350
 
                         self.bzr_home + '/authentication.conf')
351
 
 
352
 
 
353
 
class TestIniConfig(tests.TestCase):
 
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):
354
309
 
355
310
    def test_contructs(self):
356
311
        my_config = config.IniBasedConfig("nothing")
360
315
        my_config = config.IniBasedConfig(None)
361
316
        self.failUnless(
362
317
            isinstance(my_config._get_parser(file=config_file),
363
 
                        configobj.ConfigObj))
 
318
                        ConfigObj))
364
319
 
365
320
    def test_cached(self):
366
321
        config_file = StringIO(sample_config_text.encode('utf-8'))
369
324
        self.failUnless(my_config._get_parser() is parser)
370
325
 
371
326
 
372
 
class TestGetConfig(tests.TestCase):
 
327
class TestGetConfig(TestCase):
373
328
 
374
329
    def test_constructs(self):
375
330
        my_config = config.GlobalConfig()
376
331
 
377
332
    def test_calls_read_filenames(self):
378
 
        # replace the class that is constructed, to check its parameters
 
333
        # replace the class that is constructured, to check its parameters
379
334
        oldparserclass = config.ConfigObj
380
335
        config.ConfigObj = InstrumentedConfigObj
381
336
        my_config = config.GlobalConfig()
388
343
                                          'utf-8')])
389
344
 
390
345
 
391
 
class TestBranchConfig(tests.TestCaseWithTransport):
 
346
class TestBranchConfig(TestCaseWithTransport):
392
347
 
393
348
    def test_constructs(self):
394
349
        branch = FakeBranch()
404
359
 
405
360
    def test_get_config(self):
406
361
        """The Branch.get_config method works properly"""
407
 
        b = bzrdir.BzrDir.create_standalone_workingtree('.').branch
 
362
        b = BzrDir.create_standalone_workingtree('.').branch
408
363
        my_config = b.get_config()
409
364
        self.assertIs(my_config.get_user_option('wacky'), None)
410
365
        my_config.set_user_option('wacky', 'unlikely')
411
366
        self.assertEqual(my_config.get_user_option('wacky'), 'unlikely')
412
367
 
413
368
        # Ensure we get the same thing if we start again
414
 
        b2 = branch.Branch.open('.')
 
369
        b2 = Branch.open('.')
415
370
        my_config2 = b2.get_config()
416
371
        self.assertEqual(my_config2.get_user_option('wacky'), 'unlikely')
417
372
 
429
384
        locations = config.locations_config_filename()
430
385
        config.ensure_config_dir_exists()
431
386
        local_url = urlutils.local_path_to_url('branch')
432
 
        open(locations, 'wb').write('[%s]\nnickname = foobar'
 
387
        open(locations, 'wb').write('[%s]\nnickname = foobar' 
433
388
                                    % (local_url,))
434
389
        self.assertEqual('foobar', branch.nick)
435
390
 
440
395
 
441
396
        locations = config.locations_config_filename()
442
397
        config.ensure_config_dir_exists()
443
 
        open(locations, 'wb').write('[%s/branch]\nnickname = barry'
 
398
        open(locations, 'wb').write('[%s/branch]\nnickname = barry' 
444
399
                                    % (osutils.getcwd().encode('utf8'),))
445
400
        self.assertEqual('barry', branch.nick)
446
401
 
452
407
        local_path = osutils.getcwd().encode('utf8')
453
408
        # Surprisingly ConfigObj doesn't create a trailing newline
454
409
        self.check_file_contents(locations,
455
 
                                 '[%s/branch]\n'
456
 
                                 'push_location = http://foobar\n'
457
 
                                 'push_location:policy = norecurse\n'
458
 
                                 % (local_path,))
 
410
            '[%s/branch]\npush_location = http://foobar\npush_location:policy = norecurse' % (local_path,))
459
411
 
460
412
    def test_autonick_urlencoded(self):
461
413
        b = self.make_branch('!repo')
462
414
        self.assertEqual('!repo', b.get_config().get_nickname())
463
415
 
464
 
    def test_warn_if_masked(self):
465
 
        _warning = trace.warning
466
 
        warnings = []
467
 
        def warning(*args):
468
 
            warnings.append(args[0] % args[1:])
469
 
 
470
 
        def set_option(store, warn_masked=True):
471
 
            warnings[:] = []
472
 
            conf.set_user_option('example_option', repr(store), store=store,
473
 
                                 warn_masked=warn_masked)
474
 
        def assertWarning(warning):
475
 
            if warning is None:
476
 
                self.assertEqual(0, len(warnings))
477
 
            else:
478
 
                self.assertEqual(1, len(warnings))
479
 
                self.assertEqual(warning, warnings[0])
480
 
        trace.warning = warning
481
 
        try:
482
 
            branch = self.make_branch('.')
483
 
            conf = branch.get_config()
484
 
            set_option(config.STORE_GLOBAL)
485
 
            assertWarning(None)
486
 
            set_option(config.STORE_BRANCH)
487
 
            assertWarning(None)
488
 
            set_option(config.STORE_GLOBAL)
489
 
            assertWarning('Value "4" is masked by "3" from branch.conf')
490
 
            set_option(config.STORE_GLOBAL, warn_masked=False)
491
 
            assertWarning(None)
492
 
            set_option(config.STORE_LOCATION)
493
 
            assertWarning(None)
494
 
            set_option(config.STORE_BRANCH)
495
 
            assertWarning('Value "3" is masked by "0" from locations.conf')
496
 
            set_option(config.STORE_BRANCH, warn_masked=False)
497
 
            assertWarning(None)
498
 
        finally:
499
 
            trace.warning = _warning
500
 
 
501
 
 
502
 
class TestGlobalConfigItems(tests.TestCase):
 
416
 
 
417
class TestGlobalConfigItems(TestCase):
503
418
 
504
419
    def test_user_id(self):
505
420
        config_file = StringIO(sample_config_text.encode('utf-8'))
579
494
        my_config = self._get_sample_config()
580
495
        self.assertEqual("something",
581
496
                         my_config.get_user_option('user_global_option'))
582
 
 
 
497
        
583
498
    def test_post_commit_default(self):
584
499
        my_config = self._get_sample_config()
585
500
        self.assertEqual(None, my_config.post_commit())
592
507
        my_config = self._get_sample_config()
593
508
        self.assertEqual('help', my_config.get_alias('h'))
594
509
 
595
 
    def test_get_aliases(self):
596
 
        my_config = self._get_sample_config()
597
 
        aliases = my_config.get_aliases()
598
 
        self.assertEqual(2, len(aliases))
599
 
        sorted_keys = sorted(aliases)
600
 
        self.assertEqual('help', aliases[sorted_keys[0]])
601
 
        self.assertEqual(sample_long_alias, aliases[sorted_keys[1]])
602
 
 
603
510
    def test_get_no_alias(self):
604
511
        my_config = self._get_sample_config()
605
512
        self.assertEqual(None, my_config.get_alias('foo'))
609
516
        self.assertEqual(sample_long_alias, my_config.get_alias('ll'))
610
517
 
611
518
 
612
 
class TestGlobalConfigSavingOptions(tests.TestCaseInTempDir):
613
 
 
614
 
    def test_empty(self):
615
 
        my_config = config.GlobalConfig()
616
 
        self.assertEqual(0, len(my_config.get_aliases()))
617
 
 
618
 
    def test_set_alias(self):
619
 
        my_config = config.GlobalConfig()
620
 
        alias_value = 'commit --strict'
621
 
        my_config.set_alias('commit', alias_value)
622
 
        new_config = config.GlobalConfig()
623
 
        self.assertEqual(alias_value, new_config.get_alias('commit'))
624
 
 
625
 
    def test_remove_alias(self):
626
 
        my_config = config.GlobalConfig()
627
 
        my_config.set_alias('commit', 'commit --strict')
628
 
        # Now remove the alias again.
629
 
        my_config.unset_alias('commit')
630
 
        new_config = config.GlobalConfig()
631
 
        self.assertIs(None, new_config.get_alias('commit'))
632
 
 
633
 
 
634
 
class TestLocationConfig(tests.TestCaseInTempDir):
 
519
class TestLocationConfig(TestCaseInTempDir):
635
520
 
636
521
    def test_constructs(self):
637
522
        my_config = config.LocationConfig('http://example.com')
641
526
        # This is testing the correct file names are provided.
642
527
        # TODO: consolidate with the test for GlobalConfigs filename checks.
643
528
        #
644
 
        # replace the class that is constructed, to check its parameters
 
529
        # replace the class that is constructured, to check its parameters
645
530
        oldparserclass = config.ConfigObj
646
531
        config.ConfigObj = InstrumentedConfigObj
647
532
        try:
675
560
    def test__get_matching_sections_no_match(self):
676
561
        self.get_branch_config('/')
677
562
        self.assertEqual([], self.my_location_config._get_matching_sections())
678
 
 
 
563
        
679
564
    def test__get_matching_sections_exact(self):
680
565
        self.get_branch_config('http://www.example.com')
681
566
        self.assertEqual([('http://www.example.com', '')],
682
567
                         self.my_location_config._get_matching_sections())
683
 
 
 
568
   
684
569
    def test__get_matching_sections_suffix_does_not(self):
685
570
        self.get_branch_config('http://www.example.com-com')
686
571
        self.assertEqual([], self.my_location_config._get_matching_sections())
698
583
    def test__get_matching_sections_ignoreparent_subdir(self):
699
584
        self.get_branch_config(
700
585
            'http://www.example.com/ignoreparent/childbranch')
701
 
        self.assertEqual([('http://www.example.com/ignoreparent',
702
 
                           'childbranch')],
 
586
        self.assertEqual([('http://www.example.com/ignoreparent', 'childbranch')],
703
587
                         self.my_location_config._get_matching_sections())
704
588
 
705
589
    def test__get_matching_sections_subdir_trailing_slash(self):
785
669
        self.get_branch_config('/a/c')
786
670
        self.assertEqual(config.CHECK_NEVER,
787
671
                         self.my_config.signature_checking())
788
 
 
 
672
        
789
673
    def test_signatures_when_available(self):
790
674
        self.get_branch_config('/a/', global_config=sample_ignore_signatures)
791
675
        self.assertEqual(config.CHECK_IF_POSSIBLE,
792
676
                         self.my_config.signature_checking())
793
 
 
 
677
        
794
678
    def test_signatures_always(self):
795
679
        self.get_branch_config('/b')
796
680
        self.assertEqual(config.CHECK_ALWAYS,
797
681
                         self.my_config.signature_checking())
798
 
 
 
682
        
799
683
    def test_gpg_signing_command(self):
800
684
        self.get_branch_config('/b')
801
685
        self.assertEqual("gnome-gpg", self.my_config.gpg_signing_command())
892
776
            self.my_location_config._get_option_policy(
893
777
            'http://www.example.com/norecurse', 'normal_option'),
894
778
            config.POLICY_NORECURSE)
 
779
        
895
780
 
896
781
    def test_post_commit_default(self):
897
782
        self.get_branch_config('/a/c')
955
840
        self.assertIs(self.my_config.get_user_option('foo'), None)
956
841
        self.my_config.set_user_option('foo', 'bar')
957
842
        self.assertEqual(
958
 
            self.my_config.branch.control_files.files['branch.conf'].strip(),
 
843
            self.my_config.branch.control_files.files['branch.conf'], 
959
844
            'foo = bar')
960
845
        self.assertEqual(self.my_config.get_user_option('foo'), 'bar')
961
846
        self.my_config.set_user_option('foo', 'baz',
963
848
        self.assertEqual(self.my_config.get_user_option('foo'), 'baz')
964
849
        self.my_config.set_user_option('foo', 'qux')
965
850
        self.assertEqual(self.my_config.get_user_option('foo'), 'baz')
966
 
 
967
 
    def test_get_bzr_remote_path(self):
968
 
        my_config = config.LocationConfig('/a/c')
969
 
        self.assertEqual('bzr', my_config.get_bzr_remote_path())
970
 
        my_config.set_user_option('bzr_remote_path', '/path-bzr')
971
 
        self.assertEqual('/path-bzr', my_config.get_bzr_remote_path())
972
 
        os.environ['BZR_REMOTE_PATH'] = '/environ-bzr'
973
 
        self.assertEqual('/environ-bzr', my_config.get_bzr_remote_path())
974
 
 
 
851
        
975
852
 
976
853
precedence_global = 'option = global'
977
854
precedence_branch = 'option = branch'
984
861
"""
985
862
 
986
863
 
987
 
class TestBranchConfigItems(tests.TestCaseInTempDir):
 
864
class TestBranchConfigItems(TestCaseInTempDir):
988
865
 
989
 
    def get_branch_config(self, global_config=None, location=None,
 
866
    def get_branch_config(self, global_config=None, location=None, 
990
867
                          location_config=None, branch_data_config=None):
991
868
        my_config = config.BranchConfig(FakeBranch(location))
992
869
        if global_config is not None:
1006
883
        my_config = config.BranchConfig(branch)
1007
884
        self.assertEqual("Robert Collins <robertc@example.net>",
1008
885
                         my_config.username())
1009
 
        my_config.branch.control_files.files['email'] = "John"
1010
 
        my_config.set_user_option('email',
 
886
        branch.control_files.email = "John"
 
887
        my_config.set_user_option('email', 
1011
888
                                  "Robert Collins <robertc@example.org>")
1012
889
        self.assertEqual("John", my_config.username())
1013
 
        del my_config.branch.control_files.files['email']
 
890
        branch.control_files.email = None
1014
891
        self.assertEqual("Robert Collins <robertc@example.org>",
1015
892
                         my_config.username())
1016
893
 
1017
894
    def test_not_set_in_branch(self):
1018
895
        my_config = self.get_branch_config(sample_config_text)
 
896
        my_config.branch.control_files.email = None
1019
897
        self.assertEqual(u"Erik B\u00e5gfors <erik@bagfors.nu>",
1020
898
                         my_config._get_user_id())
1021
 
        my_config.branch.control_files.files['email'] = "John"
 
899
        my_config.branch.control_files.email = "John"
1022
900
        self.assertEqual("John", my_config._get_user_id())
1023
901
 
1024
902
    def test_BZR_EMAIL_OVERRIDES(self):
1027
905
        my_config = config.BranchConfig(branch)
1028
906
        self.assertEqual("Robert Collins <robertc@example.org>",
1029
907
                         my_config.username())
1030
 
 
 
908
    
1031
909
    def test_signatures_forced(self):
1032
910
        my_config = self.get_branch_config(
1033
911
            global_config=sample_always_signatures)
1077
955
    def test_config_precedence(self):
1078
956
        my_config = self.get_branch_config(global_config=precedence_global)
1079
957
        self.assertEqual(my_config.get_user_option('option'), 'global')
1080
 
        my_config = self.get_branch_config(global_config=precedence_global,
 
958
        my_config = self.get_branch_config(global_config=precedence_global, 
1081
959
                                      branch_data_config=precedence_branch)
1082
960
        self.assertEqual(my_config.get_user_option('option'), 'branch')
1083
 
        my_config = self.get_branch_config(global_config=precedence_global,
 
961
        my_config = self.get_branch_config(global_config=precedence_global, 
1084
962
                                      branch_data_config=precedence_branch,
1085
963
                                      location_config=precedence_location)
1086
964
        self.assertEqual(my_config.get_user_option('option'), 'recurse')
1087
 
        my_config = self.get_branch_config(global_config=precedence_global,
 
965
        my_config = self.get_branch_config(global_config=precedence_global, 
1088
966
                                      branch_data_config=precedence_branch,
1089
967
                                      location_config=precedence_location,
1090
968
                                      location='http://example.com/specific')
1091
969
        self.assertEqual(my_config.get_user_option('option'), 'exact')
1092
970
 
1093
 
    def test_get_mail_client(self):
1094
 
        config = self.get_branch_config()
1095
 
        client = config.get_mail_client()
1096
 
        self.assertIsInstance(client, mail_client.DefaultMail)
1097
 
 
1098
 
        # Specific clients
1099
 
        config.set_user_option('mail_client', 'evolution')
1100
 
        client = config.get_mail_client()
1101
 
        self.assertIsInstance(client, mail_client.Evolution)
1102
 
 
1103
 
        config.set_user_option('mail_client', 'kmail')
1104
 
        client = config.get_mail_client()
1105
 
        self.assertIsInstance(client, mail_client.KMail)
1106
 
 
1107
 
        config.set_user_option('mail_client', 'mutt')
1108
 
        client = config.get_mail_client()
1109
 
        self.assertIsInstance(client, mail_client.Mutt)
1110
 
 
1111
 
        config.set_user_option('mail_client', 'thunderbird')
1112
 
        client = config.get_mail_client()
1113
 
        self.assertIsInstance(client, mail_client.Thunderbird)
1114
 
 
1115
 
        # Generic options
1116
 
        config.set_user_option('mail_client', 'default')
1117
 
        client = config.get_mail_client()
1118
 
        self.assertIsInstance(client, mail_client.DefaultMail)
1119
 
 
1120
 
        config.set_user_option('mail_client', 'editor')
1121
 
        client = config.get_mail_client()
1122
 
        self.assertIsInstance(client, mail_client.Editor)
1123
 
 
1124
 
        config.set_user_option('mail_client', 'mapi')
1125
 
        client = config.get_mail_client()
1126
 
        self.assertIsInstance(client, mail_client.MAPIClient)
1127
 
 
1128
 
        config.set_user_option('mail_client', 'xdg-email')
1129
 
        client = config.get_mail_client()
1130
 
        self.assertIsInstance(client, mail_client.XDGEmail)
1131
 
 
1132
 
        config.set_user_option('mail_client', 'firebird')
1133
 
        self.assertRaises(errors.UnknownMailClient, config.get_mail_client)
1134
 
 
1135
 
 
1136
 
class TestMailAddressExtraction(tests.TestCase):
 
971
 
 
972
class TestMailAddressExtraction(TestCase):
1137
973
 
1138
974
    def test_extract_email_address(self):
1139
975
        self.assertEqual('jane@test.com',
1140
976
                         config.extract_email_address('Jane <jane@test.com>'))
1141
977
        self.assertRaises(errors.NoEmailInUsername,
1142
978
                          config.extract_email_address, 'Jane Tester')
1143
 
 
1144
 
    def test_parse_username(self):
1145
 
        self.assertEqual(('', 'jdoe@example.com'),
1146
 
                         config.parse_username('jdoe@example.com'))
1147
 
        self.assertEqual(('', 'jdoe@example.com'),
1148
 
                         config.parse_username('<jdoe@example.com>'))
1149
 
        self.assertEqual(('John Doe', 'jdoe@example.com'),
1150
 
                         config.parse_username('John Doe <jdoe@example.com>'))
1151
 
        self.assertEqual(('John Doe', ''),
1152
 
                         config.parse_username('John Doe'))
1153
 
        self.assertEqual(('John Doe', 'jdoe@example.com'),
1154
 
                         config.parse_username('John Doe jdoe@example.com'))
1155
 
 
1156
 
class TestTreeConfig(tests.TestCaseWithTransport):
1157
 
 
1158
 
    def test_get_value(self):
1159
 
        """Test that retreiving a value from a section is possible"""
1160
 
        branch = self.make_branch('.')
1161
 
        tree_config = config.TreeConfig(branch)
1162
 
        tree_config.set_option('value', 'key', 'SECTION')
1163
 
        tree_config.set_option('value2', 'key2')
1164
 
        tree_config.set_option('value3-top', 'key3')
1165
 
        tree_config.set_option('value3-section', 'key3', 'SECTION')
1166
 
        value = tree_config.get_option('key', 'SECTION')
1167
 
        self.assertEqual(value, 'value')
1168
 
        value = tree_config.get_option('key2')
1169
 
        self.assertEqual(value, 'value2')
1170
 
        self.assertEqual(tree_config.get_option('non-existant'), None)
1171
 
        value = tree_config.get_option('non-existant', 'SECTION')
1172
 
        self.assertEqual(value, None)
1173
 
        value = tree_config.get_option('non-existant', default='default')
1174
 
        self.assertEqual(value, 'default')
1175
 
        self.assertEqual(tree_config.get_option('key2', 'NOSECTION'), None)
1176
 
        value = tree_config.get_option('key2', 'NOSECTION', default='default')
1177
 
        self.assertEqual(value, 'default')
1178
 
        value = tree_config.get_option('key3')
1179
 
        self.assertEqual(value, 'value3-top')
1180
 
        value = tree_config.get_option('key3', 'SECTION')
1181
 
        self.assertEqual(value, 'value3-section')
1182
 
 
1183
 
 
1184
 
class TestTransportConfig(tests.TestCaseWithTransport):
1185
 
 
1186
 
    def test_get_value(self):
1187
 
        """Test that retreiving a value from a section is possible"""
1188
 
        bzrdir_config = config.TransportConfig(transport.get_transport('.'),
1189
 
                                               'control.conf')
1190
 
        bzrdir_config.set_option('value', 'key', 'SECTION')
1191
 
        bzrdir_config.set_option('value2', 'key2')
1192
 
        bzrdir_config.set_option('value3-top', 'key3')
1193
 
        bzrdir_config.set_option('value3-section', 'key3', 'SECTION')
1194
 
        value = bzrdir_config.get_option('key', 'SECTION')
1195
 
        self.assertEqual(value, 'value')
1196
 
        value = bzrdir_config.get_option('key2')
1197
 
        self.assertEqual(value, 'value2')
1198
 
        self.assertEqual(bzrdir_config.get_option('non-existant'), None)
1199
 
        value = bzrdir_config.get_option('non-existant', 'SECTION')
1200
 
        self.assertEqual(value, None)
1201
 
        value = bzrdir_config.get_option('non-existant', default='default')
1202
 
        self.assertEqual(value, 'default')
1203
 
        self.assertEqual(bzrdir_config.get_option('key2', 'NOSECTION'), None)
1204
 
        value = bzrdir_config.get_option('key2', 'NOSECTION',
1205
 
                                         default='default')
1206
 
        self.assertEqual(value, 'default')
1207
 
        value = bzrdir_config.get_option('key3')
1208
 
        self.assertEqual(value, 'value3-top')
1209
 
        value = bzrdir_config.get_option('key3', 'SECTION')
1210
 
        self.assertEqual(value, 'value3-section')
1211
 
 
1212
 
    def test_set_unset_default_stack_on(self):
1213
 
        my_dir = self.make_bzrdir('.')
1214
 
        bzrdir_config = config.BzrDirConfig(my_dir)
1215
 
        self.assertIs(None, bzrdir_config.get_default_stack_on())
1216
 
        bzrdir_config.set_default_stack_on('Foo')
1217
 
        self.assertEqual('Foo', bzrdir_config._config.get_option(
1218
 
                         'default_stack_on'))
1219
 
        self.assertEqual('Foo', bzrdir_config.get_default_stack_on())
1220
 
        bzrdir_config.set_default_stack_on(None)
1221
 
        self.assertIs(None, bzrdir_config.get_default_stack_on())
1222
 
 
1223
 
 
1224
 
class TestAuthenticationConfigFile(tests.TestCase):
1225
 
    """Test the authentication.conf file matching"""
1226
 
 
1227
 
    def _got_user_passwd(self, expected_user, expected_password,
1228
 
                         config, *args, **kwargs):
1229
 
        credentials = config.get_credentials(*args, **kwargs)
1230
 
        if credentials is None:
1231
 
            user = None
1232
 
            password = None
1233
 
        else:
1234
 
            user = credentials['user']
1235
 
            password = credentials['password']
1236
 
        self.assertEquals(expected_user, user)
1237
 
        self.assertEquals(expected_password, password)
1238
 
 
1239
 
    def test_empty_config(self):
1240
 
        conf = config.AuthenticationConfig(_file=StringIO())
1241
 
        self.assertEquals({}, conf._get_config())
1242
 
        self._got_user_passwd(None, None, conf, 'http', 'foo.net')
1243
 
 
1244
 
    def test_missing_auth_section_header(self):
1245
 
        conf = config.AuthenticationConfig(_file=StringIO('foo = bar'))
1246
 
        self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
1247
 
 
1248
 
    def test_auth_section_header_not_closed(self):
1249
 
        conf = config.AuthenticationConfig(_file=StringIO('[DEF'))
1250
 
        self.assertRaises(errors.ParseConfigError, conf._get_config)
1251
 
 
1252
 
    def test_auth_value_not_boolean(self):
1253
 
        conf = config.AuthenticationConfig(_file=StringIO(
1254
 
                """[broken]
1255
 
scheme=ftp
1256
 
user=joe
1257
 
verify_certificates=askme # Error: Not a boolean
1258
 
"""))
1259
 
        self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
1260
 
 
1261
 
    def test_auth_value_not_int(self):
1262
 
        conf = config.AuthenticationConfig(_file=StringIO(
1263
 
                """[broken]
1264
 
scheme=ftp
1265
 
user=joe
1266
 
port=port # Error: Not an int
1267
 
"""))
1268
 
        self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
1269
 
 
1270
 
    def test_unknown_password_encoding(self):
1271
 
        conf = config.AuthenticationConfig(_file=StringIO(
1272
 
                """[broken]
1273
 
scheme=ftp
1274
 
user=joe
1275
 
password_encoding=unknown
1276
 
"""))
1277
 
        self.assertRaises(ValueError, conf.get_password,
1278
 
                          'ftp', 'foo.net', 'joe')
1279
 
 
1280
 
    def test_credentials_for_scheme_host(self):
1281
 
        conf = config.AuthenticationConfig(_file=StringIO(
1282
 
                """# Identity on foo.net
1283
 
[ftp definition]
1284
 
scheme=ftp
1285
 
host=foo.net
1286
 
user=joe
1287
 
password=secret-pass
1288
 
"""))
1289
 
        # Basic matching
1290
 
        self._got_user_passwd('joe', 'secret-pass', conf, 'ftp', 'foo.net')
1291
 
        # different scheme
1292
 
        self._got_user_passwd(None, None, conf, 'http', 'foo.net')
1293
 
        # different host
1294
 
        self._got_user_passwd(None, None, conf, 'ftp', 'bar.net')
1295
 
 
1296
 
    def test_credentials_for_host_port(self):
1297
 
        conf = config.AuthenticationConfig(_file=StringIO(
1298
 
                """# Identity on foo.net
1299
 
[ftp definition]
1300
 
scheme=ftp
1301
 
port=10021
1302
 
host=foo.net
1303
 
user=joe
1304
 
password=secret-pass
1305
 
"""))
1306
 
        # No port
1307
 
        self._got_user_passwd('joe', 'secret-pass',
1308
 
                              conf, 'ftp', 'foo.net', port=10021)
1309
 
        # different port
1310
 
        self._got_user_passwd(None, None, conf, 'ftp', 'foo.net')
1311
 
 
1312
 
    def test_for_matching_host(self):
1313
 
        conf = config.AuthenticationConfig(_file=StringIO(
1314
 
                """# Identity on foo.net
1315
 
[sourceforge]
1316
 
scheme=bzr
1317
 
host=bzr.sf.net
1318
 
user=joe
1319
 
password=joepass
1320
 
[sourceforge domain]
1321
 
scheme=bzr
1322
 
host=.bzr.sf.net
1323
 
user=georges
1324
 
password=bendover
1325
 
"""))
1326
 
        # matching domain
1327
 
        self._got_user_passwd('georges', 'bendover',
1328
 
                              conf, 'bzr', 'foo.bzr.sf.net')
1329
 
        # phishing attempt
1330
 
        self._got_user_passwd(None, None,
1331
 
                              conf, 'bzr', 'bbzr.sf.net')
1332
 
 
1333
 
    def test_for_matching_host_None(self):
1334
 
        conf = config.AuthenticationConfig(_file=StringIO(
1335
 
                """# Identity on foo.net
1336
 
[catchup bzr]
1337
 
scheme=bzr
1338
 
user=joe
1339
 
password=joepass
1340
 
[DEFAULT]
1341
 
user=georges
1342
 
password=bendover
1343
 
"""))
1344
 
        # match no host
1345
 
        self._got_user_passwd('joe', 'joepass',
1346
 
                              conf, 'bzr', 'quux.net')
1347
 
        # no host but different scheme
1348
 
        self._got_user_passwd('georges', 'bendover',
1349
 
                              conf, 'ftp', 'quux.net')
1350
 
 
1351
 
    def test_credentials_for_path(self):
1352
 
        conf = config.AuthenticationConfig(_file=StringIO(
1353
 
                """
1354
 
[http dir1]
1355
 
scheme=http
1356
 
host=bar.org
1357
 
path=/dir1
1358
 
user=jim
1359
 
password=jimpass
1360
 
[http dir2]
1361
 
scheme=http
1362
 
host=bar.org
1363
 
path=/dir2
1364
 
user=georges
1365
 
password=bendover
1366
 
"""))
1367
 
        # no path no dice
1368
 
        self._got_user_passwd(None, None,
1369
 
                              conf, 'http', host='bar.org', path='/dir3')
1370
 
        # matching path
1371
 
        self._got_user_passwd('georges', 'bendover',
1372
 
                              conf, 'http', host='bar.org', path='/dir2')
1373
 
        # matching subdir
1374
 
        self._got_user_passwd('jim', 'jimpass',
1375
 
                              conf, 'http', host='bar.org',path='/dir1/subdir')
1376
 
 
1377
 
    def test_credentials_for_user(self):
1378
 
        conf = config.AuthenticationConfig(_file=StringIO(
1379
 
                """
1380
 
[with user]
1381
 
scheme=http
1382
 
host=bar.org
1383
 
user=jim
1384
 
password=jimpass
1385
 
"""))
1386
 
        # Get user
1387
 
        self._got_user_passwd('jim', 'jimpass',
1388
 
                              conf, 'http', 'bar.org')
1389
 
        # Get same user
1390
 
        self._got_user_passwd('jim', 'jimpass',
1391
 
                              conf, 'http', 'bar.org', user='jim')
1392
 
        # Don't get a different user if one is specified
1393
 
        self._got_user_passwd(None, None,
1394
 
                              conf, 'http', 'bar.org', user='georges')
1395
 
 
1396
 
    def test_credentials_for_user_without_password(self):
1397
 
        conf = config.AuthenticationConfig(_file=StringIO(
1398
 
                """
1399
 
[without password]
1400
 
scheme=http
1401
 
host=bar.org
1402
 
user=jim
1403
 
"""))
1404
 
        # Get user but no password
1405
 
        self._got_user_passwd('jim', None,
1406
 
                              conf, 'http', 'bar.org')
1407
 
 
1408
 
    def test_verify_certificates(self):
1409
 
        conf = config.AuthenticationConfig(_file=StringIO(
1410
 
                """
1411
 
[self-signed]
1412
 
scheme=https
1413
 
host=bar.org
1414
 
user=jim
1415
 
password=jimpass
1416
 
verify_certificates=False
1417
 
[normal]
1418
 
scheme=https
1419
 
host=foo.net
1420
 
user=georges
1421
 
password=bendover
1422
 
"""))
1423
 
        credentials = conf.get_credentials('https', 'bar.org')
1424
 
        self.assertEquals(False, credentials.get('verify_certificates'))
1425
 
        credentials = conf.get_credentials('https', 'foo.net')
1426
 
        self.assertEquals(True, credentials.get('verify_certificates'))
1427
 
 
1428
 
 
1429
 
class TestAuthenticationStorage(tests.TestCaseInTempDir):
1430
 
 
1431
 
    def test_set_credentials(self):
1432
 
        conf = config.AuthenticationConfig()
1433
 
        conf.set_credentials('name', 'host', 'user', 'scheme', 'password',
1434
 
        99, path='/foo', verify_certificates=False, realm='realm')
1435
 
        credentials = conf.get_credentials(host='host', scheme='scheme',
1436
 
                                           port=99, path='/foo',
1437
 
                                           realm='realm')
1438
 
        CREDENTIALS = {'name': 'name', 'user': 'user', 'password': 'password',
1439
 
                       'verify_certificates': False, 'scheme': 'scheme', 
1440
 
                       'host': 'host', 'port': 99, 'path': '/foo', 
1441
 
                       'realm': 'realm'}
1442
 
        self.assertEqual(CREDENTIALS, credentials)
1443
 
        credentials_from_disk = config.AuthenticationConfig().get_credentials(
1444
 
            host='host', scheme='scheme', port=99, path='/foo', realm='realm')
1445
 
        self.assertEqual(CREDENTIALS, credentials_from_disk)
1446
 
 
1447
 
    def test_reset_credentials_different_name(self):
1448
 
        conf = config.AuthenticationConfig()
1449
 
        conf.set_credentials('name', 'host', 'user', 'scheme', 'password'),
1450
 
        conf.set_credentials('name2', 'host', 'user2', 'scheme', 'password'),
1451
 
        self.assertIs(None, conf._get_config().get('name'))
1452
 
        credentials = conf.get_credentials(host='host', scheme='scheme')
1453
 
        CREDENTIALS = {'name': 'name2', 'user': 'user2', 'password':
1454
 
                       'password', 'verify_certificates': True, 
1455
 
                       'scheme': 'scheme', 'host': 'host', 'port': None, 
1456
 
                       'path': None, 'realm': None}
1457
 
        self.assertEqual(CREDENTIALS, credentials)
1458
 
 
1459
 
 
1460
 
class TestAuthenticationConfig(tests.TestCase):
1461
 
    """Test AuthenticationConfig behaviour"""
1462
 
 
1463
 
    def _check_default_password_prompt(self, expected_prompt_format, scheme,
1464
 
                              host=None, port=None, realm=None, path=None):
1465
 
        if host is None:
1466
 
            host = 'bar.org'
1467
 
        user, password = 'jim', 'precious'
1468
 
        expected_prompt = expected_prompt_format % {
1469
 
            'scheme': scheme, 'host': host, 'port': port,
1470
 
            'user': user, 'realm': realm}
1471
 
 
1472
 
        stdout = tests.StringIOWrapper()
1473
 
        ui.ui_factory = tests.TestUIFactory(stdin=password + '\n',
1474
 
                                            stdout=stdout)
1475
 
        # We use an empty conf so that the user is always prompted
1476
 
        conf = config.AuthenticationConfig()
1477
 
        self.assertEquals(password,
1478
 
                          conf.get_password(scheme, host, user, port=port,
1479
 
                                            realm=realm, path=path))
1480
 
        self.assertEquals(stdout.getvalue(), expected_prompt)
1481
 
 
1482
 
    def _check_default_username_prompt(self, expected_prompt_format, scheme,
1483
 
                              host=None, port=None, realm=None, path=None):
1484
 
        if host is None:
1485
 
            host = 'bar.org'
1486
 
        username = 'jim'
1487
 
        expected_prompt = expected_prompt_format % {
1488
 
            'scheme': scheme, 'host': host, 'port': port,
1489
 
            'realm': realm}
1490
 
        stdout = tests.StringIOWrapper()
1491
 
        ui.ui_factory = tests.TestUIFactory(stdin=username+ '\n',
1492
 
                                            stdout=stdout)
1493
 
        # We use an empty conf so that the user is always prompted
1494
 
        conf = config.AuthenticationConfig()
1495
 
        self.assertEquals(username, conf.get_user(scheme, host, port=port,
1496
 
                          realm=realm, path=path, ask=True))
1497
 
        self.assertEquals(stdout.getvalue(), expected_prompt)
1498
 
 
1499
 
    def test_username_defaults_prompts(self):
1500
 
        # HTTP prompts can't be tested here, see test_http.py
1501
 
        self._check_default_username_prompt('FTP %(host)s username: ', 'ftp')
1502
 
        self._check_default_username_prompt(
1503
 
            'FTP %(host)s:%(port)d username: ', 'ftp', port=10020)
1504
 
        self._check_default_username_prompt(
1505
 
            'SSH %(host)s:%(port)d username: ', 'ssh', port=12345)
1506
 
 
1507
 
    def test_username_default_no_prompt(self):
1508
 
        conf = config.AuthenticationConfig()
1509
 
        self.assertEquals(None,
1510
 
            conf.get_user('ftp', 'example.com'))
1511
 
        self.assertEquals("explicitdefault",
1512
 
            conf.get_user('ftp', 'example.com', default="explicitdefault"))
1513
 
 
1514
 
    def test_password_default_prompts(self):
1515
 
        # HTTP prompts can't be tested here, see test_http.py
1516
 
        self._check_default_password_prompt(
1517
 
            'FTP %(user)s@%(host)s password: ', 'ftp')
1518
 
        self._check_default_password_prompt(
1519
 
            'FTP %(user)s@%(host)s:%(port)d password: ', 'ftp', port=10020)
1520
 
        self._check_default_password_prompt(
1521
 
            'SSH %(user)s@%(host)s:%(port)d password: ', 'ssh', port=12345)
1522
 
        # SMTP port handling is a bit special (it's handled if embedded in the
1523
 
        # host too)
1524
 
        # FIXME: should we: forbid that, extend it to other schemes, leave
1525
 
        # things as they are that's fine thank you ?
1526
 
        self._check_default_password_prompt('SMTP %(user)s@%(host)s password: ',
1527
 
                                            'smtp')
1528
 
        self._check_default_password_prompt('SMTP %(user)s@%(host)s password: ',
1529
 
                                            'smtp', host='bar.org:10025')
1530
 
        self._check_default_password_prompt(
1531
 
            'SMTP %(user)s@%(host)s:%(port)d password: ',
1532
 
            'smtp', port=10025)
1533
 
 
1534
 
    def test_ssh_password_emits_warning(self):
1535
 
        conf = config.AuthenticationConfig(_file=StringIO(
1536
 
                """
1537
 
[ssh with password]
1538
 
scheme=ssh
1539
 
host=bar.org
1540
 
user=jim
1541
 
password=jimpass
1542
 
"""))
1543
 
        entered_password = 'typed-by-hand'
1544
 
        stdout = tests.StringIOWrapper()
1545
 
        ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
1546
 
                                            stdout=stdout)
1547
 
 
1548
 
        # Since the password defined in the authentication config is ignored,
1549
 
        # the user is prompted
1550
 
        self.assertEquals(entered_password,
1551
 
                          conf.get_password('ssh', 'bar.org', user='jim'))
1552
 
        self.assertContainsRe(
1553
 
            self._get_log(keep_log_file=True),
1554
 
            'password ignored in section \[ssh with password\]')
1555
 
 
1556
 
    def test_ssh_without_password_doesnt_emit_warning(self):
1557
 
        conf = config.AuthenticationConfig(_file=StringIO(
1558
 
                """
1559
 
[ssh with password]
1560
 
scheme=ssh
1561
 
host=bar.org
1562
 
user=jim
1563
 
"""))
1564
 
        entered_password = 'typed-by-hand'
1565
 
        stdout = tests.StringIOWrapper()
1566
 
        ui.ui_factory = tests.TestUIFactory(stdin=entered_password + '\n',
1567
 
                                            stdout=stdout)
1568
 
 
1569
 
        # Since the password defined in the authentication config is ignored,
1570
 
        # the user is prompted
1571
 
        self.assertEquals(entered_password,
1572
 
                          conf.get_password('ssh', 'bar.org', user='jim'))
1573
 
        # No warning shoud be emitted since there is no password. We are only
1574
 
        # providing "user".
1575
 
        self.assertNotContainsRe(
1576
 
            self._get_log(keep_log_file=True),
1577
 
            'password ignored in section \[ssh with password\]')
1578
 
 
1579
 
    def test_uses_fallback_stores(self):
1580
 
        self._old_cs_registry = config.credential_store_registry
1581
 
        def restore():
1582
 
            config.credential_store_registry = self._old_cs_registry
1583
 
        self.addCleanup(restore)
1584
 
        config.credential_store_registry = config.CredentialStoreRegistry()
1585
 
        store = StubCredentialStore()
1586
 
        store.add_credentials("http", "example.com", "joe", "secret")
1587
 
        config.credential_store_registry.register("stub", store, fallback=True)
1588
 
        conf = config.AuthenticationConfig(_file=StringIO())
1589
 
        creds = conf.get_credentials("http", "example.com")
1590
 
        self.assertEquals("joe", creds["user"])
1591
 
        self.assertEquals("secret", creds["password"])
1592
 
 
1593
 
 
1594
 
class StubCredentialStore(config.CredentialStore):
1595
 
 
1596
 
    def __init__(self):
1597
 
        self._username = {}
1598
 
        self._password = {}
1599
 
 
1600
 
    def add_credentials(self, scheme, host, user, password=None):
1601
 
        self._username[(scheme, host)] = user
1602
 
        self._password[(scheme, host)] = password
1603
 
 
1604
 
    def get_credentials(self, scheme, host, port=None, user=None,
1605
 
        path=None, realm=None):
1606
 
        key = (scheme, host)
1607
 
        if not key in self._username:
1608
 
            return None
1609
 
        return { "scheme": scheme, "host": host, "port": port,
1610
 
                "user": self._username[key], "password": self._password[key]}
1611
 
 
1612
 
 
1613
 
class CountingCredentialStore(config.CredentialStore):
1614
 
 
1615
 
    def __init__(self):
1616
 
        self._calls = 0
1617
 
 
1618
 
    def get_credentials(self, scheme, host, port=None, user=None,
1619
 
        path=None, realm=None):
1620
 
        self._calls += 1
1621
 
        return None
1622
 
 
1623
 
 
1624
 
class TestCredentialStoreRegistry(tests.TestCase):
1625
 
 
1626
 
    def _get_cs_registry(self):
1627
 
        return config.credential_store_registry
1628
 
 
1629
 
    def test_default_credential_store(self):
1630
 
        r = self._get_cs_registry()
1631
 
        default = r.get_credential_store(None)
1632
 
        self.assertIsInstance(default, config.PlainTextCredentialStore)
1633
 
 
1634
 
    def test_unknown_credential_store(self):
1635
 
        r = self._get_cs_registry()
1636
 
        # It's hard to imagine someone creating a credential store named
1637
 
        # 'unknown' so we use that as an never registered key.
1638
 
        self.assertRaises(KeyError, r.get_credential_store, 'unknown')
1639
 
 
1640
 
    def test_fallback_none_registered(self):
1641
 
        r = config.CredentialStoreRegistry()
1642
 
        self.assertEquals(None,
1643
 
                          r.get_fallback_credentials("http", "example.com"))
1644
 
 
1645
 
    def test_register(self):
1646
 
        r = config.CredentialStoreRegistry()
1647
 
        r.register("stub", StubCredentialStore(), fallback=False)
1648
 
        r.register("another", StubCredentialStore(), fallback=True)
1649
 
        self.assertEquals(["another", "stub"], r.keys())
1650
 
 
1651
 
    def test_register_lazy(self):
1652
 
        r = config.CredentialStoreRegistry()
1653
 
        r.register_lazy("stub", "bzrlib.tests.test_config",
1654
 
                        "StubCredentialStore", fallback=False)
1655
 
        self.assertEquals(["stub"], r.keys())
1656
 
        self.assertIsInstance(r.get_credential_store("stub"),
1657
 
                              StubCredentialStore)
1658
 
 
1659
 
    def test_is_fallback(self):
1660
 
        r = config.CredentialStoreRegistry()
1661
 
        r.register("stub1", None, fallback=False)
1662
 
        r.register("stub2", None, fallback=True)
1663
 
        self.assertEquals(False, r.is_fallback("stub1"))
1664
 
        self.assertEquals(True, r.is_fallback("stub2"))
1665
 
 
1666
 
    def test_no_fallback(self):
1667
 
        r = config.CredentialStoreRegistry()
1668
 
        store = CountingCredentialStore()
1669
 
        r.register("count", store, fallback=False)
1670
 
        self.assertEquals(None,
1671
 
                          r.get_fallback_credentials("http", "example.com"))
1672
 
        self.assertEquals(0, store._calls)
1673
 
 
1674
 
    def test_fallback_credentials(self):
1675
 
        r = config.CredentialStoreRegistry()
1676
 
        store = StubCredentialStore()
1677
 
        store.add_credentials("http", "example.com",
1678
 
                              "somebody", "geheim")
1679
 
        r.register("stub", store, fallback=True)
1680
 
        creds = r.get_fallback_credentials("http", "example.com")
1681
 
        self.assertEquals("somebody", creds["user"])
1682
 
        self.assertEquals("geheim", creds["password"])
1683
 
 
1684
 
    def test_fallback_first_wins(self):
1685
 
        r = config.CredentialStoreRegistry()
1686
 
        stub1 = StubCredentialStore()
1687
 
        stub1.add_credentials("http", "example.com",
1688
 
                              "somebody", "stub1")
1689
 
        r.register("stub1", stub1, fallback=True)
1690
 
        stub2 = StubCredentialStore()
1691
 
        stub2.add_credentials("http", "example.com",
1692
 
                              "somebody", "stub2")
1693
 
        r.register("stub2", stub1, fallback=True)
1694
 
        creds = r.get_fallback_credentials("http", "example.com")
1695
 
        self.assertEquals("somebody", creds["user"])
1696
 
        self.assertEquals("stub1", creds["password"])
1697
 
 
1698
 
 
1699
 
class TestPlainTextCredentialStore(tests.TestCase):
1700
 
 
1701
 
    def test_decode_password(self):
1702
 
        r = config.credential_store_registry
1703
 
        plain_text = r.get_credential_store()
1704
 
        decoded = plain_text.decode_password(dict(password='secret'))
1705
 
        self.assertEquals('secret', decoded)
1706
 
 
1707
 
 
1708
 
# FIXME: Once we have a way to declare authentication to all test servers, we
1709
 
# can implement generic tests.
1710
 
# test_user_password_in_url
1711
 
# test_user_in_url_password_from_config
1712
 
# test_user_in_url_password_prompted
1713
 
# test_user_in_config
1714
 
# test_user_getpass.getuser
1715
 
# test_user_prompted ?
1716
 
class TestAuthenticationRing(tests.TestCaseWithTransport):
1717
 
    pass