~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_config.py

  • Committer: Vincent Ladeuil
  • Date: 2011-06-15 11:36:05 UTC
  • mto: This revision was merged to the branch mainline in revision 5975.
  • Revision ID: v.ladeuil+lp@free.fr-20110615113605-p7zyyfry9wy1hquc
Make ContentConflict resolution more robust

Show diffs side-by-side

added added

removed removed

Lines of Context:
33
33
    errors,
34
34
    osutils,
35
35
    mail_client,
 
36
    mergetools,
36
37
    ui,
37
38
    urlutils,
38
 
    remote,
 
39
    registry,
39
40
    tests,
40
41
    trace,
 
42
    transport,
41
43
    )
42
44
from bzrlib.symbol_versioning import (
43
45
    deprecated_in,
 
46
    deprecated_method,
44
47
    )
45
 
from bzrlib.transport import remote as transport_remote
 
48
from bzrlib.transport import remote
46
49
from bzrlib.tests import (
47
50
    features,
 
51
    TestSkipped,
48
52
    scenarios,
49
 
    test_server,
50
53
    )
51
54
from bzrlib.util.configobj import configobj
52
55
 
112
115
config.test_store_builder_registry.register('branch', build_branch_store)
113
116
 
114
117
 
115
 
def build_control_store(test):
116
 
    build_backing_branch(test, 'branch')
117
 
    b = bzrdir.BzrDir.open('branch')
118
 
    return config.ControlStore(b)
119
 
config.test_store_builder_registry.register('control', build_control_store)
120
 
 
121
 
 
122
118
def build_remote_branch_store(test):
123
119
    # There is only one permutation (but we won't be able to handle more with
124
120
    # this design anyway)
125
 
    (transport_class,
126
 
     server_class) = transport_remote.get_test_permutations()[0]
 
121
    (transport_class, server_class) = remote.get_test_permutations()[0]
127
122
    build_backing_branch(test, 'branch', transport_class, server_class)
128
123
    b = branch.Branch.open(test.get_url('branch'))
129
124
    return config.BranchStore(b)
147
142
def build_remote_branch_stack(test):
148
143
    # There is only one permutation (but we won't be able to handle more with
149
144
    # this design anyway)
150
 
    (transport_class,
151
 
     server_class) = transport_remote.get_test_permutations()[0]
 
145
    (transport_class, server_class) = remote.get_test_permutations()[0]
152
146
    build_backing_branch(test, 'branch', transport_class, server_class)
153
147
    b = branch.Branch.open(test.get_url('branch'))
154
 
    return config.RemoteBranchStack(b)
 
148
    return config.BranchStack(b)
155
149
config.test_stack_builder_registry.register('remote_branch',
156
150
                                            build_remote_branch_stack)
157
151
 
158
 
def build_remote_control_stack(test):
159
 
    # There is only one permutation (but we won't be able to handle more with
160
 
    # this design anyway)
161
 
    (transport_class,
162
 
     server_class) = transport_remote.get_test_permutations()[0]
163
 
    # We need only a bzrdir for this, not a full branch, but it's not worth
164
 
    # creating a dedicated helper to create only the bzrdir
165
 
    build_backing_branch(test, 'branch', transport_class, server_class)
166
 
    b = branch.Branch.open(test.get_url('branch'))
167
 
    return config.RemoteControlStack(b.bzrdir)
168
 
config.test_stack_builder_registry.register('remote_control',
169
 
                                            build_remote_control_stack)
170
 
 
171
152
 
172
153
sample_long_alias="log -r-15..-1 --line"
173
154
sample_config_text = u"""
176
157
editor=vim
177
158
change_editor=vimdiff -of @new_path @old_path
178
159
gpg_signing_command=gnome-gpg
179
 
gpg_signing_key=DD4D5088
180
160
log_format=short
181
 
validate_signatures_in_log=true
182
 
acceptable_keys=amy
183
161
user_global_option=something
184
162
bzr.mergetool.sometool=sometool {base} {this} {other} -o {result}
185
163
bzr.mergetool.funkytool=funkytool "arg with spaces" {this_temp}
186
 
bzr.mergetool.newtool='"newtool with spaces" {this_temp}'
187
164
bzr.default_mergetool=sometool
188
165
[ALIASES]
189
166
h=help
232
209
[/a/]
233
210
check_signatures=check-available
234
211
gpg_signing_command=false
235
 
gpg_signing_key=default
236
212
user_local_option=local
237
213
# test trailing / matching
238
214
[/a/*]
534
510
        my_config = config.Config()
535
511
        self.assertEqual('long', my_config.log_format())
536
512
 
537
 
    def test_acceptable_keys_default(self):
538
 
        my_config = config.Config()
539
 
        self.assertEqual(None, my_config.acceptable_keys())
540
 
 
541
 
    def test_validate_signatures_in_log_default(self):
542
 
        my_config = config.Config()
543
 
        self.assertEqual(False, my_config.validate_signatures_in_log())
544
 
 
545
513
    def test_get_change_editor(self):
546
514
        my_config = InstrumentedConfig()
547
515
        change_editor = my_config.get_change_editor('old_tree', 'new_tree')
836
804
        self.assertEquals(['{foo', '}', '{', 'bar}'],
837
805
                          conf.get_user_option('hidden', expand=True))
838
806
 
839
 
 
840
807
class TestLocationConfigOptionExpansion(tests.TestCaseInTempDir):
841
808
 
842
809
    def get_config(self, location, string=None):
1053
1020
        # automatically cast to list
1054
1021
        self.assertEqual(['x'], get_list('one_item'))
1055
1022
 
1056
 
    def test_get_user_option_as_int_from_SI(self):
1057
 
        conf, parser = self.make_config_parser("""
1058
 
plain = 100
1059
 
si_k = 5k,
1060
 
si_kb = 5kb,
1061
 
si_m = 5M,
1062
 
si_mb = 5MB,
1063
 
si_g = 5g,
1064
 
si_gb = 5gB,
1065
 
""")
1066
 
        get_si = conf.get_user_option_as_int_from_SI
1067
 
        self.assertEqual(100, get_si('plain'))
1068
 
        self.assertEqual(5000, get_si('si_k'))
1069
 
        self.assertEqual(5000, get_si('si_kb'))
1070
 
        self.assertEqual(5000000, get_si('si_m'))
1071
 
        self.assertEqual(5000000, get_si('si_mb'))
1072
 
        self.assertEqual(5000000000, get_si('si_g'))
1073
 
        self.assertEqual(5000000000, get_si('si_gb'))
1074
 
        self.assertEqual(None, get_si('non-exist'))
1075
 
        self.assertEqual(42, get_si('non-exist-with-default',  42))
1076
1023
 
1077
1024
class TestSupressWarning(TestIniConfig):
1078
1025
 
1265
1212
        self.assertEqual("gnome-gpg", my_config.gpg_signing_command())
1266
1213
        self.assertEqual(False, my_config.signature_needed())
1267
1214
 
1268
 
    def test_gpg_signing_key(self):
1269
 
        my_config = self._get_sample_config()
1270
 
        self.assertEqual("DD4D5088", my_config.gpg_signing_key())
1271
 
 
1272
1215
    def _get_empty_config(self):
1273
1216
        my_config = config.GlobalConfig()
1274
1217
        return my_config
1294
1237
        my_config = self._get_sample_config()
1295
1238
        self.assertEqual("short", my_config.log_format())
1296
1239
 
1297
 
    def test_configured_acceptable_keys(self):
1298
 
        my_config = self._get_sample_config()
1299
 
        self.assertEqual("amy", my_config.acceptable_keys())
1300
 
 
1301
 
    def test_configured_validate_signatures_in_log(self):
1302
 
        my_config = self._get_sample_config()
1303
 
        self.assertEqual(True, my_config.validate_signatures_in_log())
1304
 
 
1305
1240
    def test_get_alias(self):
1306
1241
        my_config = self._get_sample_config()
1307
1242
        self.assertEqual('help', my_config.get_alias('h'))
1340
1275
        self.log(repr(tools))
1341
1276
        self.assertEqual(
1342
1277
            {u'funkytool' : u'funkytool "arg with spaces" {this_temp}',
1343
 
            u'sometool' : u'sometool {base} {this} {other} -o {result}',
1344
 
            u'newtool' : u'"newtool with spaces" {this_temp}'},
 
1278
            u'sometool' : u'sometool {base} {this} {other} -o {result}'},
1345
1279
            tools)
1346
1280
 
1347
1281
    def test_get_merge_tools_empty(self):
1565
1499
        self.get_branch_config('/a')
1566
1500
        self.assertEqual("false", self.my_config.gpg_signing_command())
1567
1501
 
1568
 
    def test_gpg_signing_key(self):
1569
 
        self.get_branch_config('/b')
1570
 
        self.assertEqual("DD4D5088", self.my_config.gpg_signing_key())
1571
 
 
1572
 
    def test_gpg_signing_key_default(self):
1573
 
        self.get_branch_config('/a')
1574
 
        self.assertEqual("erik@bagfors.nu", self.my_config.gpg_signing_key())
1575
 
 
1576
1502
    def test_get_user_option_global(self):
1577
1503
        self.get_branch_config('/a')
1578
1504
        self.assertEqual('something',
1942
1868
 
1943
1869
class TestTransportConfig(tests.TestCaseWithTransport):
1944
1870
 
1945
 
    def test_load_utf8(self):
1946
 
        """Ensure we can load an utf8-encoded file."""
1947
 
        t = self.get_transport()
1948
 
        unicode_user = u'b\N{Euro Sign}ar'
1949
 
        unicode_content = u'user=%s' % (unicode_user,)
1950
 
        utf8_content = unicode_content.encode('utf8')
1951
 
        # Store the raw content in the config file
1952
 
        t.put_bytes('foo.conf', utf8_content)
1953
 
        conf = config.TransportConfig(t, 'foo.conf')
1954
 
        self.assertEquals(unicode_user, conf.get_option('user'))
1955
 
 
1956
 
    def test_load_non_ascii(self):
1957
 
        """Ensure we display a proper error on non-ascii, non utf-8 content."""
1958
 
        t = self.get_transport()
1959
 
        t.put_bytes('foo.conf', 'user=foo\n#\xff\n')
1960
 
        conf = config.TransportConfig(t, 'foo.conf')
1961
 
        self.assertRaises(errors.ConfigContentError, conf._get_configobj)
1962
 
 
1963
 
    def test_load_erroneous_content(self):
1964
 
        """Ensure we display a proper error on content that can't be parsed."""
