~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_osutils.py

Added canonical_path function

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
 
1
# Copyright (C) 2005 by Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
 
"""Tests for the osutils wrapper."""
 
17
"""Tests for the osutils wrapper.
 
18
"""
18
19
 
19
 
import errno
20
20
import os
21
 
import socket
22
 
import stat
23
21
import sys
24
22
 
25
23
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
 
        )
 
24
import bzrlib.osutils as osutils
 
25
from bzrlib.tests import TestCaseInTempDir
37
26
 
38
27
 
39
28
class TestOSUtils(TestCaseInTempDir):
69
58
 
70
59
        self.check_file_contents('a', 'something in a\n')
71
60
 
 
61
 
72
62
    # TODO: test fancy_rename using a MemoryTransport
73
63
 
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 has been moved to test_osutils_encodings.py
584
 
# [bialix] 2006/12/26
585
 
 
586
 
 
587
 
class TestSetUnsetEnv(TestCase):
588
 
    """Test updating the environment"""
589
 
 
590
 
    def setUp(self):
591
 
        super(TestSetUnsetEnv, self).setUp()
592
 
 
593
 
        self.assertEqual(None, os.environ.get('BZR_TEST_ENV_VAR'),
594
 
                         'Environment was not cleaned up properly.'
595
 
                         ' Variable BZR_TEST_ENV_VAR should not exist.')
596
 
        def cleanup():
597
 
            if 'BZR_TEST_ENV_VAR' in os.environ:
598
 
                del os.environ['BZR_TEST_ENV_VAR']
599
 
 
600
 
        self.addCleanup(cleanup)
601
 
 
602
 
    def test_set(self):
603
 
        """Test that we can set an env variable"""
604
 
        old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'foo')
605
 
        self.assertEqual(None, old)
606
 
        self.assertEqual('foo', os.environ.get('BZR_TEST_ENV_VAR'))
607
 
 
608
 
    def test_double_set(self):
609
 
        """Test that we get the old value out"""
610
 
        osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'foo')
611
 
        old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'bar')
612
 
        self.assertEqual('foo', old)
613
 
        self.assertEqual('bar', os.environ.get('BZR_TEST_ENV_VAR'))
614
 
 
615
 
    def test_unicode(self):
616
 
        """Environment can only contain plain strings
617
 
        
618
 
        So Unicode strings must be encoded.
619
 
        """
620
 
        # Try a few different characters, to see if we can get
621
 
        # one that will be valid in the user_encoding
622
 
        possible_vals = [u'm\xb5', u'\xe1', u'\u0410']
623
 
        for uni_val in possible_vals:
624
 
            try:
625
 
                env_val = uni_val.encode(bzrlib.user_encoding)
626
 
            except UnicodeEncodeError:
627
 
                # Try a different character
628
 
                pass
629
 
            else:
630
 
                break
631
 
        else:
632
 
            raise TestSkipped('Cannot find a unicode character that works in'
633
 
                              ' encoding %s' % (bzrlib.user_encoding,))
634
 
 
635
 
        old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', uni_val)
636
 
        self.assertEqual(env_val, os.environ.get('BZR_TEST_ENV_VAR'))
637
 
 
638
 
    def test_unset(self):
639
 
        """Test that passing None will remove the env var"""
640
 
        osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'foo')
641
 
        old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', None)
642
 
        self.assertEqual('foo', old)
643
 
        self.assertEqual(None, os.environ.get('BZR_TEST_ENV_VAR'))
644
 
        self.failIf('BZR_TEST_ENV_VAR' in os.environ)
645
 
 
646
 
 
647
 
class TestLocalTimeOffset(TestCase):
648
 
 
649
 
    def test_local_time_offset(self):
650
 
        """Test that local_time_offset() returns a sane value."""
651
 
        offset = osutils.local_time_offset()
652
 
        self.assertTrue(isinstance(offset, int))
653
 
        # Test that the offset is no more than a eighteen hours in
654
 
        # either direction.
655
 
        # Time zone handling is system specific, so it is difficult to
656
 
        # do more specific tests, but a value outside of this range is
657
 
        # probably wrong.
658
 
        eighteen_hours = 18 * 3600
659
 
        self.assertTrue(-eighteen_hours < offset < eighteen_hours)
660
 
 
661
 
    def test_local_time_offset_with_timestamp(self):
662
 
        """Test that local_time_offset() works with a timestamp."""
663
 
        offset = osutils.local_time_offset(1000000000.1234567)
664
 
        self.assertTrue(isinstance(offset, int))
665
 
        eighteen_hours = 18 * 3600
666
 
        self.assertTrue(-eighteen_hours < offset < eighteen_hours)