~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_osutils.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-12-20 18:52:55 UTC
  • mfrom: (2204.2.1 bzr.dev)
  • Revision ID: pqm@pqm.ubuntu.com-20061220185255-86cd0a40a9c2e76e
(Wouter van Heyst) Mention the revisionspec topic in the revision option help (#31633).

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2006 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
 
 
17
"""Tests for the osutils wrapper."""
 
18
 
 
19
import errno
 
20
import os
 
21
import socket
 
22
import stat
 
23
import sys
 
24
 
 
25
import bzrlib
 
26
from bzrlib import (
 
27
    errors,
 
28
    osutils,
 
29
    )
 
30
from bzrlib.errors import BzrBadParameterNotUnicode, InvalidURL
 
31
from bzrlib.tests import (
 
32
        StringIOWrapper,
 
33
        TestCase, 
 
34
        TestCaseInTempDir, 
 
35
        TestSkipped,
 
36
        )
 
37
 
 
38
 
 
39
class TestOSUtils(TestCaseInTempDir):
 
40
 
 
41
    def test_fancy_rename(self):
 
42
        # This should work everywhere
 
43
        def rename(a, b):
 
44
            osutils.fancy_rename(a, b,
 
45
                    rename_func=os.rename,
 
46
                    unlink_func=os.unlink)
 
47
 
 
48
        open('a', 'wb').write('something in a\n')
 
49
        rename('a', 'b')
 
50
        self.failIfExists('a')
 
51
        self.failUnlessExists('b')
 
52
        self.check_file_contents('b', 'something in a\n')
 
53
 
 
54
        open('a', 'wb').write('new something in a\n')
 
55
        rename('b', 'a')
 
56
 
 
57
        self.check_file_contents('a', 'something in a\n')
 
58
 
 
59
    def test_rename(self):
 
60
        # Rename should be semi-atomic on all platforms
 
61
        open('a', 'wb').write('something in a\n')
 
62
        osutils.rename('a', 'b')
 
63
        self.failIfExists('a')
 
64
        self.failUnlessExists('b')
 
65
        self.check_file_contents('b', 'something in a\n')
 
66
 
 
67
        open('a', 'wb').write('new something in a\n')
 
68
        osutils.rename('b', 'a')
 
69
 
 
70
        self.check_file_contents('a', 'something in a\n')
 
71
 
 
72
    # TODO: test fancy_rename using a MemoryTransport
 
73
 
 
74
    def test_01_rand_chars_empty(self):
 
75
        result = osutils.rand_chars(0)
 
76
        self.assertEqual(result, '')
 
77
 
 
78
    def test_02_rand_chars_100(self):
 
79
        result = osutils.rand_chars(100)
 
80
        self.assertEqual(len(result), 100)
 
81
        self.assertEqual(type(result), str)
 
82
        self.assertContainsRe(result, r'^[a-z0-9]{100}$')
 
83
 
 
84
    def test_is_inside(self):
 
85
        is_inside = osutils.is_inside
 
86
        self.assertTrue(is_inside('src', 'src/foo.c'))
 
87
        self.assertFalse(is_inside('src', 'srccontrol'))
 
88
        self.assertTrue(is_inside('src', 'src/a/a/a/foo.c'))
 
89
        self.assertTrue(is_inside('foo.c', 'foo.c'))
 
90
        self.assertFalse(is_inside('foo.c', ''))
 
91
        self.assertTrue(is_inside('', 'foo.c'))
 
92
 
 
93
    def test_rmtree(self):
 
94
        # Check to remove tree with read-only files/dirs
 
95
        os.mkdir('dir')
 
96
        f = file('dir/file', 'w')
 
97
        f.write('spam')
 
98
        f.close()
 
99
        # would like to also try making the directory readonly, but at the
 
100
        # moment python shutil.rmtree doesn't handle that properly - it would
 
101
        # need to chmod the directory before removing things inside it - deferred
 
102
        # for now -- mbp 20060505
 
103
        # osutils.make_readonly('dir')
 
104
        osutils.make_readonly('dir/file')
 
105
 
 
106
        osutils.rmtree('dir')
 
107
 
 
108
        self.failIfExists('dir/file')
 
109
        self.failIfExists('dir')
 
110
 
 
111
    def test_file_kind(self):
 
112
        self.build_tree(['file', 'dir/'])
 
113
        self.assertEquals('file', osutils.file_kind('file'))
 
114
        self.assertEquals('directory', osutils.file_kind('dir/'))
 
115
        if osutils.has_symlinks():
 
116
            os.symlink('symlink', 'symlink')
 
117
            self.assertEquals('symlink', osutils.file_kind('symlink'))
 
118
        
 
119
        # TODO: jam 20060529 Test a block device
 
120
        try:
 
121
            os.lstat('/dev/null')
 
122
        except OSError, e:
 
123
            if e.errno not in (errno.ENOENT,):
 
124
                raise
 
125
        else:
 
126
            self.assertEquals('chardev', osutils.file_kind('/dev/null'))
 
127
 
 
128
        mkfifo = getattr(os, 'mkfifo', None)
 
129
        if mkfifo:
 
130
            mkfifo('fifo')
 
131
            try:
 
132
                self.assertEquals('fifo', osutils.file_kind('fifo'))
 
133
            finally:
 
134
                os.remove('fifo')
 
135
 
 
136
        AF_UNIX = getattr(socket, 'AF_UNIX', None)
 
137
        if AF_UNIX:
 
138
            s = socket.socket(AF_UNIX)
 
139
            s.bind('socket')
 
140
            try:
 
141
                self.assertEquals('socket', osutils.file_kind('socket'))
 
142
            finally:
 
143
                os.remove('socket')
 
144
 
 
145
    def test_get_umask(self):
 
146
        if sys.platform == 'win32':
 
147
            # umask always returns '0', no way to set it
 
148
            self.assertEqual(0, osutils.get_umask())
 
149
            return
 
150
 
 
151
        orig_umask = osutils.get_umask()
 
152
        try:
 
153
            os.umask(0222)
 
154
            self.assertEqual(0222, osutils.get_umask())
 
155
            os.umask(0022)
 
156
            self.assertEqual(0022, osutils.get_umask())
 
157
            os.umask(0002)
 
158
            self.assertEqual(0002, osutils.get_umask())
 
159
            os.umask(0027)
 
160
            self.assertEqual(0027, osutils.get_umask())
 
161
        finally:
 
162
            os.umask(orig_umask)
 
163
 
 
164
    def assertFormatedDelta(self, expected, seconds):
 
165
        """Assert osutils.format_delta formats as expected"""
 
166
        actual = osutils.format_delta(seconds)
 
167
        self.assertEqual(expected, actual)
 
168
 
 
169
    def test_format_delta(self):
 
170
        self.assertFormatedDelta('0 seconds ago', 0)
 
171
        self.assertFormatedDelta('1 second ago', 1)
 
172
        self.assertFormatedDelta('10 seconds ago', 10)
 
173
        self.assertFormatedDelta('59 seconds ago', 59)
 
174
        self.assertFormatedDelta('89 seconds ago', 89)
 
175
        self.assertFormatedDelta('1 minute, 30 seconds ago', 90)
 
176
        self.assertFormatedDelta('3 minutes, 0 seconds ago', 180)
 
177
        self.assertFormatedDelta('3 minutes, 1 second ago', 181)
 
178
        self.assertFormatedDelta('10 minutes, 15 seconds ago', 615)
 
179
        self.assertFormatedDelta('30 minutes, 59 seconds ago', 1859)
 
180
        self.assertFormatedDelta('31 minutes, 0 seconds ago', 1860)
 
181
        self.assertFormatedDelta('60 minutes, 0 seconds ago', 3600)
 
182
        self.assertFormatedDelta('89 minutes, 59 seconds ago', 5399)
 
183
        self.assertFormatedDelta('1 hour, 30 minutes ago', 5400)
 
184
        self.assertFormatedDelta('2 hours, 30 minutes ago', 9017)
 
185
        self.assertFormatedDelta('10 hours, 0 minutes ago', 36000)
 
186
        self.assertFormatedDelta('24 hours, 0 minutes ago', 86400)
 
187
        self.assertFormatedDelta('35 hours, 59 minutes ago', 129599)
 
188
        self.assertFormatedDelta('36 hours, 0 minutes ago', 129600)
 
189
        self.assertFormatedDelta('36 hours, 0 minutes ago', 129601)
 
190
        self.assertFormatedDelta('36 hours, 1 minute ago', 129660)
 
191
        self.assertFormatedDelta('36 hours, 1 minute ago', 129661)
 
192
        self.assertFormatedDelta('84 hours, 10 minutes ago', 303002)
 
193
 
 
194
        # We handle when time steps the wrong direction because computers
 
195
        # don't have synchronized clocks.
 
196
        self.assertFormatedDelta('84 hours, 10 minutes in the future', -303002)
 
197
        self.assertFormatedDelta('1 second in the future', -1)
 
198
        self.assertFormatedDelta('2 seconds in the future', -2)
 
199
 
 
200
    def test_dereference_path(self):
 
201
        if not osutils.has_symlinks():
 
202
            raise TestSkipped('Symlinks are not supported on this platform')
 
203
        cwd = osutils.realpath('.')
 
204
        os.mkdir('bar')
 
205
        bar_path = osutils.pathjoin(cwd, 'bar')
 
206
        # Using './' to avoid bug #1213894 (first path component not
 
207
        # dereferenced) in Python 2.4.1 and earlier
 
208
        self.assertEqual(bar_path, osutils.realpath('./bar'))
 
209
        os.symlink('bar', 'foo')
 
210
        self.assertEqual(bar_path, osutils.realpath('./foo'))
 
211
        
 
212
        # Does not dereference terminal symlinks
 
213
        foo_path = osutils.pathjoin(cwd, 'foo')
 
214
        self.assertEqual(foo_path, osutils.dereference_path('./foo'))
 
215
 
 
216
        # Dereferences parent symlinks
 
217
        os.mkdir('bar/baz')
 
218
        baz_path = osutils.pathjoin(bar_path, 'baz')
 
219
        self.assertEqual(baz_path, osutils.dereference_path('./foo/baz'))
 
220
 
 
221
        # Dereferences parent symlinks that are the first path element
 
222
        self.assertEqual(baz_path, osutils.dereference_path('foo/baz'))
 
223
 
 
224
        # Dereferences parent symlinks in absolute paths
 
225
        foo_baz_path = osutils.pathjoin(foo_path, 'baz')
 
226
        self.assertEqual(baz_path, osutils.dereference_path(foo_baz_path))
 
227
 
 
228
 
 
229
class TestSafeUnicode(TestCase):
 
230
 
 
231
    def test_from_ascii_string(self):
 
232
        self.assertEqual(u'foobar', osutils.safe_unicode('foobar'))
 
233
 
 
234
    def test_from_unicode_string_ascii_contents(self):
 
235
        self.assertEqual(u'bargam', osutils.safe_unicode(u'bargam'))
 
236
 
 
237
    def test_from_unicode_string_unicode_contents(self):
 
238
        self.assertEqual(u'bargam\xae', osutils.safe_unicode(u'bargam\xae'))
 
239
 
 
240
    def test_from_utf8_string(self):
 
241
        self.assertEqual(u'foo\xae', osutils.safe_unicode('foo\xc2\xae'))
 
242
 
 
243
    def test_bad_utf8_string(self):
 
244
        self.assertRaises(BzrBadParameterNotUnicode,
 
245
                          osutils.safe_unicode,
 
246
                          '\xbb\xbb')
 
247
 
 
248
 
 
249
class TestWin32Funcs(TestCase):
 
250
    """Test that the _win32 versions of os utilities return appropriate paths."""
 
251
 
 
252
    def test_abspath(self):
 
253
        self.assertEqual('C:/foo', osutils._win32_abspath('C:\\foo'))
 
254
        self.assertEqual('C:/foo', osutils._win32_abspath('C:/foo'))
 
255
 
 
256
    def test_realpath(self):
 
257
        self.assertEqual('C:/foo', osutils._win32_realpath('C:\\foo'))
 
258
        self.assertEqual('C:/foo', osutils._win32_realpath('C:/foo'))
 
259
 
 
260
    def test_pathjoin(self):
 
261
        self.assertEqual('path/to/foo', osutils._win32_pathjoin('path', 'to', 'foo'))
 
262
        self.assertEqual('C:/foo', osutils._win32_pathjoin('path\\to', 'C:\\foo'))
 
263
        self.assertEqual('C:/foo', osutils._win32_pathjoin('path/to', 'C:/foo'))
 
264
        self.assertEqual('path/to/foo', osutils._win32_pathjoin('path/to/', 'foo'))
 
265
        self.assertEqual('/foo', osutils._win32_pathjoin('C:/path/to/', '/foo'))
 
266
        self.assertEqual('/foo', osutils._win32_pathjoin('C:\\path\\to\\', '\\foo'))
 
267
 
 
268
    def test_normpath(self):
 
269
        self.assertEqual('path/to/foo', osutils._win32_normpath(r'path\\from\..\to\.\foo'))
 
270
        self.assertEqual('path/to/foo', osutils._win32_normpath('path//from/../to/./foo'))
 
271
 
 
272
    def test_getcwd(self):
 
273
        cwd = osutils._win32_getcwd()
 
274
        os_cwd = os.getcwdu()
 
275
        self.assertEqual(os_cwd[1:].replace('\\', '/'), cwd[1:])
 
276
        # win32 is inconsistent whether it returns lower or upper case
 
277
        # and even if it was consistent the user might type the other
 
278
        # so we force it to uppercase
 
279
        # running python.exe under cmd.exe return capital C:\\
 
280
        # running win32 python inside a cygwin shell returns lowercase
 
281
        self.assertEqual(os_cwd[0].upper(), cwd[0])
 
282
 
 
283
    def test_fixdrive(self):
 
284
        self.assertEqual('H:/foo', osutils._win32_fixdrive('h:/foo'))
 
285
        self.assertEqual('H:/foo', osutils._win32_fixdrive('H:/foo'))
 
286
        self.assertEqual('C:\\foo', osutils._win32_fixdrive('c:\\foo'))
 
287
 
 
288
 
 
289
class TestWin32FuncsDirs(TestCaseInTempDir):
 
290
    """Test win32 functions that create files."""
 
291
    
 
292
    def test_getcwd(self):
 
293
        # Make sure getcwd can handle unicode filenames
 
294
        try:
 
295
            os.mkdir(u'mu-\xb5')
 
296
        except UnicodeError:
 
297
            raise TestSkipped("Unable to create Unicode filename")
 
298
 
 
299
        os.chdir(u'mu-\xb5')
 
300
        # TODO: jam 20060427 This will probably fail on Mac OSX because
 
301
        #       it will change the normalization of B\xe5gfors
 
302
        #       Consider using a different unicode character, or make
 
303
        #       osutils.getcwd() renormalize the path.
 
304
        self.assertEndsWith(osutils._win32_getcwd(), u'mu-\xb5')
 
305
 
 
306
    def test_mkdtemp(self):
 
307
        tmpdir = osutils._win32_mkdtemp(dir='.')
 
308
        self.assertFalse('\\' in tmpdir)
 
309
 
 
310
    def test_rename(self):
 
311
        a = open('a', 'wb')
 
312
        a.write('foo\n')
 
313
        a.close()
 
314
        b = open('b', 'wb')
 
315
        b.write('baz\n')
 
316
        b.close()
 
317
 
 
318
        osutils._win32_rename('b', 'a')
 
319
        self.failUnlessExists('a')
 
320
        self.failIfExists('b')
 
321
        self.assertFileEqual('baz\n', 'a')
 
322
 
 
323
    def test_rename_missing_file(self):
 
324
        a = open('a', 'wb')
 
325
        a.write('foo\n')
 
326
        a.close()
 
327
 
 
328
        try:
 
329
            osutils._win32_rename('b', 'a')
 
330
        except (IOError, OSError), e:
 
331
            self.assertEqual(errno.ENOENT, e.errno)
 
332
        self.assertFileEqual('foo\n', 'a')
 
333
 
 
334
    def test_rename_missing_dir(self):
 
335
        os.mkdir('a')
 
336
        try:
 
337
            osutils._win32_rename('b', 'a')
 
338
        except (IOError, OSError), e:
 
339
            self.assertEqual(errno.ENOENT, e.errno)
 
340
 
 
341
    def test_rename_current_dir(self):
 
342
        os.mkdir('a')
 
343
        os.chdir('a')
 
344
        # You can't rename the working directory
 
345
        # doing rename non-existant . usually
 
346
        # just raises ENOENT, since non-existant
 
347
        # doesn't exist.
 
348
        try:
 
349
            osutils._win32_rename('b', '.')
 
350
        except (IOError, OSError), e:
 
351
            self.assertEqual(errno.ENOENT, e.errno)
 
352
 
 
353
    def test_splitpath(self):
 
354
        def check(expected, path):
 
355
            self.assertEqual(expected, osutils.splitpath(path))
 
356
 
 
357
        check(['a'], 'a')
 
358
        check(['a', 'b'], 'a/b')
 
359
        check(['a', 'b'], 'a/./b')
 
360
        check(['a', '.b'], 'a/.b')
 
361
        check(['a', '.b'], 'a\\.b')
 
362
 
 
363
        self.assertRaises(errors.BzrError, osutils.splitpath, 'a/../b')
 
364
 
 
365
 
 
366
class TestMacFuncsDirs(TestCaseInTempDir):
 
367
    """Test mac special functions that require directories."""
 
368
 
 
369
    def test_getcwd(self):
 
370
        # On Mac, this will actually create Ba\u030agfors
 
371
        # but chdir will still work, because it accepts both paths
 
372
        try:
 
373
            os.mkdir(u'B\xe5gfors')
 
374
        except UnicodeError:
 
375
            raise TestSkipped("Unable to create Unicode filename")
 
376
 
 
377
        os.chdir(u'B\xe5gfors')
 
378
        self.assertEndsWith(osutils._mac_getcwd(), u'B\xe5gfors')
 
379
 
 
380
    def test_getcwd_nonnorm(self):
 
381
        # Test that _mac_getcwd() will normalize this path
 
382
        try:
 
383
            os.mkdir(u'Ba\u030agfors')
 
384
        except UnicodeError:
 
385
            raise TestSkipped("Unable to create Unicode filename")
 
386
 
 
387
        os.chdir(u'Ba\u030agfors')
 
388
        self.assertEndsWith(osutils._mac_getcwd(), u'B\xe5gfors')
 
389
 
 
390
 
 
391
class TestSplitLines(TestCase):
 
392
 
 
393
    def test_split_unicode(self):
 
394
        self.assertEqual([u'foo\n', u'bar\xae'],
 
395
                         osutils.split_lines(u'foo\nbar\xae'))
 
396
        self.assertEqual([u'foo\n', u'bar\xae\n'],
 
397
                         osutils.split_lines(u'foo\nbar\xae\n'))
 
398
 
 
399
    def test_split_with_carriage_returns(self):
 
400
        self.assertEqual(['foo\rbar\n'],
 
401
                         osutils.split_lines('foo\rbar\n'))
 
402
 
 
403
 
 
404
class TestWalkDirs(TestCaseInTempDir):
 
405
 
 
406
    def test_walkdirs(self):
 
407
        tree = [
 
408
            '.bzr',
 
409
            '0file',
 
410
            '1dir/',
 
411
            '1dir/0file',
 
412
            '1dir/1dir/',
 
413
            '2file'
 
414
            ]
 
415
        self.build_tree(tree)
 
416
        expected_dirblocks = [
 
417
                (('', '.'),
 
418
                 [('0file', '0file', 'file'),
 
419
                  ('1dir', '1dir', 'directory'),
 
420
                  ('2file', '2file', 'file'),
 
421
                 ]
 
422
                ),
 
423
                (('1dir', './1dir'),
 
424
                 [('1dir/0file', '0file', 'file'),
 
425
                  ('1dir/1dir', '1dir', 'directory'),
 
426
                 ]
 
427
                ),
 
428
                (('1dir/1dir', './1dir/1dir'),
 
429
                 [
 
430
                 ]
 
431
                ),
 
432
            ]
 
433
        result = []
 
434
        found_bzrdir = False
 
435
        for dirdetail, dirblock in osutils.walkdirs('.'):
 
436
            if len(dirblock) and dirblock[0][1] == '.bzr':
 
437
                # this tests the filtering of selected paths
 
438
                found_bzrdir = True
 
439
                del dirblock[0]
 
440
            result.append((dirdetail, dirblock))
 
441
 
 
442
        self.assertTrue(found_bzrdir)
 
443
        self.assertEqual(expected_dirblocks,
 
444
            [(dirinfo, [line[0:3] for line in block]) for dirinfo, block in result])
 
445
        # you can search a subdir only, with a supplied prefix.
 
446
        result = []
 
447
        for dirblock in osutils.walkdirs('./1dir', '1dir'):
 
448
            result.append(dirblock)
 
449
        self.assertEqual(expected_dirblocks[1:],
 
450
            [(dirinfo, [line[0:3] for line in block]) for dirinfo, block in result])
 
451
 
 
452
    def assertPathCompare(self, path_less, path_greater):
 
453
        """check that path_less and path_greater compare correctly."""
 
454
        self.assertEqual(0, osutils.compare_paths_prefix_order(
 
455
            path_less, path_less))
 
456
        self.assertEqual(0, osutils.compare_paths_prefix_order(
 
457
            path_greater, path_greater))
 
458
        self.assertEqual(-1, osutils.compare_paths_prefix_order(
 
459
            path_less, path_greater))
 
460
        self.assertEqual(1, osutils.compare_paths_prefix_order(
 
461
            path_greater, path_less))
 
462
 
 
463
    def test_compare_paths_prefix_order(self):
 
464
        # root before all else
 
465
        self.assertPathCompare("/", "/a")
 
466
        # alpha within a dir
 
467
        self.assertPathCompare("/a", "/b")
 
468
        self.assertPathCompare("/b", "/z")
 
469
        # high dirs before lower.
 
470
        self.assertPathCompare("/z", "/a/a")
 
471
        # except if the deeper dir should be output first
 
472
        self.assertPathCompare("/a/b/c", "/d/g")
 
473
        # lexical betwen dirs of the same height
 
474
        self.assertPathCompare("/a/z", "/z/z")
 
475
        self.assertPathCompare("/a/c/z", "/a/d/e")
 
476
 
 
477
        # this should also be consistent for no leading / paths
 
478
        # root before all else
 
479
        self.assertPathCompare("", "a")
 
480
        # alpha within a dir
 
481
        self.assertPathCompare("a", "b")
 
482
        self.assertPathCompare("b", "z")
 
483
        # high dirs before lower.
 
484
        self.assertPathCompare("z", "a/a")
 
485
        # except if the deeper dir should be output first
 
486
        self.assertPathCompare("a/b/c", "d/g")
 
487
        # lexical betwen dirs of the same height
 
488
        self.assertPathCompare("a/z", "z/z")
 
489
        self.assertPathCompare("a/c/z", "a/d/e")
 
490
 
 
491
    def test_path_prefix_sorting(self):
 
492
        """Doing a sort on path prefix should match our sample data."""
 
493
        original_paths = [
 
494
            'a',
 
495
            'a/b',
 
496
            'a/b/c',
 
497
            'b',
 
498
            'b/c',
 
499
            'd',
 
500
            'd/e',
 
501
            'd/e/f',
 
502
            'd/f',
 
503
            'd/g',
 
504
            'g',
 
505
            ]
 
506
 
 
507
        dir_sorted_paths = [
 
508
            'a',
 
509
            'b',
 
510
            'd',
 
511
            'g',
 
512
            'a/b',
 
513
            'a/b/c',
 
514
            'b/c',
 
515
            'd/e',
 
516
            'd/f',
 
517
            'd/g',
 
518
            'd/e/f',
 
519
            ]
 
520
 
 
521
        self.assertEqual(
 
522
            dir_sorted_paths,
 
523
            sorted(original_paths, key=osutils.path_prefix_key))
 
524
        # using the comparison routine shoudl work too:
 
525
        self.assertEqual(
 
526
            dir_sorted_paths,
 
527
            sorted(original_paths, cmp=osutils.compare_paths_prefix_order))
 
528
 
 
529
 
 
530
class TestCopyTree(TestCaseInTempDir):
 
531
    
 
532
    def test_copy_basic_tree(self):
 
533
        self.build_tree(['source/', 'source/a', 'source/b/', 'source/b/c'])
 
534
        osutils.copy_tree('source', 'target')
 
535
        self.assertEqual(['a', 'b'], sorted(os.listdir('target')))
 
536
        self.assertEqual(['c'], os.listdir('target/b'))
 
537
 
 
538
    def test_copy_tree_target_exists(self):
 
539
        self.build_tree(['source/', 'source/a', 'source/b/', 'source/b/c',
 
540
                         'target/'])
 
541
        osutils.copy_tree('source', 'target')
 
542
        self.assertEqual(['a', 'b'], sorted(os.listdir('target')))
 
543
        self.assertEqual(['c'], os.listdir('target/b'))
 
544
 
 
545
    def test_copy_tree_symlinks(self):
 
546
        if not osutils.has_symlinks():
 
547
            return
 
548
        self.build_tree(['source/'])
 
549
        os.symlink('a/generic/path', 'source/lnk')
 
550
        osutils.copy_tree('source', 'target')
 
551
        self.assertEqual(['lnk'], os.listdir('target'))
 
552
        self.assertEqual('a/generic/path', os.readlink('target/lnk'))
 
553
 
 
554
    def test_copy_tree_handlers(self):
 
555
        processed_files = []
 
556
        processed_links = []
 
557
        def file_handler(from_path, to_path):
 
558
            processed_files.append(('f', from_path, to_path))
 
559
        def dir_handler(from_path, to_path):
 
560
            processed_files.append(('d', from_path, to_path))
 
561
        def link_handler(from_path, to_path):
 
562
            processed_links.append((from_path, to_path))
 
563
        handlers = {'file':file_handler,
 
564
                    'directory':dir_handler,
 
565
                    'symlink':link_handler,
 
566
                   }
 
567
 
 
568
        self.build_tree(['source/', 'source/a', 'source/b/', 'source/b/c'])
 
569
        if osutils.has_symlinks():
 
570
            os.symlink('a/generic/path', 'source/lnk')
 
571
        osutils.copy_tree('source', 'target', handlers=handlers)
 
572
 
 
573
        self.assertEqual([('d', 'source', 'target'),
 
574
                          ('f', 'source/a', 'target/a'),
 
575
                          ('d', 'source/b', 'target/b'),
 
576
                          ('f', 'source/b/c', 'target/b/c'),
 
577
                         ], processed_files)
 
578
        self.failIfExists('target')
 
579
        if osutils.has_symlinks():
 
580
            self.assertEqual([('source/lnk', 'target/lnk')], processed_links)
 
581
 
 
582
 
 
583
class TestTerminalEncoding(TestCase):
 
584
    """Test the auto-detection of proper terminal encoding."""
 
585
 
 
586
    def setUp(self):
 
587
        self._stdout = sys.stdout
 
588
        self._stderr = sys.stderr
 
589
        self._stdin = sys.stdin
 
590
        self._user_encoding = bzrlib.user_encoding
 
591
 
 
592
        self.addCleanup(self._reset)
 
593
 
 
594
        sys.stdout = StringIOWrapper()
 
595
        sys.stdout.encoding = 'stdout_encoding'
 
596
        sys.stderr = StringIOWrapper()
 
597
        sys.stderr.encoding = 'stderr_encoding'
 
598
        sys.stdin = StringIOWrapper()
 
599
        sys.stdin.encoding = 'stdin_encoding'
 
600
        bzrlib.user_encoding = 'user_encoding'
 
601
 
 
602
    def _reset(self):
 
603
        sys.stdout = self._stdout
 
604
        sys.stderr = self._stderr
 
605
        sys.stdin = self._stdin
 
606
        bzrlib.user_encoding = self._user_encoding
 
607
 
 
608
    def test_get_terminal_encoding(self):
 
609
        # first preference is stdout encoding
 
610
        self.assertEqual('stdout_encoding', osutils.get_terminal_encoding())
 
611
 
 
612
        sys.stdout.encoding = None
 
613
        # if sys.stdout is None, fall back to sys.stdin
 
614
        self.assertEqual('stdin_encoding', osutils.get_terminal_encoding())
 
615
 
 
616
        sys.stdin.encoding = None
 
617
        # and in the worst case, use bzrlib.user_encoding
 
618
        self.assertEqual('user_encoding', osutils.get_terminal_encoding())
 
619
 
 
620
 
 
621
class TestSetUnsetEnv(TestCase):
 
622
    """Test updating the environment"""
 
623
 
 
624
    def setUp(self):
 
625
        super(TestSetUnsetEnv, self).setUp()
 
626
 
 
627
        self.assertEqual(None, os.environ.get('BZR_TEST_ENV_VAR'),
 
628
                         'Environment was not cleaned up properly.'
 
629
                         ' Variable BZR_TEST_ENV_VAR should not exist.')
 
630
        def cleanup():
 
631
            if 'BZR_TEST_ENV_VAR' in os.environ:
 
632
                del os.environ['BZR_TEST_ENV_VAR']
 
633
 
 
634
        self.addCleanup(cleanup)
 
635
 
 
636
    def test_set(self):
 
637
        """Test that we can set an env variable"""
 
638
        old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'foo')
 
