~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_win32utils.py

  • Committer: Andrew Bennetts
  • Date: 2010-01-12 03:53:21 UTC
  • mfrom: (4948 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4964.
  • Revision ID: andrew.bennetts@canonical.com-20100112035321-hofpz5p10224ryj3
Merge lp:bzr, resolving conflicts.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
16
 
17
17
import os
18
18
import sys
19
19
 
20
 
from bzrlib import osutils
21
 
from bzrlib.tests import TestCase, TestCaseInTempDir, TestSkipped, Feature
 
20
from bzrlib import (
 
21
    osutils,
 
22
    tests,
 
23
    win32utils,
 
24
    )
 
25
from bzrlib.tests import (
 
26
    Feature,
 
27
    TestCase,
 
28
    TestCaseInTempDir,
 
29
    TestSkipped,
 
30
    UnicodeFilenameFeature,
 
31
    )
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):
 
33
 
 
34
 
 
35
class _BackslashDirSeparatorFeature(tests.Feature):
30
36
 
31
37
    def _probe(self):
32
 
        return sys.platform == 'win32'
 
38
        try:
 
39
            os.lstat(os.getcwd() + '\\')
 
40
        except OSError:
 
41
            return False
 
42
        else:
 
43
            return True
33
44
 
34
45
    def feature_name(self):
35
 
        return 'Internally performed glob expansion'
 
46
        return "Filesystem treats '\\' as a directory separator."
36
47
 
37
 
NeedsGlobExpansionFeature = _NeedsGlobExpansionFeature()
 
48
BackslashDirSeparatorFeature = _BackslashDirSeparatorFeature()
38
49
 
39
50
 
40
51
class _RequiredModuleFeature(Feature):
61
72
# Tests
62
73
# -----
63
74
 
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
75
class TestWin32UtilsGlobExpand(TestCaseInTempDir):
75
76
 
76
 
    _test_needs_features = [NeedsGlobExpansionFeature]
77
 
   
 
77
    _test_needs_features = []
 
78
 
78
79
    def test_empty_tree(self):
79
80
        self.build_tree([])
80
81
        self._run_testset([
82
83
            [['?'], ['?']],
83
84
            [['*'], ['*']],
84
85
            [['a', 'a'], ['a', 'a']]])
85
 
        
 
86
 
 
87
    def build_ascii_tree(self):
 
88
        self.build_tree(['a', 'a1', 'a2', 'a11', 'a.1',
 
89
                         'b', 'b1', 'b2', 'b3',
 
90
                         'c/', 'c/c1', 'c/c2',
 
91
                         'd/', 'd/d1', 'd/d2', 'd/e/', 'd/e/e1'])
 
92
 
 
93
    def build_unicode_tree(self):
 
94
        self.requireFeature(UnicodeFilenameFeature)
 
95
        self.build_tree([u'\u1234', u'\u1234\u1234', u'\u1235/',
 
96
                         u'\u1235/\u1235'])
 
97
 
86
98
    def test_tree_ascii(self):
