~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_osutils.py

  • Committer: Martin Pool
  • Date: 2005-06-27 08:18:07 UTC
  • mto: This revision was merged to the branch mainline in revision 852.
  • Revision ID: mbp@sourcefrog.net-20050627081807-dc3ff5726c88b247
More tests for insertion of lines in new versions.

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