~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: 2008-07-09 21:42:24 UTC
  • mto: This revision was merged to the branch mainline in revision 3543.
  • Revision ID: john@arbash-meinel.com-20080709214224-r75k87r6a01pfc3h
Restore a real weave merge to 'bzr merge --weave'.

To do so efficiently, we only add the simple LCAs to the final weave
object, unless we run into complexities with the merge graph.
This gives the same effective result as adding all the texts,
with the advantage of not having to extract all of them.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2007-2011, 2016 Canonical Ltd
 
1
# Copyright (C) 2007 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
 
 
17
 
"""Tests for win32utils."""
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
16
 
19
17
import os
 
18
import sys
20
19
 
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
 
20
from bzrlib import osutils
 
21
from bzrlib.tests import TestCase, TestCaseInTempDir, Feature
33
22
from bzrlib.win32utils import glob_expand, get_app_path
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') 
 
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()
43
52
 
44
53
 
45
54
# Tests
46
55
# -----
47
56
 
 
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
 
48
67
class TestWin32UtilsGlobExpand(TestCaseInTempDir):
49
68
 
50
 
    _test_needs_features = []
51
 
 
 
69
    _test_needs_features = [NeedsGlobExpansionFeature]
 
70
   
52
71
    def test_empty_tree(self):
53
72
        self.build_tree([])
54
73
        self._run_testset([
56
75
            [['?'], ['?']],
57
76
            [['*'], ['*']],
58
77
            [['a', 'a'], ['a', 'a']]])
59
 
 
60
 
    def build_ascii_tree(self):
 
78
        
 
79
    def test_tree_ascii(self):
 
80
        """Checks the glob expansion and path separation char
 
81
        normalization"""
61
82
        self.build_tree(['a', 'a1', 'a2', 'a11', 'a.1',
62
83
                         'b', 'b1', 'b2', 'b3',
63
 
                         'c/', 'c/c1', 'c/c2',
 
84
                         'c/', 'c/c1', 'c/c2', 
64
85
                         '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
 
 
71
 
    def test_tree_ascii(self):
72
 
        """Checks the glob expansion and path separation char
73
 
        normalization"""
74
 
        self.build_ascii_tree()
75
86
        self._run_testset([
76
87
            # no wildcards
77
88
            [[u'a'], [u'a']],
78
89
            [[u'a', u'a' ], [u'a', u'a']],
79
 
 
 
90
            [[u'A'], [u'A']],
 
91
                
80
92
            [[u'd'], [u'd']],
81
93
            [[u'd/'], [u'd/']],
82
 
 
 
94
            [[u'd\\'], [u'd/']],
 
95
            
83
96
            # wildcards
84
97
            [[u'a*'], [u'a', u'a1', u'a2', u'a11', u'a.1']],
85
98
            [[u'?'], [u'a', u'b', u'c', u'd']],
86
99
            [[u'a?'], [u'a1', u'a2']],
87
100
            [[u'a??'], [u'a11', u'a.1']],
88
101
            [[u'b[1-2]'], [u'b1', u'b2']],
89
 
 
 
102
            [[u'A?'], [u'a1', u'a2']],
 
103
               
90
104
            [[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/']],
101
105
            [[u'd\\*'], [u'd/d1', u'd/d2', u'd/e']],
102
106
            [[u'?\\*'], [u'c/c1', u'c/c2', u'd/d1', u'd/d2', u'd/e']],
103
107
            [[u'*\\*'], [u'c/c1', u'c/c2', u'd/d1', u'd/d2', u'd/e']],
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
 
 
 
108
            [[u'*/'], [u'c/', u'd/']],
 
109
            [[u'*\\'], [u'c/', u'd/']]])
 
110
        
116
111
    def test_tree_unicode(self):
117
112
        """Checks behaviour with non-ascii filenames"""
118
 
        self.build_unicode_tree()
 
113
        self.build_tree([u'\u1234', u'\u1234\u1234', u'\u1235/', u'\u1235/\u1235'])
119
114
        self._run_testset([
120
115
            # no wildcards
121
116
            [[u'\u1234'], [u'\u1234']],
122
117
            [[u'\u1235'], [u'\u1235']],
123
 
 
 
118
         
124
119
            [[u'\u1235/'], [u'\u1235/']],
125
120
            [[u'\u1235/\u1235'], [u'\u1235/\u1235']],
126
 
 
 
121
            
127
122
            # wildcards
128
123
            [[u'?'], [u'\u1234', u'\u1235']],
129
124
            [[u'*'], [u'\u1234', u'\u1234\u1234', u'\u1235']],
130
125
            [[u'\u1234*'], [u'\u1234', u'\u1234\u1234']],
131
 
 
 
126
            
132
127
            [[u'\u1235/?'], [u'\u1235/\u1235']],
133
128
            [[u'\u1235/*'], [u'\u1235/\u1235']],
 
129
            [[u'\u1235\\?'], [u'\u1235/\u1235']],
 
130
            [[u'\u1235\\*'], [u'\u1235/\u1235']],
134
131
            [[u'?/'], [u'\u1235/']],
135
132
            [[u'*/'], [u'\u1235/']],
 
133
            [[u'?\\'], [u'\u1235/']],
 
134
            [[u'*\\'], [u'\u1235/']],
136
135
            [[u'?/?'], [u'\u1235/\u1235']],
137
136
            [[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']],
147
 
            [[u'\u1235\\?'], [u'\u1235/\u1235']],
148
 
            [[u'\u1235\\*'], [u'\u1235/\u1235']],
149
 
            [[u'?\\'], [u'\u1235/']],
150
 
            [[u'*\\'], [u'\u1235/']],
151
137
            [[u'?\\?'], [u'\u1235/\u1235']],
152
 
            [[u'*\\*'], [u'\u1235/\u1235']],
153
 
            ])
 
138
            [[u'*\\*'], [u'\u1235/\u1235']]])
154
139
 
155
140
    def _run_testset(self, testset):
156
141
        for pattern, expected in testset:
169
154
        for a in ('iexplore', 'iexplore.exe'):
170
155
            p = get_app_path(a)
171
156
            d, b = os.path.split(p)
172
 
            self.assertEqual('iexplore.exe', b.lower())
173
 
            self.assertNotEqual('', d)
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.assertEqual('wordpad.exe', b.lower())
184
 
            self.assertNotEqual('', d)
 
157
            self.assertEquals('iexplore.exe', b.lower())
 
158
            self.assertNotEquals('', d)
185
159
 
186
160
    def test_not_existing(self):
187
161
        p = get_app_path('not-existing')
188
 
        self.assertEqual('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.assertEqual(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)
 
162
        self.assertEquals('not-existing', p)