~bzr-pqm/bzr/bzr.dev

1185.31.47 by John Arbash Meinel
Added a fancy footwork rename to osutils, made SftpTransport use it.
1
# Copyright (C) 2005 by Canonical Ltd
2
#
3
# This program is free software; you can redistribute it and/or modify
4
# it under the terms of the GNU General Public License as published by
5
# the Free Software Foundation; either version 2 of the License, or
6
# (at your option) any later version.
7
#
8
# This program is distributed in the hope that it will be useful,
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11
# GNU General Public License for more details.
12
#
13
# You should have received a copy of the GNU General Public License
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
16
1685.1.45 by John Arbash Meinel
Moved url functions into bzrlib.urlutils
17
"""Tests for the osutils wrapper."""
1185.31.47 by John Arbash Meinel
Added a fancy footwork rename to osutils, made SftpTransport use it.
18
1732.1.28 by John Arbash Meinel
Add tests for fancy file types.
19
import errno
1185.31.47 by John Arbash Meinel
Added a fancy footwork rename to osutils, made SftpTransport use it.
20
import os
1732.1.28 by John Arbash Meinel
Add tests for fancy file types.
21
import socket
22
import stat
1185.31.47 by John Arbash Meinel
Added a fancy footwork rename to osutils, made SftpTransport use it.
23
import sys
24
25
import bzrlib
1685.1.9 by John Arbash Meinel
Updated LocalTransport so that it's base is now a URL rather than a local path. This helps consistency with all other functions. To do so, I added local_abspath() which returns the local path, and local_path_to/from_url
26
from bzrlib.errors import BzrBadParameterNotUnicode, InvalidURL
1532 by Robert Collins
Merge in John Meinels integration branch.
27
import bzrlib.osutils as osutils
1711.4.10 by John Arbash Meinel
Pull out sys.stdout.encoding handling into a separate function so it can be tested, and used elsewhere.
28
from bzrlib.tests import (
29
        StringIOWrapper,
30
        TestCase, 
31
        TestCaseInTempDir, 
32
        TestSkipped,
33
        )
1532 by Robert Collins
Merge in John Meinels integration branch.
34
1185.31.47 by John Arbash Meinel
Added a fancy footwork rename to osutils, made SftpTransport use it.
35
36
class TestOSUtils(TestCaseInTempDir):
37
38
    def test_fancy_rename(self):
39
        # This should work everywhere
40
        def rename(a, b):
41
            osutils.fancy_rename(a, b,
42
                    rename_func=os.rename,
43
                    unlink_func=os.unlink)
44
45
        open('a', 'wb').write('something in a\n')
46
        rename('a', 'b')
47
        self.failIfExists('a')
48
        self.failUnlessExists('b')
49
        self.check_file_contents('b', 'something in a\n')
50
51
        open('a', 'wb').write('new something in a\n')
52
        rename('b', 'a')
53
54
        self.check_file_contents('a', 'something in a\n')
55
56
    def test_rename(self):
57
        # Rename should be semi-atomic on all platforms
58
        open('a', 'wb').write('something in a\n')
59
        osutils.rename('a', 'b')
60
        self.failIfExists('a')
61
        self.failUnlessExists('b')
62
        self.check_file_contents('b', 'something in a\n')
63
64
        open('a', 'wb').write('new something in a\n')
65
        osutils.rename('b', 'a')
66
67
        self.check_file_contents('a', 'something in a\n')
68
69
    # TODO: test fancy_rename using a MemoryTransport
70
1553.5.5 by Martin Pool
New utility routine rand_chars
71
    def test_01_rand_chars_empty(self):
72
        result = osutils.rand_chars(0)
73
        self.assertEqual(result, '')
74
75
    def test_02_rand_chars_100(self):
76
        result = osutils.rand_chars(100)
77
        self.assertEqual(len(result), 100)
78
        self.assertEqual(type(result), str)
79
        self.assertContainsRe(result, r'^[a-z0-9]{100}$')
80
1534.3.1 by Robert Collins
* bzrlib.osutils.safe_unicode now exists to provide parameter coercion
81
1692.7.6 by Martin Pool
[patch] force deletion of trees containing readonly files (alexander)
82
    def test_rmtree(self):