1965
 
        t = self.get_transport()
1966
 
        t.put_bytes('foo.conf', '[open_section\n')
1967
 
        conf = config.TransportConfig(t, 'foo.conf')
1968
 
        self.assertRaises(errors.ParseConfigError, conf._get_configobj)
1969
 
 
1970
 
    def test_load_permission_denied(self):
1971
 
        """Ensure we get an empty config file if the file is inaccessible."""
1972
 
        warnings = []
1973
 
        def warning(*args):
1974
 
            warnings.append(args[0] % args[1:])
1975
 
        self.overrideAttr(trace, 'warning', warning)
1976
 
 
1977
 
        class DenyingTransport(object):
1978
 
 
1979
 
            def __init__(self, base):
1980
 
                self.base = base
1981
 
 
1982
 
            def get_bytes(self, relpath):
1983
 
                raise errors.PermissionDenied(relpath, "")
1984
 
 
1985
 
        cfg = config.TransportConfig(
1986
 
            DenyingTransport("nonexisting://"), 'control.conf')
1987
 
        self.assertIs(None, cfg.get_option('non-existant', 'SECTION'))
1988
 
        self.assertEquals(
1989
 
            warnings,
1990
 
            [u'Permission denied while trying to open configuration file '
1991
 
             u'nonexisting:///control.conf.'])
1992
 
 
1993
1871
    def test_get_value(self):
1994
1872
        """Test that retreiving a value from a section is possible"""
