~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_osutils.py

  • Committer: Robert Collins
  • Date: 2005-10-18 13:11:57 UTC
  • mfrom: (1185.16.72) (0.2.1)
  • Revision ID: robertc@robertcollins.net-20051018131157-76a9970aa78e927e
Merged Martin.

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 TestSafeUtf8(TestCase):
250
 
 
251
 
    def test_from_ascii_string(self):
252
 
        f = 'foobar'
253
 
        self.assertEqual('foobar', osutils.safe_utf8(f))
254
 
 
255
 
    def test_from_unicode_string_ascii_contents(self):
256
 
        self.assertEqual('bargam', osutils.safe_utf8(u'bargam'))
257
 
 
258
 
    def test_from_unicode_string_unicode_contents(self):
259
 
        self.assertEqual('bargam\xc2\xae', osutils.safe_utf8(u'bargam\xae'))
260
 
 
261
 
    def test_from_utf8_string(self):
262
 
        self.assertEqual('foo\xc2\xae', osutils.safe_utf8('foo\xc2\xae'))
263
 
 
264
 
    def test_bad_utf8_string(self):
265
 
        self.assertRaises(BzrBadParameterNotUnicode,
266
 
                          osutils.safe_utf8, '\xbb\xbb')
267
 
 
268
 
 
269
 
class TestSafeRevisionId(TestCase):
270
 
 
271
 
    def test_from_ascii_string(self):
272
 
        f = 'foobar'
273
 
        self.assertEqual('foobar', osutils.safe_revision_id(f))
274
 
        self.assertIs(osutils.safe_utf8(f), f)
275
 
 
276
 
    def test_from_unicode_string_ascii_contents(self):
277
 
        self.assertEqual('bargam', osutils.safe_revision_id(u'bargam'))
278
 
 
279
 
    def test_from_unicode_string_unicode_contents(self):
280
 
        self.assertEqual('bargam\xc2\xae',
281
 
                         osutils.safe_revision_id(u'bargam\xae'))
282
 
 
283
 
    def test_from_utf8_string(self):
284
 
        self.assertEqual('foo\xc2\xae',
285
 
                         osutils.safe_revision_id('foo\xc2\xae'))
286
 
 
287
 
    def test_bad_utf8_string(self):
288
 
        # This check may eventually go away
289
 
        self.assertRaises(BzrBadParameterNotUnicode,
290
 
                          osutils.safe_utf8, '\xbb\xbb')
291
 
 
292
 
    def test_none(self):
293
 
        """Currently, None is a valid revision_id"""
294
 
        self.assertEqual(None, osutils.safe_revision_id(None))
295
 
 
296
 
 
297
 
class TestWin32Funcs(TestCase):
298
 
    """Test that the _win32 versions of os utilities return appropriate paths."""
299
 
 
300
 
    def test_abspath(self):
301
 
        self.assertEqual('C:/foo', osutils._win32_abspath('C:\\foo'))
302
 
        self.assertEqual('C:/foo', osutils._win32_abspath('C:/foo'))
303
 
 
304
 
    def test_realpath(self):
305
 
        self.assertEqual('C:/foo', osutils._win32_realpath('C:\\foo'))
306
 
        self.assertEqual('C:/foo', osutils._win32_realpath('C:/foo'))
307
 
 
308
 
    def test_pathjoin(self):
309
 
        self.assertEqual('path/to/foo', osutils._win32_pathjoin('path', 'to', 'foo'))
310
 
        self.assertEqual('C:/foo', osutils._win32_pathjoin('path\\to', 'C:\\foo'))
311
 
        self.assertEqual('C:/foo', osutils._win32_pathjoin('path/to', 'C:/foo'))
312
 
        self.assertEqual('path/to/foo', osutils._win32_pathjoin('path/to/', 'foo'))
313
 
        self.assertEqual('/foo', osutils._win32_pathjoin('C:/path/to/', '/foo'))
314
 
        self.assertEqual('/foo', osutils._win32_pathjoin('C:\\path\\to\\', '\\foo'))
315
 
 
316
 
    def test_normpath(self):
317
 
        self.assertEqual('path/to/foo', osutils._win32_normpath(r'path\\from\..\to\.\foo'))
318
 
        self.assertEqual('path/to/foo', osutils._win32_normpath('path//from/../to/./foo'))
319
 
 
320
 
    def test_getcwd(self):
321
 
        cwd = osutils._win32_getcwd()
322
 
        os_cwd = os.getcwdu()