83
        # Check to remove tree with read-only files/dirs
84
        os.mkdir('dir')
85
        f = file('dir/file', 'w')
86
        f.write('spam')
87
        f.close()
88
        # would like to also try making the directory readonly, but at the
89
        # moment python shutil.rmtree doesn't handle that properly - it would
90
        # need to chmod the directory before removing things inside it - deferred
91
        # for now -- mbp 20060505
92
        # osutils.make_readonly('dir')
93
        osutils.make_readonly('dir/file')
94
95
        osutils.rmtree('dir')
96
97
        self.failIfExists('dir/file')
98
        self.failIfExists('dir')
99
1732.1.10 by John Arbash Meinel
Updated version of file_kind. Rather than multiple function calls, one mask + dictionary lookup
100
    def test_file_kind(self):
101
        self.build_tree(['file', 'dir/'])
102
        self.assertEquals('file', osutils.file_kind('file'))
103
        self.assertEquals('directory', osutils.file_kind('dir/'))
104
        if osutils.has_symlinks():
105
            os.symlink('symlink', 'symlink')
106
            self.assertEquals('symlink', osutils.file_kind('symlink'))
1732.1.28 by John Arbash Meinel
Add tests for fancy file types.
107
        
108
        # TODO: jam 20060529 Test a block device
109
        try:
110
            os.lstat('/dev/null')
111
        except OSError, e:
112
            if e.errno not in (errno.ENOENT,):
113
                raise
114
        else:
115
            self.assertEquals('chardev', osutils.file_kind('/dev/null'))
116
117
        mkfifo = getattr(os, 'mkfifo', None)
118
        if mkfifo:
119
            mkfifo('fifo')
120
            try:
121
                self.assertEquals('fifo', osutils.file_kind('fifo'))
122
            finally:
123
                os.remove('fifo')
124
125
        AF_UNIX = getattr(socket, 'AF_UNIX', None)
126
        if AF_UNIX:
127
            s = socket.socket(AF_UNIX)
128
            s.bind('socket')
129
            try:
130
                self.assertEquals('socket', osutils.file_kind('socket'))
131
            finally:
132
                os.remove('socket')
1732.1.10 by John Arbash Meinel
Updated version of file_kind. Rather than multiple function calls, one mask + dictionary lookup
133
1755.3.7 by John Arbash Meinel
Clean up and write tests for permissions. Now we use fstat which should be cheap, and lets us check the permissions and the file size
134
    def test_get_umask(self):
135
        if sys.platform == 'win32':
136
            # umask always returns '0', no way to set it
137
            self.assertEqual(0, osutils.get_umask())
138
            return
139
140
        orig_umask = osutils.get_umask()
141
        try:
142
            os.umask(0222)
143
            self.assertEqual(0222, osutils.get_umask())
144
            os.umask(0022)
145
            self.assertEqual(0022, osutils.get_umask())
146
            os.umask(0002)
147
            self.assertEqual(0002, osutils.get_umask())
148
            os.umask(0027)
149
            self.assertEqual(0027, osutils.get_umask())
150
        finally:
151
            os.umask(orig_umask)
152
1692.7.6 by Martin Pool
[patch] force deletion of trees containing readonly files (alexander)
153
1534.3.1 by Robert Collins
* bzrlib.osutils.safe_unicode now exists to provide parameter coercion
154
class TestSafeUnicode(TestCase):
155
156
    def test_from_ascii_string(self):
157
        self.assertEqual(u'foobar', osutils.safe_unicode('foobar'))
158
1534.3.2 by Robert Collins
An extra test for John.
159
    def test_from_unicode_string_ascii_contents(self):
1534.3.1 by Robert Collins
* bzrlib.osutils.safe_unicode now exists to provide parameter coercion
160
        self.assertEqual(u'bargam', osutils.safe_unicode(u'bargam'))
161
1534.3.2 by Robert Collins
An extra test for John.
162
    def test_from_unicode_string_unicode_contents(self):
163
        self.assertEqual(u'bargam\xae', osutils.safe_unicode(u'bargam\xae'))
