~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_win32utils.py

(jelmer) Support upgrading between the 2a and development-colo formats.
 (Jelmer Vernooij)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007 Canonical Ltd
 
1
# Copyright (C) 2007-2011 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
"""Tests for win32utils."""
16
18
 
17
19
import os
18
 
import sys
19
20
 
20
 
from bzrlib import osutils
21
 
from bzrlib.tests import TestCase, TestCaseInTempDir, Feature
 
21
from bzrlib import (
 
22
    osutils,
 
23
    tests,
 
24
    win32utils,
 
25
    )
 
26
from bzrlib.tests import (
 
27
    TestCase,
 
28
    TestCaseInTempDir,
 
29
    TestSkipped,
 
30
    )
 
31
from bzrlib.tests.features import backslashdir_feature
22
32
from bzrlib.win32utils import glob_expand, get_app_path
23
 
 
24
 
 
25
 
# Features
26
 
# --------
27
 
 
28
 
class _NeedsGlobExpansionFeature(Feature):
29
 
 
30
 
    def _probe(self):
31
 
        return sys.platform == 'win32'
32
 
 
33
 
    def feature_name(self):
34
 
        return 'Internally performed glob expansion'
35
 
 
36
 
NeedsGlobExpansionFeature = _NeedsGlobExpansionFeature()
37
 
 
38
 
 
39
 
class _Win32RegistryFeature(Feature):
40
 
 
41
 
    def _probe(self):
42
 
        try:
43
 
            import _winreg
44
 
            return True
45
 
        except ImportError:
46
 
            return False
47
 
 
48
 
    def feature_name(self):
49
 
        return '_winreg'
50
 
 
51
 
Win32RegistryFeature = _Win32RegistryFeature()
 
33
from bzrlib.tests import (
 
34
    features,
 
35
    )
 
36
 
 
37
 
 
38
Win32RegistryFeature = features.ModuleAvailableFeature('_winreg')
 
39
CtypesFeature = features.ModuleAvailableFeature('ctypes')
 
40
Win32comShellFeature = features.ModuleAvailableFeature('win32com.shell')
 
41
Win32ApiFeature = features.ModuleAvailableFeature('win32api') 
52
42
 
53
43
 
54
44
# Tests
55
45
# -----
56
46
 
57
 
class TestNeedsGlobExpansionFeature(TestCase):
58
 
    
59
 
    def test_available(self):
60
 
        self.assertEqual(sys.platform == 'win32', 
61
 
                         NeedsGlobExpansionFeature.available())
62
 
        
63
 
    def test_str(self):
64
 
        self.assertTrue("performed" in str(NeedsGlobExpansionFeature))
65
 
 
66
 
 
67
47
class TestWin32UtilsGlobExpand(TestCaseInTempDir):
68
48
 
69
 
    _test_needs_features = [NeedsGlobExpansionFeature]
70
 
   
 
49
    _test_needs_features = []
 
50
 
71
51
    def test_empty_tree(self):
72
52
        self.build_tree([])
73
53
        self._run_testset([
75
55
            [['?'], ['?']],
76
56
            [['*'], ['*']],
77
57
            [['a', 'a'], ['a', 'a']]])
78
 
        
 
58
 
 
59
    def build_ascii_tree(self):
 
60
        self.build_tree(['a', 'a1', 'a2', 'a11', 'a.1',
 
61
                         'b', 'b1', 'b2', 'b3',
 
62
                         'c/', 'c/c1', 'c/c2',
 
63
                         'd/', 'd/d1', 'd/d2', 'd/e/', 'd/e/e1'])
 
64
 
 
65
    def build_unicode_tree(self):
 
66
        self.requireFeature(features.UnicodeFilenameFeature)
 
67
        self.build_tree([u'\u1234', u'\u1234\u1234', u'\u1235/',
 
68
                         u'\u1235/\u1235'])
 
69
 
79
70
    def test_tree_ascii(self):
80
71
        """Checks the glob expansion and path separation char
81
72
        normalization"""
82
 
        self.build_tree(['a', 'a1', 'a2', 'a11', 'a.1',
83
 
                         'b', 'b1', 'b2', 'b3',
84
 
                         'c/', 'c/c1', 'c/c2', 
85
 
                         'd/', 'd/d1', 'd/d2', 'd/e/', 'd/e/e1'])
 
73
        self.build_ascii_tree()
