~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_win32utils.py

(jameinel) Allow 'bzr serve' to interpret SIGHUP as a graceful shutdown.
 (bug #795025) (John A Meinel)

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, TestSkipped, 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
 
from bzrlib import win32utils
24
 
 
25
 
 
26
 
# Features
27
 
# --------
28
 
 
29
 
class _NeedsGlobExpansionFeature(Feature):
30
 
 
31
 
    def _probe(self):
32
 
        return sys.platform == 'win32'
33
 
 
34
 
    def feature_name(self):
35
 
        return 'Internally performed glob expansion'
36
 
 
37
 
NeedsGlobExpansionFeature = _NeedsGlobExpansionFeature()
38
 
 
39
 
 
40
 
class _RequiredModuleFeature(Feature):
41
 
 
42
 
    def __init__(self, mod_name):
43
 
        self.mod_name = mod_name
44
 
        super(_RequiredModuleFeature, self).__init__()
45
 
 
46
 
    def _probe(self):
47
 
        try:
48
 
            __import__(self.mod_name)
49
 
            return True
50
 
        except ImportError:
51
 
            return False
52
 
 
53
 
    def feature_name(self):
54
 
        return self.mod_name
55
 
 
56
 
Win32RegistryFeature = _RequiredModuleFeature('_winreg')
57
 
CtypesFeature = _RequiredModuleFeature('ctypes')
58
 
Win32comShellFeature = _RequiredModuleFeature('win32com.shell')
 
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') 
59
42
 
60
43
 
61
44
# Tests
62
45
# -----
63
46
 
64
 
class TestNeedsGlobExpansionFeature(TestCase):
65
 
    
66
 
    def test_available(self):
67
 
        self.assertEqual(sys.platform == 'win32', 
68
 
                         NeedsGlobExpansionFeature.available())
69
 
        
70
 
    def test_str(self):
71
 
        self.assertTrue("performed" in str(NeedsGlobExpansionFeature))
72
 
 
73
 
 
74
47
class TestWin32UtilsGlobExpand(TestCaseInTempDir):
75
48
 
76
 
    _test_needs_features = [NeedsGlobExpansionFeature]
77
 
   
 
49
    _test_needs_features = []
 
50
 
78
51
    def test_empty_tree(self):
79
52
        self.build_tree([])
80
53
        self._run_testset([
82
55
            [['?'], ['?']],
83
56
            [['*'], ['*']],
84
57
            [['a', 'a'], ['a', 'a']]])
85
 
        
 
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
 
86
70
    def test_tree_ascii(self):
87
71
        """Checks the glob expansion and path separation char
88
72
        normalization"""
89
 
        self.build_tree(['a', 'a1', 'a2', 'a11', 'a.1',
90
 
                         'b', 'b1', 'b2', 'b3',
91
 
                         'c/', 'c/c1', 'c/c2', 
92
 
                         'd/', 'd/d1', 'd/d2', 'd/e/', 'd/e/e1'])
 
73
        self.build_ascii_tree()
93
74
        self._run_testset([
94
75
            # no wildcards
95
76
            [[u'a'], [u'a']],
96
77
            [[u'a', u'a' ], [u'a', u'a']],
97
 
            [[u'A'], [u'A']],
98
 
                
 
78
 
99
79
            [[u'd'], [u'd']],
100
80
            [[u'd/'], [u'd/']],
101
 
            [[u'd\\'], [u'd/']],
102
 
            
 
81
 
103
82
            # wildcards
104
83
            [[u'a*'], [u'a', u'a1', u'a2', u'a11', u'a.1']],
105
84
            [[u'?'], [u'a', u'b', u'c', u'd']],
106
85
            [[u'a?'], [u'a1', u'a2']],
107
86
            [[u'a??'], [u'a11', u'a.1']],
108
87
            [[u'b[1-2]'], [u'b1', u'b2']],
109
 
            [[u'A?'], [u'a1', u'a2']],
110
 
               
 
88
 
111
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/']],
112
100
            [[u'd\\*'], [u'd/d1', u'd/d2', u'd/e']],
113
101
            [[u'?\\*'], [u'c/c1', u'c/c2', u'd/d1', u'd/d2', u'd/e']],
114
102
            [[u'*\\*'], [u'c/c1', u'c/c2', u'd/d1', u'd/d2', u'd/e']],
115
 
            [[u'*/'], [u'c/', u'd/']],
116
 
            [[u'*\\'], [u'c/', u'd/']]])
117
 
        
 
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
 
118
115
    def test_tree_unicode(self):
119
116
        """Checks behaviour with non-ascii filenames"""
120
 
        self.build_tree([u'\u1234', u'\u1234\u1234', u'\u1235/', u'\u1235/\u1235'])
 
117
        self.build_unicode_tree()
121
118
        self._run_testset([
122
119
            # no wildcards
123
120
            [[u'\u1234'], [u'\u1234']],
124
121
            [[u'\u1235'], [u'\u1235']],
125
 
         
 
122
 
126
123
            [[u'\u1235/'], [u'\u1235/']],
127
124
            [[u'\u1235/\u1235'], [u'\u1235/\u1235']],
128
 
            
 
125
 
129
126
            # wildcards
130
127
            [[u'?'], [u'\u1234', u'\u1235']],
131
128
            [[u'*'], [u'\u1234', u'\u1234\u1234', u'\u1235']],
132
129
            [[u'\u1234*'], [u'\u1234', u'\u1234\u1234']],
133
 
            
 
130
 
134
131
            [[u'\u1235/?'], [u'\u1235/\u1235']],
135
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']],
136
146
            [[u'\u1235\\?'], [u'\u1235/\u1235']],
137
147
            [[u'\u1235\\*'], [u'\u1235/\u1235']],
138
 
            [[u'?/'], [u'\u1235/']],
139
 
            [[u'*/'], [u'\u1235/']],
140
148
            [[u'?\\'], [u'\u1235/']],
141
149
            [[u'*\\'], [u'\u1235/']],
142
 
            [[u'?/?'], [u'\u1235/\u1235']],
143
 
            [[u'*/*'], [u'\u1235/\u1235']],
144
150
            [[u'?\\?'], [u'\u1235/\u1235']],
145
 
            [[u'*\\*'], [u'\u1235/\u1235']]])
 
151
            [[u'*\\*'], [u'\u1235/\u1235']],
 
152
            ])