164
1534.3.1 by Robert Collins
* bzrlib.osutils.safe_unicode now exists to provide parameter coercion
165
    def test_from_utf8_string(self):
166
        self.assertEqual(u'foo\xae', osutils.safe_unicode('foo\xc2\xae'))
167
168
    def test_bad_utf8_string(self):
1185.65.29 by Robert Collins
Implement final review suggestions.
169
        self.assertRaises(BzrBadParameterNotUnicode,
170
                          osutils.safe_unicode,
171
                          '\xbb\xbb')
1666.1.6 by Robert Collins
Make knit the default format.
172
173
1685.1.31 by John Arbash Meinel
Adding tests for the rest of the _win32 functions.
174
class TestWin32Funcs(TestCase):
175
    """Test that the _win32 versions of os utilities return appropriate paths."""
176
177
    def test_abspath(self):
178
        self.assertEqual('C:/foo', osutils._win32_abspath('C:\\foo'))
179
        self.assertEqual('C:/foo', osutils._win32_abspath('C:/foo'))
180
181
    def test_realpath(self):
182
        self.assertEqual('C:/foo', osutils._win32_realpath('C:\\foo'))
183
        self.assertEqual('C:/foo', osutils._win32_realpath('C:/foo'))
184
185
    def test_pathjoin(self):
186
        self.assertEqual('path/to/foo', osutils._win32_pathjoin('path', 'to', 'foo'))
187
        self.assertEqual('C:/foo', osutils._win32_pathjoin('path\\to', 'C:\\foo'))
188
        self.assertEqual('C:/foo', osutils._win32_pathjoin('path/to', 'C:/foo'))
189
        self.assertEqual('path/to/foo', osutils._win32_pathjoin('path/to/', 'foo'))
190
        self.assertEqual('/foo', osutils._win32_pathjoin('C:/path/to/', '/foo'))
191
        self.assertEqual('/foo', osutils._win32_pathjoin('C:\\path\\to\\', '\\foo'))
192
193
    def test_normpath(self):
194
        self.assertEqual('path/to/foo', osutils._win32_normpath(r'path\\from\..\to\.\foo'))
195
        self.assertEqual('path/to/foo', osutils._win32_normpath('path//from/../to/./foo'))
196
197
    def test_getcwd(self):
1711.5.2 by John Arbash Meinel
win32 likes to return lowercase drive letters sometimes, and uppercase at other times. normalize this
198
        cwd = osutils._win32_getcwd()
199
        os_cwd = os.getcwdu()
200
        self.assertEqual(os_cwd[1:].replace('\\', '/'), cwd[1:])
201
        # win32 is inconsistent whether it returns lower or upper case
202
        # and even if it was consistent the user might type the other
203
        # so we force it to uppercase
204
        # running python.exe under cmd.exe return capital C:\\
205
        # running win32 python inside a cygwin shell returns lowercase
206
        self.assertEqual(os_cwd[0].upper(), cwd[0])
207
208
    def test_fixdrive(self):
209
        self.assertEqual('H:/foo', osutils._win32_fixdrive('h:/foo'))
210
        self.assertEqual('H:/foo', osutils._win32_fixdrive('H:/foo'))
211
        self.assertEqual('C:\\foo', osutils._win32_fixdrive('c:\\foo'))
1685.1.31 by John Arbash Meinel
Adding tests for the rest of the _win32 functions.
212
213
214
class TestWin32FuncsDirs(TestCaseInTempDir):
215
    """Test win32 functions that create files."""
216
    
217
    def test_getcwd(self):
218
        # Make sure getcwd can handle unicode filenames
219
        try:
1830.3.9 by John Arbash Meinel
Use a directory name that doesn't get messed up on Mac for getcwd() test.
220
            os.mkdir(u'mu-\xb5')
1685.1.31 by John Arbash Meinel
Adding tests for the rest of the _win32 functions.
221
        except UnicodeError:
222
            raise TestSkipped("Unable to create Unicode filename")
223
1830.3.9 by John Arbash Meinel
Use a directory name that doesn't get messed up on Mac for getcwd() test.
224
        os.chdir(u'mu-\xb5')
1685.1.31 by John Arbash Meinel
Adding tests for the rest of the _win32 functions.
225
        # TODO: jam 20060427 This will probably fail on Mac OSX because
