~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_win32utils.py

  • Committer: John Arbash Meinel
  • Date: 2010-08-02 17:16:12 UTC
  • mto: This revision was merged to the branch mainline in revision 5369.
  • Revision ID: john@arbash-meinel.com-20100802171612-rdh5ods70w2bl3j7
We also have to re-implement it for _simple_set_pyx.pyx

Show diffs side-by-side

added added

removed removed

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