1995
 
        bzrdir_config = config.TransportConfig(self.get_transport('.'),
 
1873
        bzrdir_config = config.TransportConfig(transport.get_transport('.'),
1996
1874
                                               'control.conf')
1997
1875
        bzrdir_config.set_option('value', 'key', 'SECTION')
1998
1876
        bzrdir_config.set_option('value2', 'key2')
2028
1906
        self.assertIs(None, bzrdir_config.get_default_stack_on())
2029
1907
 
2030
1908
 
2031
 
class TestOldConfigHooks(tests.TestCaseWithTransport):
2032
 
 
2033
 
    def setUp(self):
2034
 
        super(TestOldConfigHooks, self).setUp()
2035
 
        create_configs_with_file_option(self)
2036
 
 
2037
 
    def assertGetHook(self, conf, name, value):
2038
 
        calls = []
2039
 
        def hook(*args):
2040
 
            calls.append(args)
2041
 
        config.OldConfigHooks.install_named_hook('get', hook, None)
2042
 
        self.addCleanup(
2043
 
            config.OldConfigHooks.uninstall_named_hook, 'get', None)
2044
 
        self.assertLength(0, calls)
2045
 
        actual_value = conf.get_user_option(name)
2046
 
        self.assertEquals(value, actual_value)
2047
 
        self.assertLength(1, calls)
2048
 
        self.assertEquals((conf, name, value), calls[0])
2049
 
 
2050
 
    def test_get_hook_bazaar(self):
2051
 
        self.assertGetHook(self.bazaar_config, 'file', 'bazaar')
2052
 
 
2053
 
    def test_get_hook_locations(self):
2054
 
        self.assertGetHook(self.locations_config, 'file', 'locations')
2055
 
 
2056
 
    def test_get_hook_branch(self):
2057
 
        # Since locations masks branch, we define a different option
2058
 
        self.branch_config.set_user_option('file2', 'branch')
2059
 
        self.assertGetHook(self.branch_config, 'file2', 'branch')
2060
 
 
2061
 
    def assertSetHook(self, conf, name, value):
2062
 
        calls = []
2063
 
        def hook(*args):
2064
 
            calls.append(args)
2065
 
        config.OldConfigHooks.install_named_hook('set', hook, None)
2066
 
        self.addCleanup(
2067
 
            config.OldConfigHooks.uninstall_named_hook, 'set', None)
2068
 
        self.assertLength(0, calls)
2069
 
        conf.set_user_option(name, value)
2070
 
        self.assertLength(1, calls)
2071
 
        # We can't assert the conf object below as different configs use
2072
 
        # different means to implement set_user_option and we care only about
2073
 
        # coverage here.
2074
 
        self.assertEquals((name, value), calls[0][1:])
2075
 
 
2076
 
    def test_set_hook_bazaar(self):
2077
 
        self.assertSetHook(self.bazaar_config, 'foo', 'bazaar')
2078
 
 
2079
 
    def test_set_hook_locations(self):
2080
 
        self.assertSetHook(self.locations_config, 'foo', 'locations')
2081
 
 
2082
 
    def test_set_hook_branch(self):
2083
 
        self.assertSetHook(self.branch_config, 'foo', 'branch')
2084
 
 
2085
 
    def assertRemoveHook(self, conf, name, section_name=None):
2086
 
        calls = []
2087
 
        def hook(*args):
2088
 
            calls.append(args)
2089
 
        config.OldConfigHooks.install_named_hook('remove', hook, None)
2090
 
        self.addCleanup(
2091
 
            config.OldConfigHooks.uninstall_named_hook, 'remove', None)
2092
 
        self.assertLength(0, calls)
2093
 
        conf.remove_user_option(name, section_name)
2094
 
        self.assertLength(1, calls)
2095
 
        # We can't assert the conf object below as different configs use
2096
 
        # different means to implement remove_user_option and we care only about
2097
 
        # coverage here.
2098
 
        self.assertEquals((name,), calls[0][1:])
2099
 
 
2100
 
    def test_remove_hook_bazaar(self):
2101
 
        self.assertRemoveHook(self.bazaar_config, 'file')
2102
 
 
2103
 
    def test_remove_hook_locations(self):
2104
 
        self.assertRemoveHook(self.locations_config, 'file',
2105
 
                              self.locations_config.location)
2106
 
 
2107
 
    def test_remove_hook_branch(self):
2108
 
        self.assertRemoveHook(self.branch_config, 'file')
2109
 
 
2110
 
    def assertLoadHook(self, name, conf_class, *conf_args):
2111
 
        calls = []
2112
 
        def hook(*args):
2113
 
            calls.append(args)
2114
 
        config.OldConfigHooks.install_named_hook('load', hook, None)
2115
 
        self.addCleanup(
2116
 
            config.OldConfigHooks.uninstall_named_hook, 'load', None)
2117
 
        self.assertLength(0, calls)
2118
 
        # Build a config
2119
 
        conf = conf_class(*conf_args)
2120
 
        # Access an option to trigger a load
2121
 
        conf.get_user_option(name)
2122
 
        self.assertLength(1, calls)
2123
 
        # Since we can't assert about conf, we just use the number of calls ;-/
2124
 
 
2125
 
    def test_load_hook_bazaar(self):
2126
 
        self.assertLoadHook('file', config.GlobalConfig)
2127
 
 
2128
 
    def test_load_hook_locations(self):
2129
 
        self.assertLoadHook('file', config.LocationConfig, self.tree.basedir)
2130
 
 
2131
 
    def test_load_hook_branch(self):
2132
 
        self.assertLoadHook('file', config.BranchConfig, self.tree.branch)
2133
 
 
2134
 
    def assertSaveHook(self, conf):
2135
 
        calls = []
2136
 
        def hook(*args):
2137
 
            calls.append(args)
2138
 
        config.OldConfigHooks.install_named_hook('save', hook, None)
2139
 
        self.addCleanup(
2140
 
            config.OldConfigHooks.uninstall_named_hook, 'save', None)
2141
 
        self.assertLength(0, calls)
2142
 
        # Setting an option triggers a save
2143
 
        conf.set_user_option('foo', 'bar')
2144
 
        self.assertLength(1, calls)
2145
 
        # Since we can't assert about conf, we just use the number of calls ;-/
2146
 
 
2147
 
    def test_save_hook_bazaar(self):
2148
 
        self.assertSaveHook(self.bazaar_config)
2149
 
 
2150
 
    def test_save_hook_locations(self):
2151
 
        self.assertSaveHook(self.locations_config)
2152
 
 
2153
 
    def test_save_hook_branch(self):
2154
 
        self.assertSaveHook(self.branch_config)
2155
 
 
2156
 
 
2157
 
class TestOldConfigHooksForRemote(tests.TestCaseWithTransport):
2158
 
    """Tests config hooks for remote configs.
2159
 
 
2160
 
    No tests for the remove hook as this is not implemented there.
2161
 
    """
2162
 
 
2163
 
    def setUp(self):
2164
 
        super(TestOldConfigHooksForRemote, self).setUp()
2165
 
        self.transport_server = test_server.SmartTCPServer_for_testing
2166
 
        create_configs_with_file_option(self)
2167
 
 
2168
 
    def assertGetHook(self, conf, name, value):
2169
 
        calls = []
2170
 
        def hook(*args):
2171
 
            calls.append(args)
2172
 
        config.OldConfigHooks.install_named_hook('get', hook, None)
2173
 
        self.addCleanup(
2174
 
            config.OldConfigHooks.uninstall_named_hook, 'get', None)
2175
 
        self.assertLength(0, calls)
2176
 
        actual_value = conf.get_option(name)
2177
 
        self.assertEquals(value, actual_value)
2178
 
        self.assertLength(1, calls)
2179
 
        self.assertEquals((conf, name, value), calls[0])
2180
 
 
2181
 
    def test_get_hook_remote_branch(self):
2182
 
        remote_branch = branch.Branch.open(self.get_url('tree'))
2183
 
        self.assertGetHook(remote_branch._get_config(), 'file', 'branch')
2184
 
 
2185
 
    def test_get_hook_remote_bzrdir(self):
2186
 
        remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2187
 
        conf = remote_bzrdir._get_config()
2188
 
        conf.set_option('remotedir', 'file')
2189
 
        self.assertGetHook(conf, 'file', 'remotedir')
2190
 
 
2191
 
    def assertSetHook(self, conf, name, value):
2192
 
        calls = []
2193
 
        def hook(*args):
2194
 
            calls.append(args)
2195
 
        config.OldConfigHooks.install_named_hook('set', hook, None)
2196
 
        self.addCleanup(
2197
 
            config.OldConfigHooks.uninstall_named_hook, 'set', None)
2198
 
        self.assertLength(0, calls)
2199
 
        conf.set_option(value, name)
2200
 
        self.assertLength(1, calls)
2201
 
        # We can't assert the conf object below as different configs use
2202
 
        # different means to implement set_user_option and we care only about
2203
 
        # coverage here.
2204
 
        self.assertEquals((name, value), calls[0][1:])
2205
 
 
2206
 
    def test_set_hook_remote_branch(self):
2207
 
        remote_branch = branch.Branch.open(self.get_url('tree'))
2208
 
        self.addCleanup(remote_branch.lock_write().unlock)
2209
 
        self.assertSetHook(remote_branch._get_config(), 'file', 'remote')
2210
 
 
2211
 
    def test_set_hook_remote_bzrdir(self):
2212
 
        remote_branch = branch.Branch.open(self.get_url('tree'))
2213
 
        self.addCleanup(remote_branch.lock_write().unlock)
2214
 
        remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2215
 
        self.assertSetHook(remote_bzrdir._get_config(), 'file', 'remotedir')
2216
 
 
2217
 
    def assertLoadHook(self, expected_nb_calls, name, conf_class, *conf_args):
2218
 
        calls = []
2219
 
        def hook(*args):
2220
 
            calls.append(args)
2221
 
        config.OldConfigHooks.install_named_hook('load', hook, None)
2222
 
        self.addCleanup(
2223
 
            config.OldConfigHooks.uninstall_named_hook, 'load', None)
2224
 
        self.assertLength(0, calls)
2225
 
        # Build a config
2226
 
        conf = conf_class(*conf_args)
2227
 
        # Access an option to trigger a load
2228
 
        conf.get_option(name)
2229
 
        self.assertLength(expected_nb_calls, calls)
2230
 
        # Since we can't assert about conf, we just use the number of calls ;-/
2231
 
 
2232
 
    def test_load_hook_remote_branch(self):
2233
 
        remote_branch = branch.Branch.open(self.get_url('tree'))
2234
 
        self.assertLoadHook(1, 'file', remote.RemoteBranchConfig, remote_branch)
2235
 
 
2236
 
    def test_load_hook_remote_bzrdir(self):
2237
 
        remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2238
 
        # The config file doesn't exist, set an option to force its creation
2239
 
        conf = remote_bzrdir._get_config()
2240
 
        conf.set_option('remotedir', 'file')
2241
 
        # We get one call for the server and one call for the client, this is
2242
 
        # caused by the differences in implementations betwen
2243
 
        # SmartServerBzrDirRequestConfigFile (in smart/bzrdir.py) and
2244
 
        # SmartServerBranchGetConfigFile (in smart/branch.py)
2245
 
        self.assertLoadHook(2 ,'file', remote.RemoteBzrDirConfig, remote_bzrdir)
2246
 
 
2247
 
    def assertSaveHook(self, conf):
2248
 
        calls = []
2249
 
        def hook(*args):
2250
 
            calls.append(args)
2251
 
        config.OldConfigHooks.install_named_hook('save', hook, None)
2252
 
        self.addCleanup(
2253
 
            config.OldConfigHooks.uninstall_named_hook, 'save', None)
2254
 
        self.assertLength(0, calls)
2255
 
        # Setting an option triggers a save
2256
 
        conf.set_option('foo', 'bar')
2257
 
        self.assertLength(1, calls)
2258
 
        # Since we can't assert about conf, we just use the number of calls ;-/
2259
 
 
2260
 
    def test_save_hook_remote_branch(self):
2261
 
        remote_branch = branch.Branch.open(self.get_url('tree'))
2262
 
        self.addCleanup(remote_branch.lock_write().unlock)
2263
 
        self.assertSaveHook(remote_branch._get_config())
2264
 
 
2265
 
    def test_save_hook_remote_bzrdir(self):
2266
 
        remote_branch = branch.Branch.open(self.get_url('tree'))
2267
 
        self.addCleanup(remote_branch.lock_write().unlock)
2268
 
        remote_bzrdir = bzrdir.BzrDir.open(self.get_url('tree'))
2269
 
        self.assertSaveHook(remote_bzrdir._get_config())
2270
 
 
2271
 
 
2272
1909
class TestOption(tests.TestCase):
2273
1910
 
2274
1911
    def test_default_value(self):
2275
1912
        opt = config.Option('foo', default='bar')
2276
1913
        self.assertEquals('bar', opt.get_default())
2277
1914
 
2278
 
    def test_default_value_from_env(self):
2279
 
        opt = config.Option('foo', default='bar', default_from_env=['FOO'])
2280
 
        self.overrideEnv('FOO', 'quux')
2281
 
        # Env variable provides a default taking over the option one
2282
 
        self.assertEquals('quux', opt.get_default())
2283
 
 
2284
 
    def test_first_default_value_from_env_wins(self):
2285
 
        opt = config.Option('foo', default='bar',
2286
 
                            default_from_env=['NO_VALUE', 'FOO', 'BAZ'])
2287
 
        self.overrideEnv('FOO', 'foo')
2288
 
        self.overrideEnv('BAZ', 'baz')
2289
 
        # The first env var set wins
2290
 
        self.assertEquals('foo', opt.get_default())
2291
 
 
2292
 
    def test_not_supported_list_default_value(self):
2293
 
        self.assertRaises(AssertionError, config.Option, 'foo', default=[1])
2294
 
 
2295
 
    def test_not_supported_object_default_value(self):
2296
 
        self.assertRaises(AssertionError, config.Option, 'foo',
2297
 
                          default=object())
2298
 
 
2299
 
 
2300
 
class TestOptionConverterMixin(object):
2301
 
 
2302
 
    def assertConverted(self, expected, opt, value):
2303
 
        self.assertEquals(expected, opt.convert_from_unicode(value))
2304
 
 
2305
 
    def assertWarns(self, opt, value):
2306
 
        warnings = []
2307
 
        def warning(*args):
2308
 
            warnings.append(args[0] % args[1:])
2309
 
        self.overrideAttr(trace, 'warning', warning)
2310
 
        self.assertEquals(None, opt.convert_from_unicode(value))
2311
 
        self.assertLength(1, warnings)
2312
 
        self.assertEquals(
2313
 
            'Value "%s" is not valid for "%s"' % (value, opt.name),
2314
 
            warnings[0])
2315
 
 
2316
 
    def assertErrors(self, opt, value):
2317
 
        self.assertRaises(errors.ConfigOptionValueError,
2318
 
                          opt.convert_from_unicode, value)
2319
 
 
2320
 
    def assertConvertInvalid(self, opt, invalid_value):
2321
 
        opt.invalid = None
2322
 
        self.assertEquals(None, opt.convert_from_unicode(invalid_value))
2323
 
        opt.invalid = 'warning'
2324
 
        self.assertWarns(opt, invalid_value)
2325
 
        opt.invalid = 'error'
2326
 
        self.assertErrors(opt, invalid_value)
2327
 
 
2328
 
 
2329
 
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2330
 
 
2331
 
    def get_option(self):
2332
 
        return config.Option('foo', help='A boolean.',
2333
 
                             from_unicode=config.bool_from_store)
2334
 
 
2335
 
    def test_convert_invalid(self):
2336
 
        opt = self.get_option()
2337
 
        # A string that is not recognized as a boolean
2338
 
        self.assertConvertInvalid(opt, u'invalid-boolean')
2339
 
        # A list of strings is never recognized as a boolean
2340
 
        self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2341
 
 
2342
 
    def test_convert_valid(self):
2343
 
        opt = self.get_option()
2344
 
        self.assertConverted(True, opt, u'True')
2345
 
        self.assertConverted(True, opt, u'1')
2346
 
        self.assertConverted(False, opt, u'False')
2347
 
 
2348
 
 
2349
 
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2350
 
 
2351
 
    def get_option(self):
2352
 
        return config.Option('foo', help='An integer.',
2353
 
                             from_unicode=config.int_from_store)
2354
 
 
2355
 
    def test_convert_invalid(self):
2356
 
        opt = self.get_option()
2357
 
        # A string that is not recognized as an integer
2358
 
        self.assertConvertInvalid(opt, u'forty-two')
2359
 
        # A list of strings is never recognized as an integer
2360
 
        self.assertConvertInvalid(opt, [u'a', u'list'])
2361
 
 
2362
 
    def test_convert_valid(self):
2363
 
        opt = self.get_option()
2364
 
        self.assertConverted(16, opt, u'16')
2365
 
 
2366
 
class TestOptionWithListConverter(tests.TestCase, TestOptionConverterMixin):
2367
 
 
2368
 
    def get_option(self):
2369
 
        return config.Option('foo', help='A list.',
2370
 
                             from_unicode=config.list_from_store)
2371
 
 
2372
 
    def test_convert_invalid(self):
2373
 
        # No string is invalid as all forms can be converted to a list
2374
 
        pass
2375
 
 
2376
 
    def test_convert_valid(self):
2377
 
        opt = self.get_option()
2378
 
        # An empty string is an empty list
2379
 
        self.assertConverted([], opt, '') # Using a bare str() just in case
2380
 
        self.assertConverted([], opt, u'')
2381
 
        # A boolean
2382
 
        self.assertConverted([u'True'], opt, u'True')
2383
 
        # An integer
2384
 
        self.assertConverted([u'42'], opt, u'42')
2385
 
        # A single string
2386
 
        self.assertConverted([u'bar'], opt, u'bar')
2387
 
        # A list remains a list (configObj will turn a string containing commas
2388
 
        # into a list, but that's not what we're testing here)
2389
 
        self.assertConverted([u'foo', u'1', u'True'],
2390
 
                             opt, [u'foo', u'1', u'True'])
2391
 
 
2392
 
 
2393
 
class TestOptionConverterMixin(object):
2394
 
 
2395
 
    def assertConverted(self, expected, opt, value):
2396
 
        self.assertEquals(expected, opt.convert_from_unicode(value))
2397
 
 
2398
 
    def assertWarns(self, opt, value):
2399
 
        warnings = []
2400
 
        def warning(*args):
2401
 
            warnings.append(args[0] % args[1:])
2402
 
        self.overrideAttr(trace, 'warning', warning)
2403
 
        self.assertEquals(None, opt.convert_from_unicode(value))
2404
 
        self.assertLength(1, warnings)
2405
 
        self.assertEquals(
2406
 
            'Value "%s" is not valid for "%s"' % (value, opt.name),
2407
 
            warnings[0])
2408
 
 
2409
 
    def assertErrors(self, opt, value):
2410
 
        self.assertRaises(errors.ConfigOptionValueError,
2411
 
                          opt.convert_from_unicode, value)
2412
 
 
2413
 
    def assertConvertInvalid(self, opt, invalid_value):
2414
 
        opt.invalid = None
2415
 
        self.assertEquals(None, opt.convert_from_unicode(invalid_value))
2416
 
        opt.invalid = 'warning'
2417
 
        self.assertWarns(opt, invalid_value)
2418
 
        opt.invalid = 'error'
2419
 
        self.assertErrors(opt, invalid_value)
2420
 
 
2421
 
 
2422
 
class TestOptionWithBooleanConverter(tests.TestCase, TestOptionConverterMixin):
2423
 
 
2424
 
    def get_option(self):
2425
 
        return config.Option('foo', help='A boolean.',
2426
 
                             from_unicode=config.bool_from_store)
2427
 
 
2428
 
    def test_convert_invalid(self):
2429
 
        opt = self.get_option()
2430
 
        # A string that is not recognized as a boolean
2431
 
        self.assertConvertInvalid(opt, u'invalid-boolean')
2432
 
        # A list of strings is never recognized as a boolean
2433
 
        self.assertConvertInvalid(opt, [u'not', u'a', u'boolean'])
2434
 
 
2435
 
    def test_convert_valid(self):
2436
 
        opt = self.get_option()
2437
 
        self.assertConverted(True, opt, u'True')
2438
 
        self.assertConverted(True, opt, u'1')
2439
 
        self.assertConverted(False, opt, u'False')
2440
 
 
2441
 
 
2442
 
class TestOptionWithIntegerConverter(tests.TestCase, TestOptionConverterMixin):
2443
 
 
2444
 
    def get_option(self):
2445
 
        return config.Option('foo', help='An integer.',
2446
 
                             from_unicode=config.int_from_store)
2447
 
 
2448
 
    def test_convert_invalid(self):
2449
 
        opt = self.get_option()
2450
 
        # A string that is not recognized as an integer
2451
 
        self.assertConvertInvalid(opt, u'forty-two')
2452
 
        # A list of strings is never recognized as an integer
2453
 
        self.assertConvertInvalid(opt, [u'a', u'list'])
2454
 
 
2455
 
    def test_convert_valid(self):
2456
 
        opt = self.get_option()
2457
 
        self.assertConverted(16, opt, u'16')
2458
 
 
2459
 
 
2460
 
class TestOptionWithListConverter(tests.TestCase, TestOptionConverterMixin):
2461
 
 
2462
 
    def get_option(self):
2463
 
        return config.Option('foo', help='A list.',
2464
 
                             from_unicode=config.list_from_store)
2465
 
 
2466
 
    def test_convert_invalid(self):
2467
 
        opt = self.get_option()
2468
 
        # We don't even try to convert a list into a list, we only expect
2469
 
        # strings
2470
 
        self.assertConvertInvalid(opt, [1])
2471
 
        # No string is invalid as all forms can be converted to a list
2472
 
 
2473
 
    def test_convert_valid(self):
2474
 
        opt = self.get_option()
2475
 
        # An empty string is an empty list
2476
 
        self.assertConverted([], opt, '') # Using a bare str() just in case
2477
 
        self.assertConverted([], opt, u'')
2478
 
        # A boolean
2479
 
        self.assertConverted([u'True'], opt, u'True')
2480
 
        # An integer
2481
 
        self.assertConverted([u'42'], opt, u'42')
2482
 
        # A single string
2483
 
        self.assertConverted([u'bar'], opt, u'bar')
2484
 
 
2485
1915
 
2486
1916
class TestOptionRegistry(tests.TestCase):
2487
1917
 
2488
1918
    def setUp(self):
2489
1919
        super(TestOptionRegistry, self).setUp()
2490
1920
        # Always start with an empty registry
2491
 
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
 
1921
        self.overrideAttr(config, 'option_registry', registry.Registry())
2492
1922
        self.registry = config.option_registry
2493
1923
 
2494
1924
    def test_register(self):
2495
1925
        opt = config.Option('foo')
2496
 
        self.registry.register(opt)
 
1926
        self.registry.register('foo', opt)
2497
1927
        self.assertIs(opt, self.registry.get('foo'))
2498
1928
 
 
1929
    lazy_option = config.Option('lazy_foo')
 
1930
 
 
1931
    def test_register_lazy(self):
 
1932
        self.registry.register_lazy('foo', self.__module__,
 
1933
                                    'TestOptionRegistry.lazy_option')
 
1934
        self.assertIs(self.lazy_option, self.registry.get('foo'))
 
1935
 
2499
1936
    def test_registered_help(self):
2500
 
        opt = config.Option('foo', help='A simple option')
2501
 
        self.registry.register(opt)
 
1937
        opt = config.Option('foo')
 
1938
        self.registry.register('foo', opt, help='A simple option')
2502
1939
        self.assertEquals('A simple option', self.registry.get_help('foo'))
2503
1940
 
2504
 
    lazy_option = config.Option('lazy_foo', help='Lazy help')
2505
 
 
2506
 
    def test_register_lazy(self):
2507
 
        self.registry.register_lazy('lazy_foo', self.__module__,
2508
 
                                    'TestOptionRegistry.lazy_option')
2509
 
        self.assertIs(self.lazy_option, self.registry.get('lazy_foo'))
2510
 
 
2511
 
    def test_registered_lazy_help(self):
2512
 
        self.registry.register_lazy('lazy_foo', self.__module__,
2513
 
                                    'TestOptionRegistry.lazy_option')
2514
 
        self.assertEquals('Lazy help', self.registry.get_help('lazy_foo'))
2515
 
 
2516
1941
 
2517
1942
class TestRegisteredOptions(tests.TestCase):
2518
1943
    """All registered options should verify some constraints."""
2532
1957
    def test_help_is_set(self):
2533
1958
        option_help = self.registry.get_help(self.option_name)
2534
1959
        self.assertNotEquals(None, option_help)
2535
 
        # Come on, think about the user, he really wants to know what the
 
1960
        # Come on, think about the user, he really wants to know whst the
2536
1961
        # option is about
2537
 
        self.assertIsNot(None, option_help)
2538
1962
        self.assertNotEquals('', option_help)
2539
1963
 
2540
1964
 
2562
1986
 
2563
1987
class TestMutableSection(tests.TestCase):
2564
1988
 
2565
 
    scenarios = [('mutable',
2566
 
                  {'get_section':
2567
 
                       lambda opts: config.MutableSection('myID', opts)},),
2568
 
                 ('cmdline',
2569
 
                  {'get_section':
2570
 
                       lambda opts: config.CommandLineSection(opts)},),
2571
 
        ]
 
1989
    # FIXME: Parametrize so that all sections (including os.environ and the
 
1990
    # ones produced by Stores) run these tests -- vila 2011-04-01
2572
1991
 
2573
1992
    def test_set(self):
2574
1993
        a_dict = dict(foo='bar')
2575
 
        section = self.get_section(a_dict)
 
1994
        section = config.MutableSection('myID', a_dict)
2576
1995
        section.set('foo', 'new_value')
2577
1996
        self.assertEquals('new_value', section.get('foo'))
2578
1997
        # The change appears in the shared section
2583
2002
 
2584
2003
    def test_set_preserve_original_once(self):
2585
2004
        a_dict = dict(foo='bar')
2586
 
        section = self.get_section(a_dict)
 
2005
        section = config.MutableSection('myID', a_dict)
2587
2006
        section.set('foo', 'first_value')
2588
2007
        section.set('foo', 'second_value')
2589
2008
        # We keep track of the original value
2592
2011
 
2593
2012
    def test_remove(self):
2594
2013
        a_dict = dict(foo='bar')
2595
 
        section = self.get_section(a_dict)
 
2014
        section = config.MutableSection('myID', a_dict)
2596
2015
        section.remove('foo')
2597
2016
        # We get None for unknown options via the default value
2598
2017
        self.assertEquals(None, section.get('foo'))
2605
2024
 
2606
2025
    def test_remove_new_option(self):
2607
2026
        a_dict = dict()
2608
 
        section = self.get_section(a_dict)
 
2027
        section = config.MutableSection('myID', a_dict)
2609
2028
        section.set('foo', 'bar')
2610
2029
        section.remove('foo')
2611
2030
        self.assertFalse('foo' in section.options)
2615
2034
        self.assertEquals(config._NewlyCreatedOption, section.orig['foo'])
2616
2035
 
2617
2036
 
2618
 
class TestCommandLineSection(tests.TestCase):
2619
 
 
2620
 
    def setUp(self):
2621
 
        super(TestCommandLineSection, self).setUp()
2622
 
        self.section = config.CommandLineSection()
2623
 
 
2624
 
    def test_no_override(self):
2625
 
        self.section._from_cmdline([])
2626
 
        # FIXME: we want some iterator over all options, failing that, we peek
2627
 
        # under the cover -- vila 2011-09026
2628
 
        self.assertLength(0, self.section.options)
2629
 
 
2630
 
    def test_simple_override(self):
2631
 
        self.section._from_cmdline(['a=b'])
2632
 
        self.assertEqual('b', self.section.get('a'))
2633
 
 
2634
 
    def test_list_override(self):
2635
 
        self.section._from_cmdline(['l=1,2,3'])
2636
 
        val = self.section.get('l')
2637
 
        self.assertEqual('1,2,3', val)
2638
 
        # Reminder: lists should registered as such explicitely, otherwise the
2639
 
        # conversion needs to be done afterwards.
2640
 
        self.assertEqual(['1', '2', '3'], config.list_from_store(val))
2641
 
 
2642
 
    def test_multiple_overrides(self):
2643
 
        self.section._from_cmdline(['a=b', 'x=y'])
2644
 
        self.assertEquals('b', self.section.get('a'))
2645
 
        self.assertEquals('y', self.section.get('x'))
2646
 
 
2647
 
    def test_wrong_syntax(self):
2648
 
        self.assertRaises(errors.BzrCommandError,
2649
 
                          self.section._from_cmdline, ['a=b', 'c'])
2650
 
 
2651
 
 
2652
2037
class TestStore(tests.TestCaseWithTransport):
2653
2038
 
2654
2039
    def assertSectionContent(self, expected, section):
2665
2050
    scenarios = [(key, {'get_store': builder}) for key, builder
2666
2051
                 in config.test_store_builder_registry.iteritems()]
2667
2052
 
 
2053
    def setUp(self):
 
2054
        super(TestReadonlyStore, self).setUp()
 
2055
 
2668
2056
    def test_building_delays_load(self):
2669
2057
        store = self.get_store(self)
2670
2058
        self.assertEquals(False, store.is_loaded())
2696
2084
        self.assertRaises(AssertionError, store._load_from_string, 'bar=baz')
2697
2085
 
2698
2086
 
2699
 
class TestIniFileStoreContent(tests.TestCaseWithTransport):
2700
 
    """Simulate loading a config store with content of various encodings.
2701
 
 
2702
 
    All files produced by bzr are in utf8 content.
2703
 
 
2704
 
    Users may modify them manually and end up with a file that can't be
2705
 
    loaded. We need to issue proper error messages in this case.
2706
 
    """
2707
 
 
2708
 
    invalid_utf8_char = '\xff'
2709
 
 
2710
 
    def test_load_utf8(self):
2711
 
        """Ensure we can load an utf8-encoded file."""
2712
 
        t = self.get_transport()
2713
 
        # From http://pad.lv/799212
2714
 
        unicode_user = u'b\N{Euro Sign}ar'
2715
 
        unicode_content = u'user=%s' % (unicode_user,)
2716
 
        utf8_content = unicode_content.encode('utf8')
2717
 
        # Store the raw content in the config file
2718
 
        t.put_bytes('foo.conf', utf8_content)
2719
 
        store = config.IniFileStore(t, 'foo.conf')
2720
 
        store.load()
2721
 
        stack = config.Stack([store.get_sections], store)
2722
 
        self.assertEquals(unicode_user, stack.get('user'))
2723
 
 
2724
 
    def test_load_non_ascii(self):
2725
 
        """Ensure we display a proper error on non-ascii, non utf-8 content."""
2726
 
        t = self.get_transport()
2727
 
        t.put_bytes('foo.conf', 'user=foo\n#%s\n' % (self.invalid_utf8_char,))
2728
 
        store = config.IniFileStore(t, 'foo.conf')
2729
 
        self.assertRaises(errors.ConfigContentError, store.load)
2730
 
 
2731
 
    def test_load_erroneous_content(self):
2732
 
        """Ensure we display a proper error on content that can't be parsed."""
2733
 
        t = self.get_transport()
2734
 
        t.put_bytes('foo.conf', '[open_section\n')
2735
 
        store = config.IniFileStore(t, 'foo.conf')
2736
 
        self.assertRaises(errors.ParseConfigError, store.load)
2737
 
 
2738
 
    def test_load_permission_denied(self):
2739
 
        """Ensure we get warned when trying to load an inaccessible file."""
2740
 
        warnings = []
2741
 
        def warning(*args):
2742
 
            warnings.append(args[0] % args[1:])
2743
 
        self.overrideAttr(trace, 'warning', warning)
2744
 
 
2745
 
        t = self.get_transport()
2746
 
 
2747
 
        def get_bytes(relpath):
2748
 
            raise errors.PermissionDenied(relpath, "")
2749
 
        t.get_bytes = get_bytes
2750
 
        store = config.IniFileStore(t, 'foo.conf')
2751
 
        self.assertRaises(errors.PermissionDenied, store.load)
2752
 
        self.assertEquals(
2753
 
            warnings,
2754
 
            [u'Permission denied while trying to load configuration store %s.'
2755
 
             % store.external_url()])
2756
 
 
2757
 
 
2758
 
class TestIniConfigContent(tests.TestCaseWithTransport):
2759
 
    """Simulate loading a IniBasedConfig with content of various encodings.
2760
 
 
2761
 
    All files produced by bzr are in utf8 content.
2762
 
 
2763
 
    Users may modify them manually and end up with a file that can't be
2764
 
    loaded. We need to issue proper error messages in this case.
2765
 
    """
2766
 
 
2767
 
    invalid_utf8_char = '\xff'
2768
 
 
2769
 
    def test_load_utf8(self):
2770
 
        """Ensure we can load an utf8-encoded file."""
2771
 
        # From http://pad.lv/799212
2772
 
        unicode_user = u'b\N{Euro Sign}ar'
2773
 
        unicode_content = u'user=%s' % (unicode_user,)
2774
 
        utf8_content = unicode_content.encode('utf8')
2775
 
        # Store the raw content in the config file
2776
 
        with open('foo.conf', 'wb') as f:
2777
 
            f.write(utf8_content)
2778
 
        conf = config.IniBasedConfig(file_name='foo.conf')
2779
 
        self.assertEquals(unicode_user, conf.get_user_option('user'))
2780
 
 
2781
 
    def test_load_badly_encoded_content(self):
2782
 
        """Ensure we display a proper error on non-ascii, non utf-8 content."""
2783
 
        with open('foo.conf', 'wb') as f:
2784
 
            f.write('user=foo\n#%s\n' % (self.invalid_utf8_char,))
2785
 
        conf = config.IniBasedConfig(file_name='foo.conf')
2786
 
        self.assertRaises(errors.ConfigContentError, conf._get_parser)
2787
 
 
2788
 
    def test_load_erroneous_content(self):
2789
 
        """Ensure we display a proper error on content that can't be parsed."""
2790
 
        with open('foo.conf', 'wb') as f:
2791
 
            f.write('[open_section\n')
2792
 
        conf = config.IniBasedConfig(file_name='foo.conf')
2793
 
        self.assertRaises(errors.ParseConfigError, conf._get_parser)
2794
 
 
2795
 
 
2796
2087
class TestMutableStore(TestStore):
2797
2088
 
2798
2089
    scenarios = [(key, {'store_id': key, 'get_store': builder}) for key, builder
2876
2167
        self.assertLength(1, sections)
2877
2168
        self.assertSectionContent(('baz', {'foo': 'bar'}), sections[0])
2878
2169
 
2879
 
    def test_load_hook(self):
2880
 
        # We first needs to ensure that the store exists
2881
 
        store = self.get_store(self)
2882
 
        section = store.get_mutable_section('baz')
2883
 
        section.set('foo', 'bar')
2884
 
        store.save()
2885
 
        # Now we can try to load it
2886
 
        store = self.get_store(self)
2887
 
        calls = []
2888
 
        def hook(*args):
2889
 
            calls.append(args)
2890
 
        config.ConfigHooks.install_named_hook('load', hook, None)
2891
 
        self.assertLength(0, calls)
2892
 
        store.load()
2893
 
        self.assertLength(1, calls)
2894
 
        self.assertEquals((store,), calls[0])
2895
 
 
2896
 
    def test_save_hook(self):
2897
 
        calls = []
2898
 
        def hook(*args):
2899
 
            calls.append(args)
2900
 
        config.ConfigHooks.install_named_hook('save', hook, None)
2901
 
        self.assertLength(0, calls)
2902
 
        store = self.get_store(self)
2903
 
        section = store.get_mutable_section('baz')
2904
 
        section.set('foo', 'bar')
2905
 
        store.save()
2906
 
        self.assertLength(1, calls)
2907
 
        self.assertEquals((store,), calls[0])
2908
 
 
2909
2170
 
2910
2171
class TestIniFileStore(TestStore):
2911
2172
 
2944
2205
        sections = list(store.get_sections())
2945
2206
        self.assertLength(4, sections)
2946
2207
        # The default section has no name.
2947
 
        # List values are provided as strings and need to be explicitly
2948
 
        # converted by specifying from_unicode=list_from_store at option
2949
 
        # registration
2950
 
        self.assertSectionContent((None, {'foo': 'bar', 'l': u'1,2'}),
 
2208
        # List values are provided as lists
 
2209
        self.assertSectionContent((None, {'foo': 'bar', 'l': ['1', '2']}),
2951
2210
                                  sections[0])
2952
2211
        self.assertSectionContent(
2953
2212
            ('DEFAULT', {'foo_in_DEFAULT': 'foo_DEFAULT'}), sections[1])
3099
2358
    # FIXME: It may be worth looking into removing the lock dir when it's not
3100
2359
    # needed anymore and look at possible fallouts for concurrent lockers. This
3101
2360
    # will matter if/when we use config files outside of bazaar directories
3102
 
    # (.bazaar or .bzr) -- vila 20110-04-111
 
2361
    # (.bazaar or .bzr) -- vila 20110-04-11
3103
2362
 
3104
2363
 
3105
2364
class TestSectionMatcher(TestStore):
3106
2365
 
3107
 
    scenarios = [('location', {'matcher': config.LocationMatcher}),
3108
 
                 ('id', {'matcher': config.NameMatcher}),]
 
2366
    scenarios = [('location', {'matcher': config.LocationMatcher})]
3109
2367
 
3110
 
    def setUp(self):
3111
 
        super(TestSectionMatcher, self).setUp()
3112
 
        # Any simple store is good enough
3113
 
        self.get_store = config.test_store_builder_registry.get('configobj')
 
2368
    def get_store(self, file_name):
 
2369
        return config.IniFileStore(self.get_readonly_transport(), file_name)
3114
2370
 
3115
2371
    def test_no_matches_for_empty_stores(self):
3116
 
        store = self.get_store(self)
 
2372
        store = self.get_store('foo.conf')
3117
2373
        store._load_from_string('')
3118
2374
        matcher = self.matcher(store, '/bar')
3119
2375
        self.assertEquals([], list(matcher.get_sections()))
3120
2376
 
3121
2377
    def test_build_doesnt_load_store(self):
3122
 
        store = self.get_store(self)
 
2378
        store = self.get_store('foo.conf')
3123
2379
        matcher = self.matcher(store, '/bar')
3124
2380
        self.assertFalse(store.is_loaded())
3125
2381
 
3149
2405
 
3150
2406
class TestLocationMatcher(TestStore):
3151
2407
 
3152
 
    def setUp(self):
3153
 
        super(TestLocationMatcher, self).setUp()
3154
 
        # Any simple store is good enough
3155
 
        self.get_store = config.test_store_builder_registry.get('configobj')
3156
 
 
3157
 
    def test_unrelated_section_excluded(self):
3158
 
        store = self.get_store(self)
3159
 
        store._load_from_string('''
3160
 
[/foo]
3161
 
section=/foo
3162
 
[/foo/baz]
3163
 
section=/foo/baz
3164
 
[/foo/bar]
3165
 
section=/foo/bar
3166
 
[/foo/bar/baz]
3167
 
section=/foo/bar/baz
3168
 
[/quux/quux]
3169
 
section=/quux/quux
3170
 
''')
3171
 
        self.assertEquals(['/foo', '/foo/baz', '/foo/bar', '/foo/bar/baz',
3172
 
                           '/quux/quux'],
3173
 
                          [section.id for section in store.get_sections()])
3174
 
        matcher = config.LocationMatcher(store, '/foo/bar/quux')
3175
 
        sections = list(matcher.get_sections())
3176
 
        self.assertEquals([3, 2],
3177
 
                          [section.length for section in sections])
3178
 
        self.assertEquals(['/foo/bar', '/foo'],
3179
 
                          [section.id for section in sections])
3180
 
        self.assertEquals(['quux', 'bar/quux'],
3181
 
                          [section.extra_path for section in sections])
 
2408
    def get_store(self, file_name):
 
2409
        return config.IniFileStore(self.get_readonly_transport(), file_name)
3182
2410
 
3183
2411
    def test_more_specific_sections_first(self):
3184
 
        store = self.get_store(self)
 
2412
        store = self.get_store('foo.conf')
3185
2413
        store._load_from_string('''
3186
2414
[/foo]
3187
2415
section=/foo
3202
2430
    def test_appendpath_in_no_name_section(self):
3203
2431
        # It's a bit weird to allow appendpath in a no-name section, but
3204
2432
        # someone may found a use for it
3205
 
        store = self.get_store(self)
 
2433
        store = self.get_store('foo.conf')
3206
2434
        store._load_from_string('''
3207
2435
foo=bar
3208
2436
foo:policy = appendpath
3213
2441
        self.assertEquals('bar/dir/subdir', sections[0].get('foo'))
3214
2442
 
3215
2443
    def test_file_urls_are_normalized(self):
3216
 
        store = self.get_store(self)
 
2444
        store = self.get_store('foo.conf')
3217
2445
        if sys.platform == 'win32':
3218
2446
            expected_url = 'file:///C:/dir/subdir'
3219
2447
            expected_location = 'C:/dir/subdir'
3224
2452
        self.assertEquals(expected_location, matcher.location)
3225
2453
 
3226
2454
 
3227
 
class TestNameMatcher(TestStore):
3228
 
 
3229
 
    def setUp(self):
3230
 
        super(TestNameMatcher, self).setUp()
3231
 
        self.matcher = config.NameMatcher
3232
 
        # Any simple store is good enough
3233
 
        self.get_store = config.test_store_builder_registry.get('configobj')
3234
 
 
3235
 
    def get_matching_sections(self, name):
3236
 
        store = self.get_store(self)
3237
 
        store._load_from_string('''
3238
 
[foo]
3239
 
option=foo
3240
 
[foo/baz]
3241
 
option=foo/baz
3242
 
[bar]
3243
 
option=bar
3244
 
''')
3245
 
        matcher = self.matcher(store, name)
3246
 
        return list(matcher.get_sections())
3247
 
 
3248
 
    def test_matching(self):
3249
 
        sections = self.get_matching_sections('foo')
3250
 
        self.assertLength(1, sections)
3251
 
        self.assertSectionContent(('foo', {'option': 'foo'}), sections[0])
3252
 
 
3253
 
    def test_not_matching(self):
3254
 
        sections = self.get_matching_sections('baz')
3255
 
        self.assertLength(0, sections)
3256
 
 
3257
 
 
3258
2455
class TestStackGet(tests.TestCase):
3259
2456
 
3260
2457
    # FIXME: This should be parametrized for all known Stack or dedicated
3261
2458
    # paramerized tests created to avoid bloating -- vila 2011-03-31
3262
2459
 
3263
 
    def overrideOptionRegistry(self):
3264
 
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3265
 
 
3266
2460
    def test_single_config_get(self):
3267
2461
        conf = dict(foo='bar')
3268
2462
        conf_stack = config.Stack([conf])
3271
2465
    def test_get_with_registered_default_value(self):
3272
2466
        conf_stack = config.Stack([dict()])
3273
2467
        opt = config.Option('foo', default='bar')
3274
 
        self.overrideOptionRegistry()
 
2468
        self.overrideAttr(config, 'option_registry', registry.Registry())
3275
2469
        config.option_registry.register('foo', opt)
3276
2470
        self.assertEquals('bar', conf_stack.get('foo'))
3277
2471
 
3278
2472
    def test_get_without_registered_default_value(self):
3279
2473
        conf_stack = config.Stack([dict()])
3280
2474
        opt = config.Option('foo')
3281
 
        self.overrideOptionRegistry()
 
2475
        self.overrideAttr(config, 'option_registry', registry.Registry())
3282
2476
        config.option_registry.register('foo', opt)
3283
2477
        self.assertEquals(None, conf_stack.get('foo'))
3284
2478
 
3285
2479
    def test_get_without_default_value_for_not_registered(self):
3286
2480
        conf_stack = config.Stack([dict()])
3287
2481
        opt = config.Option('foo')
3288
 
        self.overrideOptionRegistry()
 
2482
        self.overrideAttr(config, 'option_registry', registry.Registry())
3289
2483
        self.assertEquals(None, conf_stack.get('foo'))
3290
2484
 
3291
2485
    def test_get_first_definition(self):
3300
2494
        conf_stack = config.Stack([conf1, conf2])
3301
2495
        self.assertEquals('baz', conf_stack.get('foo'))
3302
2496
 
 
2497
    def test_get_for_empty_stack(self):
 
2498
        conf_stack = config.Stack([])
 
2499
        self.assertEquals(None, conf_stack.get('foo'))
 
2500
 
3303
2501
    def test_get_for_empty_section_callable(self):
3304
2502
        conf_stack = config.Stack([lambda : []])
3305
2503
        self.assertEquals(None, conf_stack.get('foo'))
3323
2521
        stack = self.get_stack(self)
3324
2522
 
3325
2523
 
3326
 
class TestStackGet(TestStackWithTransport):
3327
 
 
3328
 
    def setUp(self):
3329
 
        super(TestStackGet, self).setUp()
3330
 
        self.conf = self.get_stack(self)
3331
 
 
3332
 
    def test_get_for_empty_stack(self):
3333
 
        self.assertEquals(None, self.conf.get('foo'))
3334
 
 
3335
 
    def test_get_hook(self):
3336
 
        self.conf.store._load_from_string('foo=bar')
3337
 
        calls = []
3338
 
        def hook(*args):
3339
 
            calls.append(args)
3340
 
        config.ConfigHooks.install_named_hook('get', hook, None)
3341
 
        self.assertLength(0, calls)
3342
 
        value = self.conf.get('foo')
3343
 
        self.assertEquals('bar', value)
3344
 
        self.assertLength(1, calls)
3345
 
        self.assertEquals((self.conf, 'foo', 'bar'), calls[0])
3346
 
 
3347
 
 
3348
 
class TestStackGetWithConverter(TestStackGet):
3349
 
 
3350
 
    def setUp(self):
3351
 
        super(TestStackGetWithConverter, self).setUp()
3352
 
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3353
 
        self.registry = config.option_registry
3354
 
 
3355
 
    def register_bool_option(self, name, default=None, default_from_env=None):
3356
 
        b = config.Option(name, help='A boolean.',
3357
 
                          default=default, default_from_env=default_from_env,
3358
 
                          from_unicode=config.bool_from_store)
3359
 
        self.registry.register(b)
3360
 
 
3361
 
    def test_get_default_bool_None(self):
3362
 
        self.register_bool_option('foo')
3363
 
        self.assertEquals(None, self.conf.get('foo'))
3364
 
 
3365
 
    def test_get_default_bool_True(self):
3366
 
        self.register_bool_option('foo', u'True')
3367
 
        self.assertEquals(True, self.conf.get('foo'))
3368
 
 
3369
 
    def test_get_default_bool_False(self):
3370
 
        self.register_bool_option('foo', False)
3371
 
        self.assertEquals(False, self.conf.get('foo'))
3372
 
 
3373
 
    def test_get_default_bool_False_as_string(self):
3374
 
        self.register_bool_option('foo', u'False')
3375
 
        self.assertEquals(False, self.conf.get('foo'))
3376
 
 
3377
 
    def test_get_default_bool_from_env_converted(self):
3378
 
        self.register_bool_option('foo', u'True', default_from_env=['FOO'])
3379
 
        self.overrideEnv('FOO', 'False')
3380
 
        self.assertEquals(False, self.conf.get('foo'))
3381
 
 
3382
 
    def test_get_default_bool_when_conversion_fails(self):
3383
 
        self.register_bool_option('foo', default='True')
3384
 
        self.conf.store._load_from_string('foo=invalid boolean')
3385
 
        self.assertEquals(True, self.conf.get('foo'))
3386
 
 
3387
 
    def register_integer_option(self, name,
3388
 
                                default=None, default_from_env=None):
3389
 
        i = config.Option(name, help='An integer.',
3390
 
                          default=default, default_from_env=default_from_env,
3391
 
                          from_unicode=config.int_from_store)
3392
 
        self.registry.register(i)
3393
 
 
3394
 
    def test_get_default_integer_None(self):
3395
 
        self.register_integer_option('foo')
3396
 
        self.assertEquals(None, self.conf.get('foo'))
3397
 
 
3398
 
    def test_get_default_integer(self):
3399
 
        self.register_integer_option('foo', 42)
3400
 
        self.assertEquals(42, self.conf.get('foo'))
3401
 
 
3402
 
    def test_get_default_integer_as_string(self):
3403
 
        self.register_integer_option('foo', u'42')
3404
 
        self.assertEquals(42, self.conf.get('foo'))
3405
 
 
3406
 
    def test_get_default_integer_from_env(self):
3407
 
        self.register_integer_option('foo', default_from_env=['FOO'])
3408
 
        self.overrideEnv('FOO', '18')
3409
 
        self.assertEquals(18, self.conf.get('foo'))
3410
 
 
3411
 
    def test_get_default_integer_when_conversion_fails(self):
3412
 
        self.register_integer_option('foo', default='12')
3413
 
        self.conf.store._load_from_string('foo=invalid integer')
3414
 
        self.assertEquals(12, self.conf.get('foo'))
3415
 
 
3416
 
    def register_list_option(self, name, default=None, default_from_env=None):
3417
 
        l = config.Option(name, help='A list.',
3418
 
                          default=default, default_from_env=default_from_env,
3419
 
                          from_unicode=config.list_from_store)
3420
 
        self.registry.register(l)
3421
 
 
3422
 
    def test_get_default_list_None(self):
3423
 
        self.register_list_option('foo')
3424
 
        self.assertEquals(None, self.conf.get('foo'))
3425
 
 
3426
 
    def test_get_default_list_empty(self):
3427
 
        self.register_list_option('foo', '')
3428
 
        self.assertEquals([], self.conf.get('foo'))
3429
 
 
3430
 
    def test_get_default_list_from_env(self):
3431
 
        self.register_list_option('foo', default_from_env=['FOO'])
3432
 
        self.overrideEnv('FOO', '')
3433
 
        self.assertEquals([], self.conf.get('foo'))
3434
 
 
3435
 
    def test_get_with_list_converter_no_item(self):
3436
 
        self.register_list_option('foo', None)
3437
 
        self.conf.store._load_from_string('foo=,')
3438
 
        self.assertEquals([], self.conf.get('foo'))
3439
 
 
3440
 
    def test_get_with_list_converter_many_items(self):
3441
 
        self.register_list_option('foo', None)
3442
 
        self.conf.store._load_from_string('foo=m,o,r,e')
3443
 
        self.assertEquals(['m', 'o', 'r', 'e'], self.conf.get('foo'))
3444
 
 
3445
 
    def test_get_with_list_converter_embedded_spaces_many_items(self):
3446
 
        self.register_list_option('foo', None)
3447
 
        self.conf.store._load_from_string('foo=" bar", "baz "')
3448
 
        self.assertEquals([' bar', 'baz '], self.conf.get('foo'))
3449
 
 
3450
 
    def test_get_with_list_converter_stripped_spaces_many_items(self):
3451
 
        self.register_list_option('foo', None)
3452
 
        self.conf.store._load_from_string('foo= bar ,  baz ')
3453
 
        self.assertEquals(['bar', 'baz'], self.conf.get('foo'))
3454
 
 
3455
 
 
3456
 
class TestStackExpandOptions(tests.TestCaseWithTransport):
3457
 
 
3458
 
    def setUp(self):
3459
 
        super(TestStackExpandOptions, self).setUp()
3460
 
        self.overrideAttr(config, 'option_registry', config.OptionRegistry())
3461
 
        self.registry = config.option_registry
3462
 
        self.conf = build_branch_stack(self)
3463
 
 
3464
 
    def assertExpansion(self, expected, string, env=None):
3465
 
        self.assertEquals(expected, self.conf.expand_options(string, env))
3466
 
 
3467
 
    def test_no_expansion(self):
3468
 
        self.assertExpansion('foo', 'foo')
3469
 
 
3470
 
    def test_expand_default_value(self):
3471
 
        self.conf.store._load_from_string('bar=baz')
3472
 
        self.registry.register(config.Option('foo', default=u'{bar}'))
3473
 
        self.assertEquals('baz', self.conf.get('foo', expand=True))
3474
 
 
3475
 
    def test_expand_default_from_env(self):
3476
 
        self.conf.store._load_from_string('bar=baz')
3477
 
        self.registry.register(config.Option('foo', default_from_env=['FOO']))
3478
 
        self.overrideEnv('FOO', '{bar}')
3479
 
        self.assertEquals('baz', self.conf.get('foo', expand=True))
3480
 
 
3481
 
    def test_expand_default_on_failed_conversion(self):
3482
 
        self.conf.store._load_from_string('baz=bogus\nbar=42\nfoo={baz}')
3483
 
        self.registry.register(
3484
 
            config.Option('foo', default=u'{bar}',
3485
 
                          from_unicode=config.int_from_store))
3486
 
        self.assertEquals(42, self.conf.get('foo', expand=True))
3487
 
 
3488
 
    def test_env_adding_options(self):
3489
 
        self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3490
 
 
3491
 
    def test_env_overriding_options(self):
3492
 
        self.conf.store._load_from_string('foo=baz')
3493
 
        self.assertExpansion('bar', '{foo}', {'foo': 'bar'})
3494
 
 
3495
 
    def test_simple_ref(self):
3496
 
        self.conf.store._load_from_string('foo=xxx')
3497
 
        self.assertExpansion('xxx', '{foo}')
3498
 
 
3499
 
    def test_unknown_ref(self):
3500
 
        self.assertRaises(errors.ExpandingUnknownOption,
3501
 
                          self.conf.expand_options, '{foo}')
3502
 
 
3503
 
    def test_indirect_ref(self):
3504
 
        self.conf.store._load_from_string('''
3505
 
foo=xxx
3506
 
bar={foo}
3507
 
''')
3508
 
        self.assertExpansion('xxx', '{bar}')
3509
 
 
3510
 
    def test_embedded_ref(self):
3511
 
        self.conf.store._load_from_string('''
3512
 
foo=xxx
3513
 
bar=foo
3514
 
''')
3515
 
        self.assertExpansion('xxx', '{{bar}}')
3516
 
 
3517
 
    def test_simple_loop(self):
3518
 
        self.conf.store._load_from_string('foo={foo}')
3519
 
        self.assertRaises(errors.OptionExpansionLoop,
3520
 
                          self.conf.expand_options, '{foo}')
3521
 
 
3522
 
    def test_indirect_loop(self):
3523
 
        self.conf.store._load_from_string('''
3524
 
foo={bar}
3525
 
bar={baz}
3526
 
baz={foo}''')
3527
 
        e = self.assertRaises(errors.OptionExpansionLoop,
3528
 
                              self.conf.expand_options, '{foo}')
3529
 
        self.assertEquals('foo->bar->baz', e.refs)
3530
 
        self.assertEquals('{foo}', e.string)
3531
 
 
3532
 
    def test_list(self):
3533
 
        self.conf.store._load_from_string('''
3534
 
foo=start
3535
 
bar=middle
3536
 
baz=end
3537
 
list={foo},{bar},{baz}
3538
 
''')
3539
 
        self.registry.register(
3540
 
            config.Option('list', from_unicode=config.list_from_store))
3541
 
        self.assertEquals(['start', 'middle', 'end'],
3542
 
                           self.conf.get('list', expand=True))
3543
 
 
3544
 
    def test_cascading_list(self):
3545
 
        self.conf.store._load_from_string('''
3546
 
foo=start,{bar}
3547
 
bar=middle,{baz}
3548
 
baz=end
3549
 
list={foo}
3550
 
''')
3551
 
        self.registry.register(
3552
 
            config.Option('list', from_unicode=config.list_from_store))
3553
 
        self.assertEquals(['start', 'middle', 'end'],
3554
 
                           self.conf.get('list', expand=True))
3555
 
 
3556
 
    def test_pathologically_hidden_list(self):
3557
 
        self.conf.store._load_from_string('''
3558
 
foo=bin
3559
 
bar=go
3560
 
start={foo
3561
 
middle=},{
3562
 
end=bar}
3563
 
hidden={start}{middle}{end}
3564
 
''')
3565
 
        # What matters is what the registration says, the conversion happens
3566
 
        # only after all expansions have been performed
3567
 
        self.registry.register(
3568
 
            config.Option('hidden', from_unicode=config.list_from_store))
3569
 
        self.assertEquals(['bin', 'go'],
3570
 
                          self.conf.get('hidden', expand=True))
3571
 
 
3572
 
 
3573
 
class TestStackCrossSectionsExpand(tests.TestCaseWithTransport):
3574
 
 
3575
 
    def setUp(self):
3576
 
        super(TestStackCrossSectionsExpand, self).setUp()
3577
 
 
3578
 
    def get_config(self, location, string):
3579
 
        if string is None:
3580
 
            string = ''
3581
 
        # Since we don't save the config we won't strictly require to inherit
3582
 
        # from TestCaseInTempDir, but an error occurs so quickly...
3583
 
        c = config.LocationStack(location)
3584
 
        c.store._load_from_string(string)
3585
 
        return c
3586
 
 
3587
 
    def test_dont_cross_unrelated_section(self):
3588
 
        c = self.get_config('/another/branch/path','''
3589
 
[/one/branch/path]
3590
 
foo = hello
3591
 
bar = {foo}/2
3592
 
 
3593
 
[/another/branch/path]
3594
 
bar = {foo}/2
3595
 
''')
3596
 
        self.assertRaises(errors.ExpandingUnknownOption,
3597
 
                          c.get, 'bar', expand=True)
3598
 
 
3599
 
    def test_cross_related_sections(self):
3600
 
        c = self.get_config('/project/branch/path','''
3601
 
[/project]
3602
 
foo = qu
3603
 
 
3604
 
[/project/branch/path]
3605
 
bar = {foo}ux
3606
 
''')
3607
 
        self.assertEquals('quux', c.get('bar', expand=True))
3608
 
 
3609
 
 
3610
2524
class TestStackSet(TestStackWithTransport):
3611
2525
 
3612
2526
    def test_simple_set(self):
3622
2536
        conf.set('foo', 'baz')
3623
2537
        self.assertEquals, 'baz', conf.get('foo')
3624
2538
 
3625
 
    def test_set_hook(self):
3626
 
        calls = []
3627
 
        def hook(*args):
3628
 
            calls.append(args)
3629
 
        config.ConfigHooks.install_named_hook('set', hook, None)
3630
 
        self.assertLength(0, calls)
3631
 
        conf = self.get_stack(self)
3632
 
        conf.set('foo', 'bar')
3633
 
        self.assertLength(1, calls)
3634
 
        self.assertEquals((conf, 'foo', 'bar'), calls[0])
3635
 
 
3636
2539
 
3637
2540
class TestStackRemove(TestStackWithTransport):
3638
2541
 
3639
2542
    def test_remove_existing(self):
3640
2543
        conf = self.get_stack(self)
3641
 
        conf.set('foo', 'bar')
 
2544
        conf.store._load_from_string('foo=bar')
3642
2545
        self.assertEquals('bar', conf.get('foo'))
3643
2546
        conf.remove('foo')
3644
2547
        # Did we get it back ?
3648
2551
        conf = self.get_stack(self)
3649
2552
        self.assertRaises(KeyError, conf.remove, 'I_do_not_exist')
3650
2553
 
3651
 
    def test_remove_hook(self):
3652
 
        calls = []
3653
 
        def hook(*args):
3654
 
            calls.append(args)
3655
 
        config.ConfigHooks.install_named_hook('remove', hook, None)
3656
 
        self.assertLength(0, calls)
3657
 
        conf = self.get_stack(self)
3658
 
        conf.set('foo', 'bar')
3659
 
        conf.remove('foo')
3660
 
        self.assertLength(1, calls)
3661
 
        self.assertEquals((conf, 'foo'), calls[0])
3662
 
 
3663
2554
 
3664
2555
class TestConfigGetOptions(tests.TestCaseWithTransport, TestOptionsMixin):
3665
2556
 
3832
2723
        self.assertEquals({}, conf._get_config())
3833
2724
        self._got_user_passwd(None, None, conf, 'http', 'foo.net')
3834
2725
 
3835
 
    def test_non_utf8_config(self):
3836
 
        conf = config.AuthenticationConfig(_file=StringIO(
3837
 
                'foo = bar\xff'))
3838
 
        self.assertRaises(errors.ConfigContentError, conf._get_config)
3839
 
 
3840
2726
    def test_missing_auth_section_header(self):
3841
2727
        conf = config.AuthenticationConfig(_file=StringIO('foo = bar'))
3842
2728
        self.assertRaises(ValueError, conf.get_credentials, 'ftp', 'foo.net')
4330
3216
        to be able to choose a user name with no configuration.
4331
3217
        """
4332
3218
        if sys.platform == 'win32':
4333
 
            raise tests.TestSkipped(
4334
 
                "User name inference not implemented on win32")
 
3219
            raise TestSkipped("User name inference not implemented on win32")
4335
3220
        realname, address = config._auto_user_id()
4336
3221
        if os.path.exists('/etc/mailname'):
4337
3222
            self.assertIsNot(None, realname)