226
        #       it will change the normalization of B\xe5gfors
227
        #       Consider using a different unicode character, or make
228
        #       osutils.getcwd() renormalize the path.
1830.3.9 by John Arbash Meinel
Use a directory name that doesn't get messed up on Mac for getcwd() test.
229
        self.assertEndsWith(osutils._win32_getcwd(), u'mu-\xb5')
1685.1.31 by John Arbash Meinel
Adding tests for the rest of the _win32 functions.
230
231
    def test_mkdtemp(self):
232
        tmpdir = osutils._win32_mkdtemp(dir='.')
233
        self.assertFalse('\\' in tmpdir)
234
235
    def test_rename(self):
236
        a = open('a', 'wb')
237
        a.write('foo\n')
238
        a.close()
239
        b = open('b', 'wb')
240
        b.write('baz\n')
241
        b.close()
242
243
        osutils._win32_rename('b', 'a')
244
        self.failUnlessExists('a')
245
        self.failIfExists('b')
246
        self.assertFileEqual('baz\n', 'a')
247
1711.7.6 by John Arbash Meinel
Change _win32_rename() so that it raises ENOENT *before* it tries any renaming.
248
    def test_rename_missing_file(self):
249
        a = open('a', 'wb')
250
        a.write('foo\n')
251
        a.close()
252
253
        try:
254
            osutils._win32_rename('b', 'a')
255
        except (IOError, OSError), e:
256
            self.assertEqual(errno.ENOENT, e.errno)
257
        self.assertFileEqual('foo\n', 'a')
258
259
    def test_rename_missing_dir(self):
260
        os.mkdir('a')
261
        try:
262
            osutils._win32_rename('b', 'a')
263
        except (IOError, OSError), e:
264
            self.assertEqual(errno.ENOENT, e.errno)
265
266
    def test_rename_current_dir(self):
267
        os.mkdir('a')
268
        os.chdir('a')
269
        # You can't rename the working directory
270
        # doing rename non-existant . usually
271
        # just raises ENOENT, since non-existant
272
        # doesn't exist.
273
        try:
274
            osutils._win32_rename('b', '.')
275
        except (IOError, OSError), e:
276
            self.assertEqual(errno.ENOENT, e.errno)
277
1685.1.31 by John Arbash Meinel
Adding tests for the rest of the _win32 functions.
278
1830.3.11 by John Arbash Meinel
Create a mac version of 'getcwd()' which normalizes the path.
279
class TestMacFuncsDirs(TestCaseInTempDir):
280
    """Test mac special functions that require directories."""
281
282
    def test_getcwd(self):
283
        # On Mac, this will actually create Ba\u030agfors
284
        # but chdir will still work, because it accepts both paths
285
        try:
286
            os.mkdir(u'B\xe5gfors')
287
        except UnicodeError:
288
            raise TestSkipped("Unable to create Unicode filename")
289
290
        os.chdir(u'B\xe5gfors')
291
        self.assertEndsWith(osutils._mac_getcwd(), u'B\xe5gfors')
292
293
    def test_getcwd_nonnorm(self):
294
        # Test that _mac_getcwd() will normalize this path
295
        try:
296
            os.mkdir(u'Ba\u030agfors')
297
        except UnicodeError:
298
            raise TestSkipped("Unable to create Unicode filename")
299
300
        os.chdir(u'Ba\u030agfors')
301
        self.assertEndsWith(osutils._mac_getcwd(), u'B\xe5gfors')
302
1666.1.6 by Robert Collins
Make knit the default format.
303
class TestSplitLines(TestCase):
304
305
    def test_split_unicode(self):
306
        self.assertEqual([u'foo\n', u'bar\xae'],
307
                         osutils.split_lines(u'foo\nbar\xae'))
308
        self.assertEqual([u'foo\n', u'bar\xae\n'],
309
                         osutils.split_lines(u'foo\nbar\xae\n'))
310
311
    def test_split_with_carriage_returns(self):
312
        self.assertEqual(['foo\rbar\n'],
313
                         osutils.split_lines('foo\rbar\n'))
