~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_win32utils.py

  • Committer: Jelmer Vernooij
  • Date: 2012-01-27 19:05:43 UTC
  • mto: This revision was merged to the branch mainline in revision 6450.
  • Revision ID: jelmer@samba.org-20120127190543-vk350mv4a0c7aug2
Fix weave test.

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
    symbol_versioning,
 
24
    tests,
 
25
    win32utils,
 
26
    )
 
27
from bzrlib.tests import (
 
28
    TestCase,
 
29
    TestCaseInTempDir,
 
30
    TestSkipped,
 
31
    )
 
32
from bzrlib.tests.features import backslashdir_feature
22
33
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()
 
34
from bzrlib.tests import (
 
35
    features,
 
36
    )
 
37
 
 
38
 
 
39
Win32RegistryFeature = features.ModuleAvailableFeature('_winreg')
 
40
CtypesFeature = features.ModuleAvailableFeature('ctypes')
 
41
Win32comShellFeature = features.ModuleAvailableFeature('win32com.shell')
 
42
Win32ApiFeature = features.ModuleAvailableFeature('win32api') 
52
43
 
53
44
 
54
45
# Tests
55
46
# -----
56
47
 
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
48
class TestWin32UtilsGlobExpand(TestCaseInTempDir):
68
49
 
69
 
    _test_needs_features = [NeedsGlobExpansionFeature]
70
 
   
 
50
    _test_needs_features = []
 
51
 
71
52
    def test_empty_tree(self):
72
53
        self.build_tree([])
73
54
        self._run_testset([
75
56
            [['?'], ['?']],
76
57
            [['*'], ['*']],
77
58
            [['a', 'a'], ['a', 'a']]])
78
 
        
 
59
 
 
60
    def build_ascii_tree(self):
 
61
        self.build_tree(['a', 'a1', 'a2', 'a11', 'a.1',
 
62
                         'b', 'b1', 'b2', 'b3',
 
63
                         'c/', 'c/c1', 'c/c2',
 
64
                         'd/', 'd/d1', 'd/d2', 'd/e/', 'd/e/e1'])
 
65
 
 
66
    def build_unicode_tree(self):
 
67
        self.requireFeature(features.UnicodeFilenameFeature)
 
68
        self.build_tree([u'\u1234', u'\u1234\u1234', u'\u1235/',
 
69
                         u'\u1235/\u1235'])
 
70
 
79
71
    def test_tree_ascii(self):