86
74
        self._run_testset([
87
75
            # no wildcards
88
76
            [[u'a'], [u'a']],
89
77
            [[u'a', u'a' ], [u'a', u'a']],
90
 
            [[u'A'], [u'A']],
91
 
                
 
78
 
92
79
            [[u'd'], [u'd']],
93
80
            [[u'd/'], [u'd/']],
94
 
            [[u'd\\'], [u'd/']],
95
 
            
 
81
 
96
82
            # wildcards
97
83
            [[u'a*'], [u'a', u'a1', u'a2', u'a11', u'a.1']],
98
84
            [[u'?'], [u'a', u'b', u'c', u'd']],
99
85
            [[u'a?'], [u'a1', u'a2']],
100
86
            [[u'a??'], [u'a11', u'a.1']],
101
87
            [[u'b[1-2]'], [u'b1', u'b2']],
102
 
            [[u'A?'], [u'a1', u'a2']],
103
 
               
 
88
 
104
89
            [[u'd/*'], [u'd/d1', u'd/d2', u'd/e']],
 
90
            [[u'?/*'], [u'c/c1', u'c/c2', u'd/d1', u'd/d2', u'd/e']],
 
91
            [[u'*/*'], [u'c/c1', u'c/c2', u'd/d1', u'd/d2', u'd/e']],
 
92
            [[u'*/'], [u'c/', u'd/']],
 
93
            ])
 
94
 
 
95
    def test_backslash_globbing(self):
 
96
        self.requireFeature(backslashdir_feature)
 
97
        self.build_ascii_tree()
 
98
        self._run_testset([
 
99
            [[u'd\\'], [u'd/']],
105
100
            [[u'd\\*'], [u'd/d1', u'd/d2', u'd/e']],
106
101
            [[u'?\\*'], [u'c/c1', u'c/c2', u'd/d1', u'd/d2', u'd/e']],
107
102
            [[u'*\\*'], [u'c/c1', u'c/c2', u'd/d1', u'd/d2', u'd/e']],
108
 
            [[u'*/'], [u'c/', u'd/']],
109
 
            [[u'*\\'], [u'c/', u'd/']]])
110
 
        
 
103
            [[u'*\\'], [u'c/', u'd/']],
 
104
            ])
 
105
 
 
106
    def test_case_insensitive_globbing(self):
 
107
        if os.path.normcase("AbC") == "AbC":
 
108
            self.skip("Test requires case insensitive globbing function")
 
109
        self.build_ascii_tree()
 
110
        self._run_testset([
 
111
            [[u'A'], [u'A']],
 
112
            [[u'A?'], [u'a1', u'a2']],
 
113
            ])
 
114
 
111
115
    def test_tree_unicode(self):
112
116
        """Checks behaviour with non-ascii filenames"""
113
 
        self.build_tree([u'\u1234', u'\u1234\u1234', u'\u1235/', u'\u1235/\u1235'])
 
117
        self.build_unicode_tree()
114
118
        self._run_testset([
115
119
            # no wildcards
116
120
            [[u'\u1234'], [u'\u1234']],
117
121
            [[u'\u1235'], [u'\u1235']],
118
 
         
 
122
 
119
123
            [[u'\u1235/'], [u'\u1235/']],
120
124
            [[u'\u1235/\u1235'], [u'\u1235/\u1235']],
121
 
            
 
125
 
122
126
            # wildcards
123
127
            [[u'?'], [u'\u1234', u'\u1235']],
124
128
            [[u'*'], [u'\u1234', u'\u1234\u1234', u'\u1235']],
125
129
            [[u'\u1234*'], [u'\u1234', u'\u1234\u1234']],
126
 
            
 
130
 
127
131
            [[u'\u1235/?'], [u'\u1235/\u1235']],
128
132
            [[u'\u1235/*'], [u'\u1235/\u1235']],
 
133
            [[u'?/'], [u'\u1235/']],
 
134
            [[u'*/'], [u'\u1235/']],
 
135
            [[u'?/?'], [u'\u1235/\u1235']],
 
136
            [[u'*/*'], [u'\u1235/\u1235']],
 
137
            ])
 
138
 
 
139
    def test_unicode_backslashes(self):
 
140
        self.requireFeature(backslashdir_feature)
 
141
        self.build_unicode_tree()
 
142
        self._run_testset([
 
143
            # no wildcards
 
144
            [[u'\u1235\\'], [u'\u1235/']],
 
145
            [[u'\u1235\\\u1235'], [u'\u1235/\u1235']],
129
146
            [[u'\u1235\\?'], [u'\u1235/\u1235']],
130
147
            [[u'\u1235\\*'], [u'\u1235/\u1235']],
131
 
            [[u'?/'], [u'\u1235/']],
132
 
            [[u'*/'], [u'\u1235/']],
133
148
            [[u'?\\'], [u'\u1235/']],
134
149
            [[u'*\\'], [u'\u1235/']],
135
 
            [[u'?/?'], [u'\u1235/\u1235']],
136
 
            [[u'*/*'], [u'\u1235/\u1235']],
137
150
            [[u'?\\?'], [u'\u1235/\u1235']],
138
 
            [[u'*\\*'], [u'\u1235/\u1235']]])
 