1753.1.1 by Robert Collins
(rbc, jam, mbp)Add bzrlib.osutils.walkdirs, an optimised walk-and-stat routine.
314
315
316
class TestWalkDirs(TestCaseInTempDir):
317
318
    def test_walkdirs(self):
319
        tree = [
320
            '.bzr',
321
            '0file',
322
            '1dir/',
323
            '1dir/0file',
324
            '1dir/1dir/',
325
            '2file'
326
            ]
327
        self.build_tree(tree)
328
        expected_dirblocks = [
1897.1.1 by Robert Collins
Add some useful summary data to osutils.walkdirs output.
329
                (('', '.'),
330
                 [('0file', '0file', 'file'),
331
                  ('1dir', '1dir', 'directory'),
332
                  ('2file', '2file', 'file'),
333
                 ]
334
                ),
335
                (('1dir', './1dir'),
336
                 [('1dir/0file', '0file', 'file'),
337
                  ('1dir/1dir', '1dir', 'directory'),
338
                 ]
339
                ),
340
                (('1dir/1dir', './1dir/1dir'),
341
                 [
342
                 ]
343
                ),
1753.1.1 by Robert Collins
(rbc, jam, mbp)Add bzrlib.osutils.walkdirs, an optimised walk-and-stat routine.
344
            ]
345
        result = []
346
        found_bzrdir = False
1897.1.1 by Robert Collins
Add some useful summary data to osutils.walkdirs output.
347
        for dirdetail, dirblock in osutils.walkdirs('.'):
1753.1.1 by Robert Collins
(rbc, jam, mbp)Add bzrlib.osutils.walkdirs, an optimised walk-and-stat routine.
348
            if len(dirblock) and dirblock[0][1] == '.bzr':
349
                # this tests the filtering of selected paths
350
                found_bzrdir = True
351
                del dirblock[0]
1897.1.1 by Robert Collins
Add some useful summary data to osutils.walkdirs output.
352
            result.append((dirdetail, dirblock))
1753.1.1 by Robert Collins
(rbc, jam, mbp)Add bzrlib.osutils.walkdirs, an optimised walk-and-stat routine.
353
354
        self.assertTrue(found_bzrdir)
355
        self.assertEqual(expected_dirblocks,
1897.1.1 by Robert Collins
Add some useful summary data to osutils.walkdirs output.
356
            [(dirinfo, [line[0:3] for line in block]) for dirinfo, block in result])
1757.2.8 by Robert Collins
Teach walkdirs to walk a subdir of a tree.
357
        # you can search a subdir only, with a supplied prefix.
358
        result = []
1897.1.1 by Robert Collins
Add some useful summary data to osutils.walkdirs output.
359
        for dirblock in osutils.walkdirs('./1dir', '1dir'):
1757.2.8 by Robert Collins
Teach walkdirs to walk a subdir of a tree.
360
            result.append(dirblock)
361
        self.assertEqual(expected_dirblocks[1:],
1897.1.1 by Robert Collins
Add some useful summary data to osutils.walkdirs output.
362
            [(dirinfo, [line[0:3] for line in block]) for dirinfo, block in result])
1757.2.8 by Robert Collins
Teach walkdirs to walk a subdir of a tree.
363
1773.3.1 by Robert Collins
Add path_prefix_key and compare_paths_prefix_order utility functions.
364
    def assertPathCompare(self, path_less, path_greater):
365
        """check that path_less and path_greater compare correctly."""
366
        self.assertEqual(0, osutils.compare_paths_prefix_order(
367
            path_less, path_less))
368
        self.assertEqual(0, osutils.compare_paths_prefix_order(
369
            path_greater, path_greater))
370
        self.assertEqual(-1, osutils.compare_paths_prefix_order(
371
            path_less, path_greater))
372
        self.assertEqual(1, osutils.compare_paths_prefix_order(
373
            path_greater, path_less))
374
375
    def test_compare_paths_prefix_order(self):
376
        # root before all else
377
        self.assertPathCompare("/", "/a")
378
        # alpha within a dir
379
        self.assertPathCompare("/a", "/b")
380
        self.assertPathCompare("/b", "/z")
381
        # high dirs before lower.
382
        self.assertPathCompare("/z", "/a/a")