639
        self.assertEqual(None, old)
 
640
        self.assertEqual('foo', os.environ.get('BZR_TEST_ENV_VAR'))
 
641
 
 
642
    def test_double_set(self):
 
643
        """Test that we get the old value out"""
 
644
        osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'foo')
 
645
        old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'bar')
 
646
        self.assertEqual('foo', old)
 
647
        self.assertEqual('bar', os.environ.get('BZR_TEST_ENV_VAR'))
 
648
 
 
649
    def test_unicode(self):
 
650
        """Environment can only contain plain strings
 
651
        
 
652
        So Unicode strings must be encoded.
 
653
        """
 
654
        # Try a few different characters, to see if we can get
 
655
        # one that will be valid in the user_encoding
 
656
        possible_vals = [u'm\xb5', u'\xe1', u'\u0410']
 
657
        for uni_val in possible_vals:
 
658
            try:
 
659
                env_val = uni_val.encode(bzrlib.user_encoding)
 
660
            except UnicodeEncodeError:
 
661
                # Try a different character
 
662
                pass
 
663
            else:
 
664
                break
 
665
        else:
 
666
            raise TestSkipped('Cannot find a unicode character that works in'
 
667
                              ' encoding %s' % (bzrlib.user_encoding,))
 
668
 
 
669
        old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', uni_val)
 
670
        self.assertEqual(env_val, os.environ.get('BZR_TEST_ENV_VAR'))
 
671
 
 
672
    def test_unset(self):
 
673
        """Test that passing None will remove the env var"""
 
674
        osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'foo')
 
675
        old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', None)
 
676
        self.assertEqual('foo', old)
 
677
        self.assertEqual(None, os.environ.get('BZR_TEST_ENV_VAR'))
 
678
        self.failIf('BZR_TEST_ENV_VAR' in os.environ)
 
679