151
            [[u'*\\*'], [u'\u1235/\u1235']],
 
152
            ])
139
153
 
140
154
    def _run_testset(self, testset):
141
155
        for pattern, expected in testset:
157
171
            self.assertEquals('iexplore.exe', b.lower())
158
172
            self.assertNotEquals('', d)
159
173
 
 
174
    def test_wordpad(self):
 
175
        # typical windows users should have wordpad in the system
 
176
        # but there is problem: its path has the format REG_EXPAND_SZ
 
177
        # so naive attempt to get the path is not working
 
178
        self.requireFeature(Win32ApiFeature)
 
179
        for a in ('wordpad', 'wordpad.exe'):
 
180
            p = get_app_path(a)
 
181
            d, b = os.path.split(p)
 
182
            self.assertEquals('wordpad.exe', b.lower())
 
183
            self.assertNotEquals('', d)
 
184
 
160
185
    def test_not_existing(self):
161
186
        p = get_app_path('not-existing')
162
187
        self.assertEquals('not-existing', p)
 
188
 
 
189
 
 
190
class TestLocationsCtypes(TestCase):
 
191
 
 
192
    _test_needs_features = [CtypesFeature]
 
193
 
 
194
    def assertPathsEqual(self, p1, p2):
 
195
        # TODO: The env var values in particular might return the "short"
 
196
        # version (ie, "C:\DOCUME~1\...").  Its even possible the returned
 
197
        # values will differ only by case - handle these situations as we
 
198
        # come across them.
 
199
        self.assertEquals(p1, p2)
 
200
 
 
201
    def test_appdata_not_using_environment(self):
 
202
        # Test that we aren't falling back to the environment
 
203
        first = win32utils.get_appdata_location()
 
204
        self.overrideEnv("APPDATA", None)
 
205
        self.assertPathsEqual(first, win32utils.get_appdata_location())
 
206
 
 
207
    def test_appdata_matches_environment(self):
 
208
        # Typically the APPDATA environment variable will match
 
209
        # get_appdata_location
 
210
        # XXX - See bug 262874, which asserts the correct encoding is 'mbcs',
 
211
        encoding = osutils.get_user_encoding()
 
212
        env_val = os.environ.get("APPDATA", None)
 
213
        if not env_val:
 
214
            raise TestSkipped("No APPDATA environment variable exists")
 
215
        self.assertPathsEqual(win32utils.get_appdata_location(),
 
216
                              env_val.decode(encoding))
 
217
 
 
218
    def test_local_appdata_not_using_environment(self):
 
219
        # Test that we aren't falling back to the environment
 
220
        first = win32utils.get_local_appdata_location()
 
221
        self.overrideEnv("LOCALAPPDATA", None)
 
222
        self.assertPathsEqual(first, win32utils.get_local_appdata_location())
 
223
 
 
224
    def test_local_appdata_matches_environment(self):
 
225
        # LOCALAPPDATA typically only exists on Vista, so we only attempt to
 
226
        # compare when it exists.
 
227
        lad = win32utils.get_local_appdata_location()
 
228
        env = os.environ.get("LOCALAPPDATA")
 
229
        if env:
 
230
            # XXX - See bug 262874, which asserts the correct encoding is 'mbcs'
 
231
            encoding = osutils.get_user_encoding()
 
232
            self.assertPathsEqual(lad, env.decode(encoding))
 
233
 
 
234
 
 
235
class TestLocationsPywin32(TestLocationsCtypes):
 
236
 
 
237
    _test_needs_features = [Win32comShellFeature]
 
238
 
 
239
    def setUp(self):
 
240
        super(TestLocationsPywin32, self).setUp()
 
241
        # We perform the exact same tests after disabling the use of ctypes.
 
242
        # This causes the implementation to fall back to pywin32.
 
243
        self.overrideAttr(win32utils, 'has_ctypes', False)
 
244
        # FIXME: this should be done by parametrization -- vila 100123
 
245
 
 
246
 
 
247
class TestSetHidden(TestCaseInTempDir):
 
248
 
 
249
    def test_unicode_dir(self):
 
250
        # we should handle unicode paths without errors
 
251
        self.requireFeature(features.UnicodeFilenameFeature)
 
252
        os.mkdir(u'\u1234')
 
253
        win32utils.set_file_attr_hidden(u'\u1234')
 
254
 
 
255
    def test_dot_bzr_in_unicode_dir(self):
 
256
        # we should not raise traceback if we try to set hidden attribute
 
257
        # on .bzr directory below unicode path
 