323
 
        self.assertEqual(os_cwd[1:].replace('\\', '/'), cwd[1:])
324
 
        # win32 is inconsistent whether it returns lower or upper case
325
 
        # and even if it was consistent the user might type the other
326
 
        # so we force it to uppercase
327
 
        # running python.exe under cmd.exe return capital C:\\
328
 
        # running win32 python inside a cygwin shell returns lowercase
329
 
        self.assertEqual(os_cwd[0].upper(), cwd[0])
330
 
 
331
 
    def test_fixdrive(self):
332
 
        self.assertEqual('H:/foo', osutils._win32_fixdrive('h:/foo'))
333
 
        self.assertEqual('H:/foo', osutils._win32_fixdrive('H:/foo'))
334
 
        self.assertEqual('C:\\foo', osutils._win32_fixdrive('c:\\foo'))
335
 
 
336
 
 
337
 
class TestWin32FuncsDirs(TestCaseInTempDir):
338
 
    """Test win32 functions that create files."""
339
 
    
340
 
    def test_getcwd(self):
341
 
        # Make sure getcwd can handle unicode filenames
342
 
        try:
343
 
            os.mkdir(u'mu-\xb5')
344
 
        except UnicodeError:
345
 
            raise TestSkipped("Unable to create Unicode filename")
346
 
 
347
 
        os.chdir(u'mu-\xb5')
348
 
        # TODO: jam 20060427 This will probably fail on Mac OSX because
349
 
        #       it will change the normalization of B\xe5gfors
350
 
        #       Consider using a different unicode character, or make
351
 
        #       osutils.getcwd() renormalize the path.
352
 
        self.assertEndsWith(osutils._win32_getcwd(), u'mu-\xb5')
353
 
 
354
 
    def test_mkdtemp(self):
355
 
        tmpdir = osutils._win32_mkdtemp(dir='.')
356
 
        self.assertFalse('\\' in tmpdir)
357
 
 
358
 
    def test_rename(self):
359
 
        a = open('a', 'wb')
360
 
        a.write('foo\n')
361
 
        a.close()
362
 
        b = open('b', 'wb')
363
 
        b.write('baz\n')
364
 
        b.close()
365
 
 
366
 
        osutils._win32_rename('b', 'a')
367
 
        self.failUnlessExists('a')
368
 
        self.failIfExists('b')
369
 
        self.assertFileEqual('baz\n', 'a')
370
 
 
371
 
    def test_rename_missing_file(self):
372
 
        a = open('a', 'wb')
373
 
        a.write('foo\n')
374
 
        a.close()
375
 
 
376
 
        try:
377
 
            osutils._win32_rename('b', 'a')
378
 
        except (IOError, OSError), e:
379
 
            self.assertEqual(errno.ENOENT, e.errno)
380
 
        self.assertFileEqual('foo\n', 'a')
381
 
 
382
 
    def test_rename_missing_dir(self):
383
 
        os.mkdir('a')
384
 
        try:
385
 
            osutils._win32_rename('b', 'a')
386
 
        except (IOError, OSError), e:
387
 
            self.assertEqual(errno.ENOENT, e.errno)
388
 
 
389
 
    def test_rename_current_dir(self):
390
 
        os.mkdir('a')
391
 
        os.chdir('a')
392
 
        # You can't rename the working directory
393
 
        # doing rename non-existant . usually
394
 
        # just raises ENOENT, since non-existant
395
 
        # doesn't exist.
396
 
        try:
397
 
            osutils._win32_rename('b', '.')
398
 
        except (IOError, OSError), e:
399
 
            self.assertEqual(errno.ENOENT, e.errno)
400
 
 
401
 
    def test_splitpath(self):
402
 
        def check(expected, path):
403
 
            self.assertEqual(expected, osutils.splitpath(path))
404
 
 
405
 
        check(['a'], 'a')
406
 
        check(['a', 'b'], 'a/b')
407
 
        check(['a', 'b'], 'a/./b')
408
 
        check(['a', '.b'], 'a/.b')
409
 
        check(['a', '.b'], 'a\\.b')
410
 
 
411
 
        self.assertRaises(errors.BzrError, osutils.splitpath, 'a/../b')
412
 
 
413
 
 
414
 
class TestMacFuncsDirs(TestCaseInTempDir):
415
 
    """Test mac special functions that require directories."""
416
 
 
417
 
    def test_getcwd(self):
418
 
        # On Mac, this will actually create Ba\u030agfors
419
 
        # but chdir will still work, because it accepts both paths
420
 
        try:
421
 
            os.mkdir(u'B\xe5gfors')
422
 
        except UnicodeError:
423
 
            raise TestSkipped("Unable to create Unicode filename")
424
 
 
425
 
        os.chdir(u'B\xe5gfors')
426
 
        self.assertEndsWith(osutils._mac_getcwd(), u'B\xe5gfors')
427
 
 
428
 
    def test_getcwd_nonnorm(self):
429
 
        # Test that _mac_getcwd() will normalize this path
430
 
        try:
431
 
            os.mkdir(u'Ba\u030agfors')
432
 
        except UnicodeError:
433
 
            raise TestSkipped("Unable to create Unicode filename")
434
 
 
435
 
        os.chdir(u'Ba\u030agfors')
436
 
        self.assertEndsWith(osutils._mac_getcwd(), u'B\xe5gfors')
437
 
 
438
 
 
439
 
class TestSplitLines(TestCase):
440
 
 
441
 
    def test_split_unicode(self):
442
 
        self.assertEqual([u'foo\n', u'bar\xae'],
443
 
                         osutils.split_lines(u'foo\nbar\xae'))
444
 
        self.assertEqual([u'foo\n', u'bar\xae\n'],
445
 
                         osutils.split_lines(u'foo\nbar\xae\n'))
446
 
 
447
 
    def test_split_with_carriage_returns(self):
448
 
        self.assertEqual(['foo\rbar\n'],
449
 
                         osutils.split_lines('foo\rbar\n'))
450
 
 
451
 
 
452
 
class TestWalkDirs(TestCaseInTempDir):
453
 
 
454
 
    def test_walkdirs(self):
455
 
        tree = [
456
 
            '.bzr',
457
 
            '0file',
458
 
            '1dir/',
459
 
            '1dir/0file',
460
 
            '1dir/1dir/',
461
 
            '2file'
462
 
            ]
463
 
        self.build_tree(tree)
464
 
        expected_dirblocks = [
465
 
                (('', '.'),
466
 
                 [('0file', '0file', 'file'),
467
 
                  ('1dir', '1dir', 'directory'),
468
 
                  ('2file', '2file', 'file'),
469
 
                 ]
470
 
                ),
471
 
                (('1dir', './1dir'),
472
 
                 [('1dir/0file', '0file', 'file'),
473
 
                  ('1dir/1dir', '1dir', 'directory'),
474
 
                 ]
475
 
                ),
476
 
                (('1dir/1dir', './1dir/1dir'),
477
 
                 [
478
 
                 ]
479
 
                ),
480
 
            ]
481
 
        result = []
482
 
        found_bzrdir = False
483
 
        for dirdetail, dirblock in osutils.walkdirs('.'):
484
 
            if len(dirblock) and dirblock[0][1] == '.bzr':
485
 
                # this tests the filtering of selected paths
486
 
                found_bzrdir = True
487
 
                del dirblock[0]
488
 
            result.append((dirdetail, dirblock))
489
 
 
490
 
        self.assertTrue(found_bzrdir)
491
 
        self.assertEqual(expected_dirblocks,
492
 
            [(dirinfo, [line[0:3] for line in block]) for dirinfo, block in result])
493
 
        # you can search a subdir only, with a supplied prefix.
494
 
        result = []
495
 
        for dirblock in osutils.walkdirs('./1dir', '1dir'):
496
 
            result.append(dirblock)
497
 
        self.assertEqual(expected_dirblocks[1:],
498
 
            [(dirinfo, [line[0:3] for line in block]) for dirinfo, block in result])
499
 
 
500
 
    def assertPathCompare(self, path_less, path_greater):
501
 
        """check that path_less and path_greater compare correctly."""
502
 
        self.assertEqual(0, osutils.compare_paths_prefix_order(
503
 
            path_less, path_less))
504
 
        self.assertEqual(0, osutils.compare_paths_prefix_order(
505
 
            path_greater, path_greater))
506
 
        self.assertEqual(-1, osutils.compare_paths_prefix_order(
507
 
            path_less, path_greater))
508
 
        self.assertEqual(1, osutils.compare_paths_prefix_order(
509
 
            path_greater, path_less))
510
 
 
511
 
    def test_compare_paths_prefix_order(self):
512
 
        # root before all else
513
 
        self.assertPathCompare("/", "/a")
514
 
        # alpha within a dir
515
 
        self.assertPathCompare("/a", "/b")
516
 
        self.assertPathCompare("/b", "/z")
517
 
        # high dirs before lower.