146
153
 
147
154
    def _run_testset(self, testset):
148
155
        for pattern, expected in testset:
164
171
            self.assertEquals('iexplore.exe', b.lower())
165
172
            self.assertNotEquals('', d)
166
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
 
167
185
    def test_not_existing(self):
168
186
        p = get_app_path('not-existing')
169
187
        self.assertEquals('not-existing', p)
183
201
    def test_appdata_not_using_environment(self):
184
202
        # Test that we aren't falling back to the environment
185
203
        first = win32utils.get_appdata_location()
186
 
        self._captureVar("APPDATA", None)
 
204
        self.overrideEnv("APPDATA", None)
187
205
        self.assertPathsEqual(first, win32utils.get_appdata_location())
188
 
 
 
206
 
189
207
    def test_appdata_matches_environment(self):
190
208
        # Typically the APPDATA environment variable will match
191
209
        # get_appdata_location
200
218
    def test_local_appdata_not_using_environment(self):
201
219
        # Test that we aren't falling back to the environment
202
220
        first = win32utils.get_local_appdata_location()
203
 
        self._captureVar("LOCALAPPDATA", None)
 
221
        self.overrideEnv("LOCALAPPDATA", None)
204
222
        self.assertPathsEqual(first, win32utils.get_local_appdata_location())
205
223
 
206
224
    def test_local_appdata_matches_environment(self):
222
240
        super(TestLocationsPywin32, self).setUp()
223
241
        # We perform the exact same tests after disabling the use of ctypes.
224
242
        # This causes the implementation to fall back to pywin32.
225
 
        self.old_ctypes = win32utils.has_ctypes
226
 
        win32utils.has_ctypes = False
227
 
        self.addCleanup(self.restoreCtypes)
228
 
 
229
 
    def restoreCtypes(self):
230
 
        win32utils.has_ctypes = self.old_ctypes
 
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/*"])