80
72
        """Checks the glob expansion and path separation char
81
73
        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'])
 
74
        self.build_ascii_tree()
86
75
        self._run_testset([
87
76
            # no wildcards
88
77
            [[u'a'], [u'a']],
89
78
            [[u'a', u'a' ], [u'a', u'a']],
90
 
            [[u'A'], [u'A']],
91
 
                
 
79
 
92
80
            [[u'd'], [u'd']],
93
81
            [[u'd/'], [u'd/']],
94
 
            [[u'd\\'], [u'd/']],
95
 
            
 
82
 
96
83
            # wildcards
97
84
            [[u'a*'], [u'a', u'a1', u'a2', u'a11', u'a.1']],
98
85
            [[u'?'], [u'a', u'b', u'c', u'd']],
99
86
            [[u'a?'], [u'a1', u'a2']],
100
87
            [[u'a??'], [u'a11', u'a.1']],
101
88
            [[u'b[1-2]'], [u'b1', u'b2']],
102
 
            [[u'A?'], [u'a1', u'a2']],
103
 
               
 
89
 
104
90
            [[u'd/*'], [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/c1', u'c/c2', u'd/d1', u'd/d2', u'd/e']],
 
93
            [[u'*/'], [u'c/', u'd/']],
 
94
            ])
 
95
 
 
96
    def test_backslash_globbing(self):
 
97
        self.requireFeature(backslashdir_feature)
 
98
        self.build_ascii_tree()
 
99
        self._run_testset([
 
100
            [[u'd\\'], [u'd/']],
105
101
            [[u'd\\*'], [u'd/d1', u'd/d2', u'd/e']],
106
102
            [[u'?\\*'], [u'c/c1', u'c/c2', u'd/d1', u'd/d2', u'd/e']],
107
103
            [[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
 
        
 
104
            [[u'*\\'], [u'c/', u'd/']],
 
105
            ])
 
106
 
 
107
    def test_case_insensitive_globbing(self):
 
108
        if os.path.normcase("AbC") == "AbC":
 
109
            self.skip("Test requires case insensitive globbing function")
 
110
        self.build_ascii_tree()
 
111
        self._run_testset([
 
112
            [[u'A'], [u'A']],
 
113
            [[u'A?'], [u'a1', u'a2']],
 
114
            ])
 
115
 
111
116
    def test_tree_unicode(self):
112
117
        """Checks behaviour with non-ascii filenames"""
113
 
        self.build_tree([u'\u1234', u'\u1234\u1234', u'\u1235/', u'\u1235/\u1235'])
 
118
        self.build_unicode_tree()
114
119
        self._run_testset([
115
120
            # no wildcards
116
121
            [[u'\u1234'], [u'\u1234']],
117
122
            [[u'\u1235'], [u'\u1235']],
118
 
         
 
123
 
119
124
            [[u'\u1235/'], [u'\u1235/']],
120
125
            [[u'\u1235/\u1235'], [u'\u1235/\u1235']],
121
 
            
 
126
 
122
127
            # wildcards
123
128
            [[u'?'], [u'\u1234', u'\u1235']],
124
129
            [[u'*'], [u'\u1234', u'\u1234\u1234', u'\u1235']],
125
130
            [[u'\u1234*'], [u'\u1234', u'\u1234\u1234']],
126
 
            
 
131
 
127
132
            [[u'\u1235/?'], [u'\u1235/\u1235']],
128
133
            [[u'\u1235/*'], [u'\u1235/\u1235']],
 
134
            [[u'?/'], [u'\u1235/']],
 
135
            [[u'*/'], [u'\u1235/']],
 
136
            [[u'?/?'], [u'\u1235/\u1235']],
 
137
            [[u'*/*'], [u'\u1235/\u1235']],
 
138
            ])
 
139
 
 
140
    def test_unicode_backslashes(self):
 
141
        self.requireFeature(backslashdir_feature)
 
142
        self.build_unicode_tree()
 
143
        self._run_testset([
 
144
            # no wildcards
 
145
            [[u'\u1235\\'], [u'\u1235/']],
 
146
            [[u'\u1235\\\u1235'], [u'\u1235/\u1235']],
129
147
            [[u'\u1235\\?'], [u'\u1235/\u1235']],
130
148
            [[u'\u1235\\*'], [u'\u1235/\u1235']],
131
 
            [[u'?/'], [u'\u1235/']],
132
 
            [[u'*/'], [u'\u1235/']],
133
149
            [[u'?\\'], [u'\u1235/']],
134
150
            [[u'*\\'], [u'\u1235/']],
135
 
            [[u'?/?'], [u'\u1235/\u1235']],
136
 
            [[u'*/*'], [u'\u1235/\u1235']],
137
151
            [[u'?\\?'], [u'\u1235/\u1235']],
138
 
            [[u'*\\*'], [u'\u1235/\u1235']]])
 
152
            [[u'*\\*'], [u'\u1235/\u1235']],
 
153
            ])
139
154
 
140
155
    def _run_testset(self, testset):
141
156
        for pattern, expected in testset:
157
172
            self.assertEquals('iexplore.exe', b.lower())
158
173
            self.assertNotEquals('', d)
159
174
 
 
175
    def test_wordpad(self):
 
176
        # typical windows users should have wordpad in the system
 
177
        # but there is problem: its path has the format REG_EXPAND_SZ
 
178
        # so naive attempt to get the path is not working
 
179
        self.requireFeature(Win32ApiFeature)
 
180
        for a in ('wordpad', 'wordpad.exe'):
 
181
            p = get_app_path(a)
 
182
            d, b = os.path.split(p)
 
183
            self.assertEquals('wordpad.exe', b.lower())
 
184
            self.assertNotEquals('', d)
 
185
 
160
186
    def test_not_existing(self):
161
187
        p = get_app_path('not-existing')
162
188
        self.assertEquals('not-existing', p)
 
189
 
 
190
 
 
191
class TestLocations(TestCase):
 
192
    """Tests for windows specific path and name retrieving functions"""
 
193
 
 
194
    def test__ensure_unicode_deprecated(self):
 
195
        s = "text"
 
196
        u1 = self.applyDeprecated(symbol_versioning.deprecated_in((2, 5, 0)),
 
197
            win32utils._ensure_unicode, s)
 
198
        self.assertEqual(s, u1)
 
199
        self.assertIsInstance(u1, unicode)
 
200
        u2 = self.applyDeprecated(symbol_versioning.deprecated_in((2, 5, 0)),
 
201
            win32utils._ensure_unicode, u1)
 
202
        self.assertIs(u1, u2)
 
203
    
 
204
    def test_appdata_unicode_deprecated(self):
 
205
        self.overrideEnv("APPDATA", "fakepath")
 
206
        s = win32utils.get_appdata_location()
 
207
        u = self.applyDeprecated(symbol_versioning.deprecated_in((2, 5, 0)),
 
208
            win32utils.get_appdata_location_unicode)
 
209
        self.assertEqual(s, u)
 
210
        self.assertIsInstance(s, unicode)
 
211
 
 
212
    def test_home_unicode_deprecated(self):
 
213
        s = win32utils.get_home_location()
 
214
        u = self.applyDeprecated(symbol_versioning.deprecated_in((2, 5, 0)),
 
215
            win32utils.get_home_location_unicode)
 
216
        self.assertEqual(s, u)
 
217
        self.assertIsInstance(s, unicode)
 
218
 
 
219
    def test_user_unicode_deprecated(self):
 
220
        self.overrideEnv("USERNAME", "alien")
 
221
        s = win32utils.get_user_name()
 
222
        u = self.applyDeprecated(symbol_versioning.deprecated_in((2, 5, 0)),
 
223
            win32utils.get_user_name_unicode)
 
224
        self.assertEqual(s, u)
 
225
        self.assertIsInstance(s, unicode)
 
226
 
 
227
    def test_host_unicode_deprecated(self):
 
228
        self.overrideEnv("COMPUTERNAME", "alienbox")
 
229
        s = win32utils.get_host_name()
 
230
        u = self.applyDeprecated(symbol_versioning.deprecated_in((2, 5, 0)),
 
231
            win32utils.get_host_name_unicode)
 
232
        self.assertEqual(s, u)
 
233
        self.assertIsInstance(s, unicode)
 
234
 
 
235
 
 
236
class TestLocationsCtypes(TestCase):
 
237
 
 
238
    _test_needs_features = [CtypesFeature]
 
239
 
 
240
    def assertPathsEqual(self, p1, p2):
 
241
        # TODO: The env var values in particular might return the "short"
 
242
        # version (ie, "C:\DOCUME~1\...").  Its even possible the returned
 
243
        # values will differ only by case - handle these situations as we
 
244
        # come across them.
 
245
        self.assertEquals(p1, p2)
 
246
 
 
247
    def test_appdata_not_using_environment(self):
 
248
        # Test that we aren't falling back to the environment
 
249
        first = win32utils.get_appdata_location()
 
250
        self.overrideEnv("APPDATA", None)
 
251
        self.assertPathsEqual(first, win32utils.get_appdata_location())
 
252
 
 
253
    def test_appdata_matches_environment(self):
 
254
        # Typically the APPDATA environment variable will match
 
255
        # get_appdata_location
 
256
        # XXX - See bug 262874, which asserts the correct encoding is 'mbcs',
 
257
        encoding = osutils.get_user_encoding()
 
258
        env_val = os.environ.get("APPDATA", None)
 
259
        if not env_val:
 
260
            raise TestSkipped("No APPDATA environment variable exists")
 
261
        self.assertPathsEqual(win32utils.get_appdata_location(),
 
262
                              env_val.decode(encoding))
 
263
 
 
264
    def test_local_appdata_not_using_environment(self):
 
265
        # Test that we aren't falling back to the environment
 
266
        first = win32utils.get_local_appdata_location()
 
267
        self.overrideEnv("LOCALAPPDATA", None)
 
268
        self.assertPathsEqual(first, win32utils.get_local_appdata_location())
 
269
 
 
270
    def test_local_appdata_matches_environment(self):
 
271
        # LOCALAPPDATA typically only exists on Vista, so we only attempt to
 
272
        # compare when it exists.
 
273
        lad = win32utils.get_local_appdata_location()
 
274
        env = os.environ.get("LOCALAPPDATA")
 
275
        if env:
 
276
            # XXX - See bug 262874, which asserts the correct encoding is 'mbcs'
 
277
            encoding = osutils.get_user_encoding()
 
278
            self.assertPathsEqual(lad, env.decode(encoding))
 
279
 
 
280
 
 
281
class TestLocationsPywin32(TestLocationsCtypes):
 
282
 
 
283
    _test_needs_features = [Win32comShellFeature]
 
284
 
 
285
    def setUp(self):
 
286
        super(TestLocationsPywin32, self).setUp()
 
287
        # We perform the exact same tests after disabling the use of ctypes.
 
288
        # This causes the implementation to fall back to pywin32.
 
289
        self.overrideAttr(win32utils, 'has_ctypes', False)
 
290
        # FIXME: this should be done by parametrization -- vila 100123
 
291
 
 
292
 
 
293
class TestSetHidden(TestCaseInTempDir):
 
294
 
 
295
    def test_unicode_dir(self):
 
296
        # we should handle unicode paths without errors
 
297
        self.requireFeature(features.UnicodeFilenameFeature)
 
298
        os.mkdir(u'\u1234')
 
299
        win32utils.set_file_attr_hidden(u'\u1234')
 
300
 
 
301
    def test_dot_bzr_in_unicode_dir(self):
 
302
        # we should not raise traceback if we try to set hidden attribute
 
303
        # on .bzr directory below unicode path
 
304
        self.requireFeature(features.UnicodeFilenameFeature)
 
305
        os.makedirs(u'\u1234\\.bzr')
 
306
        path = osutils.abspath(u'\u1234\\.bzr')
 
307
        win32utils.set_file_attr_hidden(path)
 
308
 
 
309
 
 
310
class Test_CommandLineToArgv(tests.TestCaseInTempDir):
 
311
 
 
312
    def assertCommandLine(self, expected, line, argv=None,
 
313
            single_quotes_allowed=False):
 
314
        # Strictly speaking we should respect parameter order versus glob
 
315
        # expansions, but it's not really worth the effort here
 
316
        if argv is None:
 
317
            argv = [line]
 
318
        argv = win32utils._command_line_to_argv(line, argv,
 
319
                single_quotes_allowed=single_quotes_allowed)
 
320
        self.assertEqual(expected, sorted(argv))
 
321
 
 
322
    def test_glob_paths(self):
 
323
        self.build_tree(['a/', 'a/b.c', 'a/c.c', 'a/c.h'])
 
324
        self.assertCommandLine([u'a/b.c', u'a/c.c'], 'a/*.c')
 
325
        self.build_tree(['b/', 'b/b.c', 'b/d.c', 'b/d.h'])
 
326
        self.assertCommandLine([u'a/b.c', u'b/b.c'], '*/b.c')
 
327
        self.assertCommandLine([u'a/b.c', u'a/c.c', u'b/b.c', u'b/d.c'],
 
328
                               '*/*.c')
 
329
        # Bash style, just pass through the argument if nothing matches
 
330
        self.assertCommandLine([u'*/*.qqq'], '*/*.qqq')
 
331
 
 
332
    def test_quoted_globs(self):
 
333
        self.build_tree(['a/', 'a/b.c', 'a/c.c', 'a/c.h'])
 
334
        self.assertCommandLine([u'a/*.c'], '"a/*.c"')
 
335
        self.assertCommandLine([u"'a/*.c'"], "'a/*.c'")
 
336
        self.assertCommandLine([u'a/*.c'], "'a/*.c'",
 
337
            single_quotes_allowed=True)
 
338
 
 
339
    def test_slashes_changed(self):
 
340
        # Quoting doesn't change the supplied args
 
341
        self.assertCommandLine([u'a\\*.c'], '"a\\*.c"')
 
342
        self.assertCommandLine([u'a\\*.c'], "'a\\*.c'",
 
343
            single_quotes_allowed=True)
 
344
        # Expands the glob, but nothing matches, swaps slashes
 
345
        self.assertCommandLine([u'a/*.c'], 'a\\*.c')
 
346
        self.assertCommandLine([u'a/?.c'], 'a\\?.c')
 
347
        # No glob, doesn't touch slashes
 
348
        self.assertCommandLine([u'a\\foo.c'], 'a\\foo.c')
 
349
 
 
350
    def test_single_quote_support(self):
 
351
        self.assertCommandLine(["add", "let's-do-it.txt"],
 
352
            "add let's-do-it.txt",
 
353
            ["add", "let's-do-it.txt"])
 
354
        self.expectFailure("Using single quotes breaks trimming from argv",
 
355
            self.assertCommandLine, ["add", "lets do it.txt"],
 
356
            "add 'lets do it.txt'", ["add", "'lets", "do", "it.txt'"],
 
357
            single_quotes_allowed=True)
 
358
 
 
359
    def test_case_insensitive_globs(self):
 
360
        if os.path.normcase("AbC") == "AbC":
 
361
            self.skip("Test requires case insensitive globbing function")
 
362
        self.build_tree(['a/', 'a/b.c', 'a/c.c', 'a/c.h'])
 
363
        self.assertCommandLine([u'A/b.c'], 'A/B*')
 
364
 
 
365
    def test_backslashes(self):
 
366
        self.requireFeature(backslashdir_feature)
 
367
        self.build_tree(['a/', 'a/b.c', 'a/c.c', 'a/c.h'])
 
368
        self.assertCommandLine([u'a/b.c'], 'a\\b*')
 
369
 
 
370
    def test_with_pdb(self):
 
371
        """Check stripping Python arguments before bzr script per lp:587868"""
 
372
        self.assertCommandLine([u"rocks"], "-m pdb rocks", ["rocks"])
 
373
        self.build_tree(['d/', 'd/f1', 'd/f2'])
 
374
        self.assertCommandLine([u"rm", u"x*"], "-m pdb rm x*", ["rm", u"x*"])
 
375
        self.assertCommandLine([u"add", u"d/f1", u"d/f2"], "-m pdb add d/*",
 
376
            ["add", u"d/*"])
 
377
 
 
378
 
 
379
class TestGetEnvironUnicode(tests.TestCase):
 
380
    """Tests for accessing the environment via the windows wide api"""
 
381
 
 
382
    _test_needs_features = [CtypesFeature, features.win32_feature]
 
383
 
 
384
    def setUp(self):
 
385
        super(TestGetEnvironUnicode, self).setUp()
 
386
        self.overrideEnv("TEST", "1")
 
387
 
 
388
    def test_get(self):
 
389
        """In the normal case behaves the same as os.environ access"""
 
390
        self.assertEqual("1", win32utils.get_environ_unicode("TEST"))
 
391
 
 
392
    def test_unset(self):
 
393
        """A variable not present in the environment gives None by default"""
 
394
        del os.environ["TEST"]
 
395
        self.assertIs(None, win32utils.get_environ_unicode("TEST"))
 
396
 
 
397
    def test_unset_default(self):
 
398
        """A variable not present in the environment gives passed default"""
 
399
        del os.environ["TEST"]
 
400
        self.assertIs("a", win32utils.get_environ_unicode("TEST", "a"))
 
401
 
 
402
    def test_unicode(self):
 
403
        """A non-ascii variable is returned as unicode"""
 
404
        unicode_val = u"\xa7" # non-ascii character present in many encodings
 
405
        try:
 
406
            bytes_val = unicode_val.encode(osutils.get_user_encoding())
 
407
        except UnicodeEncodeError:
 
408
            self.skip("Couldn't encode non-ascii string to place in environ")
 
409
        os.environ["TEST"] = bytes_val
 
410
        self.assertEqual(unicode_val, win32utils.get_environ_unicode("TEST"))
 
411
 
 
412
    def test_long(self):
 
413
        """A variable bigger than heuristic buffer size is still accessible"""
 
414
        big_val = "x" * (2<<10)
 
415
        os.environ["TEST"] = big_val
 
416
        self.assertEqual(big_val, win32utils.get_environ_unicode("TEST"))
 
417
 
 
418
    def test_unexpected_error(self):
 
419
        """An error from the underlying platform function is propogated"""
 
420
        ERROR_INVALID_PARAMETER = 87
 
421
        SetLastError = win32utils.ctypes.windll.kernel32.SetLastError
 
422
        def failer(*args, **kwargs):
 
423
            SetLastError(ERROR_INVALID_PARAMETER)
 
424
            return 0
 
425
        self.overrideAttr(win32utils.get_environ_unicode, "_c_function",
 
426
            failer)
 
427
        e = self.assertRaises(WindowsError,
 
428
            win32utils.get_environ_unicode, "TEST")
 
429
        self.assertEqual(e.winerror, ERROR_INVALID_PARAMETER)