518
 
        self.assertPathCompare("/z", "/a/a")
519
 
        # except if the deeper dir should be output first
520
 
        self.assertPathCompare("/a/b/c", "/d/g")
521
 
        # lexical betwen dirs of the same height
522
 
        self.assertPathCompare("/a/z", "/z/z")
523
 
        self.assertPathCompare("/a/c/z", "/a/d/e")
524
 
 
525
 
        # this should also be consistent for no leading / paths
526
 
        # root before all else
527
 
        self.assertPathCompare("", "a")
528
 
        # alpha within a dir
529
 
        self.assertPathCompare("a", "b")
530
 
        self.assertPathCompare("b", "z")
531
 
        # high dirs before lower.
532
 
        self.assertPathCompare("z", "a/a")
533
 
        # except if the deeper dir should be output first
534
 
        self.assertPathCompare("a/b/c", "d/g")
535
 
        # lexical betwen dirs of the same height
536
 
        self.assertPathCompare("a/z", "z/z")
537
 
        self.assertPathCompare("a/c/z", "a/d/e")
538
 
 
539
 
    def test_path_prefix_sorting(self):
540
 
        """Doing a sort on path prefix should match our sample data."""
541
 
        original_paths = [
542
 
            'a',
543
 
            'a/b',
544
 
            'a/b/c',
545
 
            'b',
546
 
            'b/c',
547
 
            'd',
548
 
            'd/e',
549
 
            'd/e/f',
550
 
            'd/f',
551
 
            'd/g',
552
 
            'g',
553
 
            ]
554
 
 
555
 
        dir_sorted_paths = [
556
 
            'a',
557
 
            'b',
558
 
            'd',
559
 
            'g',
560
 
            'a/b',
561
 
            'a/b/c',
562
 
            'b/c',
563
 
            'd/e',
564
 
            'd/f',
565
 
            'd/g',
566
 
            'd/e/f',
567
 
            ]
568
 
 
569
 
        self.assertEqual(
570
 
            dir_sorted_paths,
571
 
            sorted(original_paths, key=osutils.path_prefix_key))
572
 
        # using the comparison routine shoudl work too:
573
 
        self.assertEqual(
574
 
            dir_sorted_paths,
575
 
            sorted(original_paths, cmp=osutils.compare_paths_prefix_order))
576
 
 
577
 
 
578
 
class TestCopyTree(TestCaseInTempDir):
579
 
    
580
 
    def test_copy_basic_tree(self):
581
 
        self.build_tree(['source/', 'source/a', 'source/b/', 'source/b/c'])
582
 
        osutils.copy_tree('source', 'target')
583
 
        self.assertEqual(['a', 'b'], sorted(os.listdir('target')))
584
 
        self.assertEqual(['c'], os.listdir('target/b'))
585
 
 
586
 
    def test_copy_tree_target_exists(self):
587
 
        self.build_tree(['source/', 'source/a', 'source/b/', 'source/b/c',
588
 
                         'target/'])
589
 
        osutils.copy_tree('source', 'target')
590
 
        self.assertEqual(['a', 'b'], sorted(os.listdir('target')))
591
 
        self.assertEqual(['c'], os.listdir('target/b'))
592
 
 
593
 
    def test_copy_tree_symlinks(self):
594
 
        if not osutils.has_symlinks():
595
 
            return
596
 
        self.build_tree(['source/'])
597
 
        os.symlink('a/generic/path', 'source/lnk')
598
 
        osutils.copy_tree('source', 'target')
599
 
        self.assertEqual(['lnk'], os.listdir('target'))
600
 
        self.assertEqual('a/generic/path', os.readlink('target/lnk'))
601
 
 
602
 
    def test_copy_tree_handlers(self):
603
 
        processed_files = []
604
 
        processed_links = []
605
 
        def file_handler(from_path, to_path):
606
 
            processed_files.append(('f', from_path, to_path))
607
 
        def dir_handler(from_path, to_path):
608
 
            processed_files.append(('d', from_path, to_path))
609
 
        def link_handler(from_path, to_path):
610
 
            processed_links.append((from_path, to_path))
611
 
        handlers = {'file':file_handler,
612
 
                    'directory':dir_handler,
613
 
                    'symlink':link_handler,
614
 
                   }
615
 
 
616
 
        self.build_tree(['source/', 'source/a', 'source/b/', 'source/b/c'])
617
 
        if osutils.has_symlinks():
618
 
            os.symlink('a/generic/path', 'source/lnk')