258
        self.requireFeature(features.UnicodeFilenameFeature)
 
259
        os.makedirs(u'\u1234\\.bzr')
 
260
        path = osutils.abspath(u'\u1234\\.bzr')
 
261
        win32utils.set_file_attr_hidden(path)
 
262
 
 
263
 
 
264
class Test_CommandLineToArgv(tests.TestCaseInTempDir):
 
265
 
 
266
    def assertCommandLine(self, expected, line, argv=None,
 
267
            single_quotes_allowed=False):
 
268
        # Strictly speaking we should respect parameter order versus glob
 
269
        # expansions, but it's not really worth the effort here
 
270
        if argv is None:
 
271
            argv = [line]
 
272
        argv = win32utils._command_line_to_argv(line, argv,
 
273
                single_quotes_allowed=single_quotes_allowed)
 
274
        self.assertEqual(expected, sorted(argv))
 
275
 
 
276
    def test_glob_paths(self):
 
277
        self.build_tree(['a/', 'a/b.c', 'a/c.c', 'a/c.h'])
 
278
        self.assertCommandLine([u'a/b.c', u'a/c.c'], 'a/*.c')
 
279
        self.build_tree(['b/', 'b/b.c', 'b/d.c', 'b/d.h'])
 
280
        self.assertCommandLine([u'a/b.c', u'b/b.c'], '*/b.c')
 
281
        self.assertCommandLine([u'a/b.c', u'a/c.c', u'b/b.c', u'b/d.c'],
 
282
                               '*/*.c')
 
283
        # Bash style, just pass through the argument if nothing matches
 
284
        self.assertCommandLine([u'*/*.qqq'], '*/*.qqq')
 
285
 
 
286
    def test_quoted_globs(self):
 
287
        self.build_tree(['a/', 'a/b.c', 'a/c.c', 'a/c.h'])
 
288
        self.assertCommandLine([u'a/*.c'], '"a/*.c"')
 
289
        self.assertCommandLine([u"'a/*.c'"], "'a/*.c'")
 
290
        self.assertCommandLine([u'a/*.c'], "'a/*.c'",
 
291
            single_quotes_allowed=True)
 
292
 
 
293
    def test_slashes_changed(self):
 
294
        # Quoting doesn't change the supplied args
 
295
        self.assertCommandLine([u'a\\*.c'], '"a\\*.c"')
 
296
        self.assertCommandLine([u'a\\*.c'], "'a\\*.c'",
 
297
            single_quotes_allowed=True)
 
298
        # Expands the glob, but nothing matches, swaps slashes
 
299
        self.assertCommandLine([u'a/*.c'], 'a\\*.c')
 
300
        self.assertCommandLine([u'a/?.c'], 'a\\?.c')
 
301
        # No glob, doesn't touch slashes
 
302
        self.assertCommandLine([u'a\\foo.c'], 'a\\foo.c')
 
303
 
 
304
    def test_single_quote_support(self):
 
305
        self.assertCommandLine(["add", "let's-do-it.txt"],
 
306
            "add let's-do-it.txt",
 
307
            ["add", "let's-do-it.txt"])
 
308
        self.expectFailure("Using single quotes breaks trimming from argv",
 
309
            self.assertCommandLine, ["add", "lets do it.txt"],
 
310
            "add 'lets do it.txt'", ["add", "'lets", "do", "it.txt'"],
 
311
            single_quotes_allowed=True)
 
312
 
 
313
    def test_case_insensitive_globs(self):
 
314
        if os.path.normcase("AbC") == "AbC":
 
315
            self.skip("Test requires case insensitive globbing function")
 
316
        self.build_tree(['a/', 'a/b.c', 'a/c.c', 'a/c.h'])
 
317
        self.assertCommandLine([u'A/b.c'], 'A/B*')
 
318
 
 
319
    def test_backslashes(self):
 
320
        self.requireFeature(backslashdir_feature)
 
321
        self.build_tree(['a/', 'a/b.c', 'a/c.c', 'a/c.h'])
 
322
        self.assertCommandLine([u'a/b.c'], 'a\\b*')
 
323
 
 
324
    def test_with_pdb(self):
 
325
        """Check stripping Python arguments before bzr script per lp:587868"""
 
326
        self.assertCommandLine([u"rocks"], "-m pdb rocks", ["rocks"])
 
327
        self.build_tree(['d/', 'd/f1', 'd/f2'])
 
328
        self.assertCommandLine([u"rm", u"x*"], "-m pdb rm x*", ["rm", u"x*"])
 
329
        self.assertCommandLine([u"add", u"d/f1", u"d/f2"], "-m pdb add d/*",
 
330
            ["add", u"d/*"])