87
99
        """Checks the glob expansion and path separation char
88
100
        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'])
 
101
        self.build_ascii_tree()
93
102
        self._run_testset([
94
103
            # no wildcards
95
104
            [[u'a'], [u'a']],
96
105
            [[u'a', u'a' ], [u'a', u'a']],
97
 
            [[u'A'], [u'A']],
98
 
                
 
106
 
99
107
            [[u'd'], [u'd']],
100
108
            [[u'd/'], [u'd/']],
101
 
            [[u'd\\'], [u'd/']],
102
 
            
 
109
 
103
110
            # wildcards
104
111
            [[u'a*'], [u'a', u'a1', u'a2', u'a11', u'a.1']],
105
112
            [[u'?'], [u'a', u'b', u'c', u'd']],
106
113
            [[u'a?'], [u'a1', u'a2']],
107
114
            [[u'a??'], [u'a11', u'a.1']],
108
115
            [[u'b[1-2]'], [u'b1', u'b2']],
109
 
            [[u'A?'], [u'a1', u'a2']],
110
 
               
 
116
 
111
117
            [[u'd/*'], [u'd/d1', u'd/d2', u'd/e']],
 
118
            [[u'?/*'], [u'c/c1', u'c/c2', u'd/d1', u'd/d2', u'd/e']],
 
119
            [[u'*/*'], [u'c/c1', u'c/c2', u'd/d1', u'd/d2', u'd/e']],
 
120
            [[u'*/'], [u'c/', u'd/']],
 
121
            ])
 
122
 
 
123
    def test_backslash_globbing(self):
 
124
        self.requireFeature(BackslashDirSeparatorFeature)
 
125
        self.build_ascii_tree()
 
126
        self._run_testset([
 
127
            [[u'd\\'], [u'd/']],
112
128
            [[u'd\\*'], [u'd/d1', u'd/d2', u'd/e']],
113
129
            [[u'?\\*'], [u'c/c1', u'c/c2', u'd/d1', u'd/d2', u'd/e']],
114
130
            [[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
 
        
 
131
            [[u'*\\'], [u'c/', u'd/']],
 
132
            ])
 
133
 
 
134
    def test_case_insensitive_globbing(self):
 
135
        self.requireFeature(tests.CaseInsCasePresFilenameFeature)
 
136
        self.build_ascii_tree()
 
137
        self._run_testset([
 
138
            [[u'A'], [u'A']],
 
139
            [[u'A?'], [u'a1', u'a2']],
 
140
            ])
 
141
 
118
142
    def test_tree_unicode(self):
119
143
        """Checks behaviour with non-ascii filenames"""
120
 
        self.build_tree([u'\u1234', u'\u1234\u1234', u'\u1235/', u'\u1235/\u1235'])
 
144
        self.build_unicode_tree()
121
145
        self._run_testset([
122
146
            # no wildcards
123
147
            [[u'\u1234'], [u'\u1234']],
124
148
            [[u'\u1235'], [u'\u1235']],
125
 
         
 
149
 
126
150
            [[u'\u1235/'], [u'\u1235/']],
127
151
            [[u'\u1235/\u1235'], [u'\u1235/\u1235']],
128
 
            
 
152
 
129
153
            # wildcards
130
154
            [[u'?'], [u'\u1234', u'\u1235']],
131
155
            [[u'*'], [u'\u1234', u'\u1234\u1234', u'\u1235']],
132
156
            [[u'\u1234*'], [u'\u1234', u'\u1234\u1234']],
133
 
            
 
157
 
134
158
            [[u'\u1235/?'], [u'\u1235/\u1235']],
135
159
            [[u'\u1235/*'], [u'\u1235/\u1235']],
 
160
            [[u'?/'], [u'\u1235/']],
 
161
            [[u'*/'], [u'\u1235/']],
 
162
            [[u'?/?'], [u'\u1235/\u1235']],
 
163
            [[u'*/*'], [u'\u1235/\u1235']],
 
164
            ])
 
165
 
 
166
    def test_unicode_backslashes(self):
 
167
        self.requireFeature(BackslashDirSeparatorFeature)
 
168
        self.build_unicode_tree()
 
169
        self._run_testset([
 
170
            # no wildcards
 
171
            [[u'\u1235\\'], [u'\u1235/']],
 
172
            [[u'\u1235\\\u1235'], [u'\u1235/\u1235']],
136
173
            [[u'\u1235\\?'], [u'\u1235/\u1235']],
137
174
            [[u'\u1235\\*'], [u'\u1235/\u1235']],
138
 
            [[u'?/'], [u'\u1235/']],
139
 
            [[u'*/'], [u'\u1235/']],
140
175
            [[u'?\\'], [u'\u1235/']],
141
176
            [[u'*\\'], [u'\u1235/']],
142
 
            [[u'?/?'], [u'\u1235/\u1235']],
143
 
            [[u'*/*'], [u'\u1235/\u1235']],
144
177
            [[u'?\\?'], [u'\u1235/\u1235']],
145
 
            [[u'*\\*'], [u'\u1235/\u1235']]])
 
178
            [[u'*\\*'], [u'\u1235/\u1235']],
 
179
            ])
146
180
 
147
181
    def _run_testset(self, testset):
148
182
        for pattern, expected in testset:
164
198
            self.assertEquals('iexplore.exe', b.lower())
165
199
            self.assertNotEquals('', d)
166
200
 
 
201
    def test_wordpad(self):
 
202
        # typical windows users should have wordpad in the system
 
203
        # but there is problem: its path has the format REG_EXPAND_SZ
 
204
        # so naive attempt to get the path is not working
 
205
        for a in ('wordpad', 'wordpad.exe'):
 
206
            p = get_app_path(a)
 
207
            d, b = os.path.split(p)
 
208
            self.assertEquals('wordpad.exe', b.lower())
 
209
            self.assertNotEquals('', d)
 
210
 
167
211
    def test_not_existing(self):
168
212
        p = get_app_path('not-existing')
169
213
        self.assertEquals('not-existing', p)
185
229
        first = win32utils.get_appdata_location()
186
230
        self._captureVar("APPDATA", None)
187
231
        self.assertPathsEqual(first, win32utils.get_appdata_location())
188
 
 
 
232
 
189
233
    def test_appdata_matches_environment(self):
190
234
        # Typically the APPDATA environment variable will match
191
235
        # get_appdata_location
228
272
 
229
273
    def restoreCtypes(self):
230
274
        win32utils.has_ctypes = self.old_ctypes
 
275
 
 
276
 
 
277
class TestSetHidden(TestCaseInTempDir):
 
278
 
 
279
    def test_unicode_dir(self):
 
280
        # we should handle unicode paths without errors
 
281
        self.requireFeature(UnicodeFilenameFeature)
 
282
        os.mkdir(u'\u1234')
 
283
        win32utils.set_file_attr_hidden(u'\u1234')
 
284
 
 
285
    def test_dot_bzr_in_unicode_dir(self):
 
286
        # we should not raise traceback if we try to set hidden attribute
 
287
        # on .bzr directory below unicode path
 
288
        self.requireFeature(UnicodeFilenameFeature)
 
289
        os.makedirs(u'\u1234\\.bzr')
 
290
        path = osutils.abspath(u'\u1234\\.bzr')
 
291
        win32utils.set_file_attr_hidden(path)
 
292
 
 
293
 
 
294
 
 
295
class TestUnicodeShlex(tests.TestCase):
 
296
 
 
297
    def assertAsTokens(self, expected, line):
 
298
        s = win32utils.UnicodeShlex(line)
 
299
        self.assertEqual(expected, list(s))
 
300
 
 
301
    def test_simple(self):
 
302
        self.assertAsTokens([(False, u'foo'), (False, u'bar'), (False, u'baz')],
 
303
                            u'foo bar baz')
 
304
 
 
305
    def test_ignore_multiple_spaces(self):
 
306
        self.assertAsTokens([(False, u'foo'), (False, u'bar')], u'foo  bar')
 
307
 
 
308
    def test_ignore_leading_space(self):
 
309
        self.assertAsTokens([(False, u'foo'), (False, u'bar')], u'  foo bar')
 
310
 
 
311
    def test_ignore_trailing_space(self):
 
312
        self.assertAsTokens([(False, u'foo'), (False, u'bar')], u'foo bar  ')
 
313
 
 
314
    def test_posix_quotations(self):
 
315
        self.assertAsTokens([(True, u'foo bar')], u'"foo bar"')
 
316
        self.assertAsTokens([(False, u"'fo''o"), (False, u"b''ar'")],
 
317
            u"'fo''o b''ar'")
 
318
        self.assertAsTokens([(True, u'foo bar')], u'"fo""o b""ar"')
 
319
        self.assertAsTokens([(True, u"fo'o"), (True, u"b'ar")],
 
320
            u'"fo"\'o b\'"ar"')
 
321
 
 
322
    def test_nested_quotations(self):
 
323
        self.assertAsTokens([(True, u'foo"" bar')], u"\"foo\\\"\\\" bar\"")
 
324
        self.assertAsTokens([(True, u'foo\'\' bar')], u"\"foo'' bar\"")
 
325
 
 
326
    def test_empty_result(self):
 
327
        self.assertAsTokens([], u'')
 
328
        self.assertAsTokens([], u'    ')
 
329
 
 
330
    def test_quoted_empty(self):
 
331
        self.assertAsTokens([(True, '')], u'""')
 
332
        self.assertAsTokens([(False, u"''")], u"''")
 
333
 
 
334
    def test_unicode_chars(self):
 
335
        self.assertAsTokens([(False, u'f\xb5\xee'), (False, u'\u1234\u3456')],
 
336
                             u'f\xb5\xee \u1234\u3456')
 
337
 
 
338
    def test_newline_in_quoted_section(self):
 
339
        self.assertAsTokens([(True, u'foo\nbar\nbaz\n')], u'"foo\nbar\nbaz\n"')
 
340
 
 
341
    def test_escape_chars(self):
 
342
        self.assertAsTokens([(False, u'foo\\bar')], u'foo\\bar')
 
343
 
 
344
    def test_escape_quote(self):
 
345
        self.assertAsTokens([(True, u'foo"bar')], u'"foo\\"bar"')
 
346
 
 
347
    def test_double_escape(self):
 
348
        self.assertAsTokens([(True, u'foo\\bar')], u'"foo\\\\bar"')
 
349
        self.assertAsTokens([(False, u'foo\\\\bar')], u"foo\\\\bar")
 
350
 
 
351
 
 
352
class Test_CommandLineToArgv(tests.TestCaseInTempDir):
 
353
 
 
354
    def assertCommandLine(self, expected, line):
 
355
        # Strictly speaking we should respect parameter order versus glob
 
356
        # expansions, but it's not really worth the effort here
 
357
        self.assertEqual(expected,
 
358
                         sorted(win32utils._command_line_to_argv(line)))
 
359
 
 
360
    def test_glob_paths(self):
 
361
        self.build_tree(['a/', 'a/b.c', 'a/c.c', 'a/c.h'])
 
362
        self.assertCommandLine([u'a/b.c', u'a/c.c'], 'a/*.c')
 
363
        self.build_tree(['b/', 'b/b.c', 'b/d.c', 'b/d.h'])
 
364
        self.assertCommandLine([u'a/b.c', u'b/b.c'], '*/b.c')
 
365
        self.assertCommandLine([u'a/b.c', u'a/c.c', u'b/b.c', u'b/d.c'],
 
366
                               '*/*.c')
 
367
        # Bash style, just pass through the argument if nothing matches
 
368
        self.assertCommandLine([u'*/*.qqq'], '*/*.qqq')
 
369
 
 
370
    def test_quoted_globs(self):
 
371
        self.build_tree(['a/', 'a/b.c', 'a/c.c', 'a/c.h'])
 
372
        self.assertCommandLine([u'a/*.c'], '"a/*.c"')
 
373
        self.assertCommandLine([u"'a/*.c'"], "'a/*.c'")
 
374
 
 
375
    def test_slashes_changed(self):
 
376
        # Quoting doesn't change the supplied args
 
377
        self.assertCommandLine([u'a\\*.c'], '"a\\*.c"')
 
378
        # Expands the glob, but nothing matches, swaps slashes
 
379
        self.assertCommandLine([u'a/*.c'], 'a\\*.c')
 
380
        self.assertCommandLine([u'a/?.c'], 'a\\?.c')
 
381
        # No glob, doesn't touch slashes
 
382
        self.assertCommandLine([u'a\\foo.c'], 'a\\foo.c')
 
383
 
 
384
    def test_no_single_quote_supported(self):
 
385
        self.assertCommandLine(["add", "let's-do-it.txt"],
 
386
            "add let's-do-it.txt")
 
387
 
 
388
    def test_case_insensitive_globs(self):
 
389
        self.requireFeature(tests.CaseInsCasePresFilenameFeature)
 
390
        self.build_tree(['a/', 'a/b.c', 'a/c.c', 'a/c.h'])
 
391
        self.assertCommandLine([u'A/b.c'], 'A/B*')
 
392
 
 
393
    def test_backslashes(self):
 
394
        self.requireFeature(BackslashDirSeparatorFeature)
 
395
        self.build_tree(['a/', 'a/b.c', 'a/c.c', 'a/c.h'])
 
396
        self.assertCommandLine([u'a/b.c'], 'a\\b*')