1773.3.2 by Robert Collins
New corner case from John Meinel, showing up the need to check the directory lexographically outside of a single tree's root. Fixed.
383
        # except if the deeper dir should be output first
384
        self.assertPathCompare("/a/b/c", "/d/g")
1773.3.1 by Robert Collins
Add path_prefix_key and compare_paths_prefix_order utility functions.
385
        # lexical betwen dirs of the same height
386
        self.assertPathCompare("/a/z", "/z/z")
387
        self.assertPathCompare("/a/c/z", "/a/d/e")
388
389
        # this should also be consistent for no leading / paths
390
        # root before all else
391
        self.assertPathCompare("", "a")
392
        # alpha within a dir
393
        self.assertPathCompare("a", "b")
394
        self.assertPathCompare("b", "z")
395
        # high dirs before lower.
396
        self.assertPathCompare("z", "a/a")
1773.3.2 by Robert Collins
New corner case from John Meinel, showing up the need to check the directory lexographically outside of a single tree's root. Fixed.
397
        # except if the deeper dir should be output first
398
        self.assertPathCompare("a/b/c", "d/g")
1773.3.1 by Robert Collins
Add path_prefix_key and compare_paths_prefix_order utility functions.
399
        # lexical betwen dirs of the same height
400
        self.assertPathCompare("a/z", "z/z")
401
        self.assertPathCompare("a/c/z", "a/d/e")
402
1773.3.3 by Robert Collins
Add new tests John Meinel asked for.
403
    def test_path_prefix_sorting(self):
404
        """Doing a sort on path prefix should match our sample data."""
405
        original_paths = [
406
            'a',
407
            'a/b',
408
            'a/b/c',
409
            'b',
410
            'b/c',
411
            'd',
412
            'd/e',
413
            'd/e/f',
414
            'd/f',
415
            'd/g',
416
            'g',
417
            ]
418
419
        dir_sorted_paths = [
420
            'a',
421
            'b',
422
            'd',
423
            'g',
424
            'a/b',
425
            'a/b/c',
426
            'b/c',
427
            'd/e',
428
            'd/f',
429
            'd/g',
430
            'd/e/f',
431
            ]
432
433
        self.assertEqual(
434
            dir_sorted_paths,
435
            sorted(original_paths, key=osutils.path_prefix_key))
436
        # using the comparison routine shoudl work too:
437
        self.assertEqual(
438
            dir_sorted_paths,
439
            sorted(original_paths, cmp=osutils.compare_paths_prefix_order))
1711.4.10 by John Arbash Meinel
Pull out sys.stdout.encoding handling into a separate function so it can be tested, and used elsewhere.
440
441
442
class TestTerminalEncoding(TestCase):
443
    """Test the auto-detection of proper terminal encoding."""
444
445
    def setUp(self):
446
        self._stdout = sys.stdout
447
        self._stderr = sys.stderr
448
        self._stdin = sys.stdin
449
        self._user_encoding = bzrlib.user_encoding
450
451
        self.addCleanup(self._reset)
452
453
        sys.stdout = StringIOWrapper()
454
        sys.stdout.encoding = 'stdout_encoding'
455
        sys.stderr = StringIOWrapper()
456
        sys.stderr.encoding = 'stderr_encoding'
457
        sys.stdin = StringIOWrapper()
458
        sys.stdin.encoding = 'stdin_encoding'
459
        bzrlib.user_encoding = 'user_encoding'
460
461
    def _reset(self):
462
        sys.stdout = self._stdout
463
        sys.stderr = self._stderr
464
        sys.stdin = self._stdin
465
        bzrlib.user_encoding = self._user_encoding
466
467
    def test_get_terminal_encoding(self):
468
        # first preference is stdout encoding
469
        self.assertEqual('stdout_encoding', osutils.get_terminal_encoding())
470
471
        sys.stdout.encoding = None
472
        # if sys.stdout is None, fall back to sys.stdin
473
        self.assertEqual('stdin_encoding', osutils.get_terminal_encoding())
474
475
        sys.stdin.encoding = None
476
        # and in the worst case, use bzrlib.user_encoding
477
        self.assertEqual('user_encoding', osutils.get_terminal_encoding())
478