619
 
        osutils.copy_tree('source', 'target', handlers=handlers)
620
 
 
621
 
        self.assertEqual([('d', 'source', 'target'),
622
 
                          ('f', 'source/a', 'target/a'),
623
 
                          ('d', 'source/b', 'target/b'),
624
 
                          ('f', 'source/b/c', 'target/b/c'),
625
 
                         ], processed_files)
626
 
        self.failIfExists('target')
627
 
        if osutils.has_symlinks():
628
 
            self.assertEqual([('source/lnk', 'target/lnk')], processed_links)
629
 
 
630
 
 
631
 
#class TestTerminalEncoding has been moved to test_osutils_encodings.py
632
 
# [bialix] 2006/12/26
633
 
 
634
 
 
635
 
class TestSetUnsetEnv(TestCase):
636
 
    """Test updating the environment"""
637
 
 
638
 
    def setUp(self):
639
 
        super(TestSetUnsetEnv, self).setUp()
640
 
 
641
 
        self.assertEqual(None, os.environ.get('BZR_TEST_ENV_VAR'),
642
 
                         'Environment was not cleaned up properly.'
643
 
                         ' Variable BZR_TEST_ENV_VAR should not exist.')
644
 
        def cleanup():
645
 
            if 'BZR_TEST_ENV_VAR' in os.environ:
646
 
                del os.environ['BZR_TEST_ENV_VAR']
647
 
 
648
 
        self.addCleanup(cleanup)
649
 
 
650
 
    def test_set(self):
651
 
        """Test that we can set an env variable"""
652
 
        old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'foo')
653
 
        self.assertEqual(None, old)
654
 
        self.assertEqual('foo', os.environ.get('BZR_TEST_ENV_VAR'))
655
 
 
656
 
    def test_double_set(self):
657
 
        """Test that we get the old value out"""
658
 
        osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'foo')
659
 
        old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'bar')
660
 
        self.assertEqual('foo', old)
661
 
        self.assertEqual('bar', os.environ.get('BZR_TEST_ENV_VAR'))
662
 
 
663
 
    def test_unicode(self):
664
 
        """Environment can only contain plain strings
665
 
        
666
 
        So Unicode strings must be encoded.
667
 
        """
668
 
        # Try a few different characters, to see if we can get
669
 
        # one that will be valid in the user_encoding
670
 
        possible_vals = [u'm\xb5', u'\xe1', u'\u0410']
671
 
        for uni_val in possible_vals:
672
 
            try:
673
 
                env_val = uni_val.encode(bzrlib.user_encoding)
674
 
            except UnicodeEncodeError:
675
 
                # Try a different character
676
 
                pass
677
 
            else:
678
 
                break
679
 
        else:
680
 
            raise TestSkipped('Cannot find a unicode character that works in'
681
 
                              ' encoding %s' % (bzrlib.user_encoding,))
682
 
 
683
 
        old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', uni_val)
684
 
        self.assertEqual(env_val, os.environ.get('BZR_TEST_ENV_VAR'))
685
 
 
686
 
    def test_unset(self):
687
 
        """Test that passing None will remove the env var"""
688
 
        osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'foo')
689
 
        old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', None)
690
 
        self.assertEqual('foo', old)
691
 
        self.assertEqual(None, os.environ.get('BZR_TEST_ENV_VAR'))
692
 
        self.failIf('BZR_TEST_ENV_VAR' in os.environ)
693
 
 
694
 
 
695
 
class TestLocalTimeOffset(TestCase):
696
 
 
697
 
    def test_local_time_offset(self):
698
 
        """Test that local_time_offset() returns a sane value."""
699
 
        offset = osutils.local_time_offset()
700
 
        self.assertTrue(isinstance(offset, int))
701
 
        # Test that the offset is no more than a eighteen hours in
702
 
        # either direction.
703
 
        # Time zone handling is system specific, so it is difficult to
704
 
        # do more specific tests, but a value outside of this range is
705
 
        # probably wrong.
706
 
        eighteen_hours = 18 * 3600
707
 
        self.assertTrue(-eighteen_hours < offset < eighteen_hours)
708
 
 
709
 
    def test_local_time_offset_with_timestamp(self):
710
 
        """Test that local_time_offset() works with a timestamp."""
711
 
        offset = osutils.local_time_offset(1000000000.1234567)
712
 
        self.assertTrue(isinstance(offset, int))
713
 
        eighteen_hours = 18 * 3600
714
 
        self.assertTrue(-eighteen_hours < offset < eighteen_hours)