1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
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.
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.
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
17
"""Tests for the osutils wrapper."""
31
from bzrlib.errors import BzrBadParameterNotUnicode, InvalidURL
32
from bzrlib.osutils import (
34
is_inside_or_parent_of_any,
37
from bzrlib.tests import (
45
class TestOSUtils(TestCaseInTempDir):
47
def test_contains_whitespace(self):
48
self.failUnless(osutils.contains_whitespace(u' '))
49
self.failUnless(osutils.contains_whitespace(u'hello there'))
50
self.failUnless(osutils.contains_whitespace(u'hellothere\n'))
51
self.failUnless(osutils.contains_whitespace(u'hello\nthere'))
52
self.failUnless(osutils.contains_whitespace(u'hello\rthere'))
53
self.failUnless(osutils.contains_whitespace(u'hello\tthere'))
55
# \xa0 is "Non-breaking-space" which on some python locales thinks it
56
# is whitespace, but we do not.
57
self.failIf(osutils.contains_whitespace(u''))
58
self.failIf(osutils.contains_whitespace(u'hellothere'))
59
self.failIf(osutils.contains_whitespace(u'hello\xa0there'))
61
def test_fancy_rename(self):
62
# This should work everywhere
64
osutils.fancy_rename(a, b,
65
rename_func=os.rename,
66
unlink_func=os.unlink)
68
open('a', 'wb').write('something in a\n')
70
self.failIfExists('a')
71
self.failUnlessExists('b')
72
self.check_file_contents('b', 'something in a\n')
74
open('a', 'wb').write('new something in a\n')
77
self.check_file_contents('a', 'something in a\n')
79
def test_rename(self):
80
# Rename should be semi-atomic on all platforms
81
open('a', 'wb').write('something in a\n')
82
osutils.rename('a', 'b')
83
self.failIfExists('a')
84
self.failUnlessExists('b')
85
self.check_file_contents('b', 'something in a\n')
87
open('a', 'wb').write('new something in a\n')
88
osutils.rename('b', 'a')
90
self.check_file_contents('a', 'something in a\n')
92
# TODO: test fancy_rename using a MemoryTransport
94
def test_01_rand_chars_empty(self):
95
result = osutils.rand_chars(0)
96
self.assertEqual(result, '')
98
def test_02_rand_chars_100(self):
99
result = osutils.rand_chars(100)
100
self.assertEqual(len(result), 100)
101
self.assertEqual(type(result), str)
102
self.assertContainsRe(result, r'^[a-z0-9]{100}$')
104
def test_is_inside(self):
105
is_inside = osutils.is_inside
106
self.assertTrue(is_inside('src', 'src/foo.c'))
107
self.assertFalse(is_inside('src', 'srccontrol'))
108
self.assertTrue(is_inside('src', 'src/a/a/a/foo.c'))
109
self.assertTrue(is_inside('foo.c', 'foo.c'))
110
self.assertFalse(is_inside('foo.c', ''))
111
self.assertTrue(is_inside('', 'foo.c'))
113
def test_is_inside_any(self):
114
SRC_FOO_C = pathjoin('src', 'foo.c')
115
for dirs, fn in [(['src', 'doc'], SRC_FOO_C),
116
(['src'], SRC_FOO_C),
119
self.assert_(is_inside_any(dirs, fn))
120
for dirs, fn in [(['src'], 'srccontrol'),
121
(['src'], 'srccontrol/foo')]:
122
self.assertFalse(is_inside_any(dirs, fn))
124
def test_is_inside_or_parent_of_any(self):
125
for dirs, fn in [(['src', 'doc'], 'src/foo.c'),
126
(['src'], 'src/foo.c'),
127
(['src/bar.c'], 'src'),
128
(['src/bar.c', 'bla/foo.c'], 'src'),
131
self.assert_(is_inside_or_parent_of_any(dirs, fn))
133
for dirs, fn in [(['src'], 'srccontrol'),
134
(['srccontrol/foo.c'], 'src'),
135
(['src'], 'srccontrol/foo')]:
136
self.assertFalse(is_inside_or_parent_of_any(dirs, fn))
138
def test_rmtree(self):
139
# Check to remove tree with read-only files/dirs
141
f = file('dir/file', 'w')
144
# would like to also try making the directory readonly, but at the
145
# moment python shutil.rmtree doesn't handle that properly - it would
146
# need to chmod the directory before removing things inside it - deferred
147
# for now -- mbp 20060505
148
# osutils.make_readonly('dir')
149
osutils.make_readonly('dir/file')
151
osutils.rmtree('dir')
153
self.failIfExists('dir/file')
154
self.failIfExists('dir')
156
def test_file_kind(self):
157
self.build_tree(['file', 'dir/'])
158
self.assertEquals('file', osutils.file_kind('file'))
159
self.assertEquals('directory', osutils.file_kind('dir/'))
160
if osutils.has_symlinks():
161
os.symlink('symlink', 'symlink')
162
self.assertEquals('symlink', osutils.file_kind('symlink'))
164
# TODO: jam 20060529 Test a block device
166
os.lstat('/dev/null')
168
if e.errno not in (errno.ENOENT,):
171
self.assertEquals('chardev', osutils.file_kind('/dev/null'))
173
mkfifo = getattr(os, 'mkfifo', None)
177
self.assertEquals('fifo', osutils.file_kind('fifo'))
181
AF_UNIX = getattr(socket, 'AF_UNIX', None)
183
s = socket.socket(AF_UNIX)
186
self.assertEquals('socket', osutils.file_kind('socket'))
190
def test_kind_marker(self):
191
self.assertEqual(osutils.kind_marker('file'), '')
192
self.assertEqual(osutils.kind_marker('directory'), '/')
193
self.assertEqual(osutils.kind_marker('symlink'), '@')
194
self.assertEqual(osutils.kind_marker('tree-reference'), '+')
196
def test_get_umask(self):
197
if sys.platform == 'win32':
198
# umask always returns '0', no way to set it
199
self.assertEqual(0, osutils.get_umask())
202
orig_umask = osutils.get_umask()
205
self.assertEqual(0222, osutils.get_umask())
207
self.assertEqual(0022, osutils.get_umask())
209
self.assertEqual(0002, osutils.get_umask())
211
self.assertEqual(0027, osutils.get_umask())
215
def assertFormatedDelta(self, expected, seconds):
216
"""Assert osutils.format_delta formats as expected"""
217
actual = osutils.format_delta(seconds)
218
self.assertEqual(expected, actual)
220
def test_format_delta(self):
221
self.assertFormatedDelta('0 seconds ago', 0)
222
self.assertFormatedDelta('1 second ago', 1)
223
self.assertFormatedDelta('10 seconds ago', 10)
224
self.assertFormatedDelta('59 seconds ago', 59)
225
self.assertFormatedDelta('89 seconds ago', 89)
226
self.assertFormatedDelta('1 minute, 30 seconds ago', 90)
227
self.assertFormatedDelta('3 minutes, 0 seconds ago', 180)
228
self.assertFormatedDelta('3 minutes, 1 second ago', 181)
229
self.assertFormatedDelta('10 minutes, 15 seconds ago', 615)
230
self.assertFormatedDelta('30 minutes, 59 seconds ago', 1859)
231
self.assertFormatedDelta('31 minutes, 0 seconds ago', 1860)
232
self.assertFormatedDelta('60 minutes, 0 seconds ago', 3600)
233
self.assertFormatedDelta('89 minutes, 59 seconds ago', 5399)
234
self.assertFormatedDelta('1 hour, 30 minutes ago', 5400)
235
self.assertFormatedDelta('2 hours, 30 minutes ago', 9017)
236
self.assertFormatedDelta('10 hours, 0 minutes ago', 36000)
237
self.assertFormatedDelta('24 hours, 0 minutes ago', 86400)
238
self.assertFormatedDelta('35 hours, 59 minutes ago', 129599)
239
self.assertFormatedDelta('36 hours, 0 minutes ago', 129600)
240
self.assertFormatedDelta('36 hours, 0 minutes ago', 129601)
241
self.assertFormatedDelta('36 hours, 1 minute ago', 129660)
242
self.assertFormatedDelta('36 hours, 1 minute ago', 129661)
243
self.assertFormatedDelta('84 hours, 10 minutes ago', 303002)
245
# We handle when time steps the wrong direction because computers
246
# don't have synchronized clocks.
247
self.assertFormatedDelta('84 hours, 10 minutes in the future', -303002)
248
self.assertFormatedDelta('1 second in the future', -1)
249
self.assertFormatedDelta('2 seconds in the future', -2)
251
def test_dereference_path(self):
252
if not osutils.has_symlinks():
253
raise TestSkipped('Symlinks are not supported on this platform')
254
cwd = osutils.realpath('.')
256
bar_path = osutils.pathjoin(cwd, 'bar')
257
# Using './' to avoid bug #1213894 (first path component not
258
# dereferenced) in Python 2.4.1 and earlier
259
self.assertEqual(bar_path, osutils.realpath('./bar'))
260
os.symlink('bar', 'foo')
261
self.assertEqual(bar_path, osutils.realpath('./foo'))
263
# Does not dereference terminal symlinks
264
foo_path = osutils.pathjoin(cwd, 'foo')
265
self.assertEqual(foo_path, osutils.dereference_path('./foo'))
267
# Dereferences parent symlinks
269
baz_path = osutils.pathjoin(bar_path, 'baz')
270
self.assertEqual(baz_path, osutils.dereference_path('./foo/baz'))
272
# Dereferences parent symlinks that are the first path element
273
self.assertEqual(baz_path, osutils.dereference_path('foo/baz'))
275
# Dereferences parent symlinks in absolute paths
276
foo_baz_path = osutils.pathjoin(foo_path, 'baz')
277
self.assertEqual(baz_path, osutils.dereference_path(foo_baz_path))
280
def test_changing_access(self):
281
f = file('file', 'w')
285
# Make a file readonly
286
osutils.make_readonly('file')
287
mode = osutils.lstat('file').st_mode
288
self.assertEqual(mode, mode & 0777555)
290
# Make a file writable
291
osutils.make_writable('file')
292
mode = osutils.lstat('file').st_mode
293
self.assertEqual(mode, mode | 0200)
295
if osutils.has_symlinks():
296
# should not error when handed a symlink
297
os.symlink('nonexistent', 'dangling')
298
osutils.make_readonly('dangling')
299
osutils.make_writable('dangling')
302
def test_kind_marker(self):
303
self.assertEqual("", osutils.kind_marker("file"))
304
self.assertEqual("/", osutils.kind_marker(osutils._directory_kind))
305
self.assertEqual("@", osutils.kind_marker("symlink"))
306
self.assertRaises(errors.BzrError, osutils.kind_marker, "unknown")
309
class TestSafeUnicode(TestCase):
311
def test_from_ascii_string(self):
312
self.assertEqual(u'foobar', osutils.safe_unicode('foobar'))
314
def test_from_unicode_string_ascii_contents(self):
315
self.assertEqual(u'bargam', osutils.safe_unicode(u'bargam'))
317
def test_from_unicode_string_unicode_contents(self):
318
self.assertEqual(u'bargam\xae', osutils.safe_unicode(u'bargam\xae'))
320
def test_from_utf8_string(self):
321
self.assertEqual(u'foo\xae', osutils.safe_unicode('foo\xc2\xae'))
323
def test_bad_utf8_string(self):
324
self.assertRaises(BzrBadParameterNotUnicode,
325
osutils.safe_unicode,
329
class TestSafeUtf8(TestCase):
331
def test_from_ascii_string(self):
333
self.assertEqual('foobar', osutils.safe_utf8(f))
335
def test_from_unicode_string_ascii_contents(self):
336
self.assertEqual('bargam', osutils.safe_utf8(u'bargam'))
338
def test_from_unicode_string_unicode_contents(self):
339
self.assertEqual('bargam\xc2\xae', osutils.safe_utf8(u'bargam\xae'))
341
def test_from_utf8_string(self):
342
self.assertEqual('foo\xc2\xae', osutils.safe_utf8('foo\xc2\xae'))
344
def test_bad_utf8_string(self):
345
self.assertRaises(BzrBadParameterNotUnicode,
346
osutils.safe_utf8, '\xbb\xbb')
349
class TestSafeRevisionId(TestCase):
351
def test_from_ascii_string(self):
352
self.assertEqual('foobar', osutils.safe_revision_id('foobar'))
354
def test_from_unicode_string_ascii_contents(self):
355
self.assertEqual('bargam',
356
osutils.safe_revision_id(u'bargam', warn=False))
358
def test_from_unicode_deprecated(self):
359
self.assertEqual('bargam',
360
self.callDeprecated([osutils._revision_id_warning],
361
osutils.safe_revision_id, u'bargam'))
363
def test_from_unicode_string_unicode_contents(self):
364
self.assertEqual('bargam\xc2\xae',
365
osutils.safe_revision_id(u'bargam\xae', warn=False))
367
def test_from_utf8_string(self):
368
self.assertEqual('foo\xc2\xae',
369
osutils.safe_revision_id('foo\xc2\xae'))
372
"""Currently, None is a valid revision_id"""
373
self.assertEqual(None, osutils.safe_revision_id(None))
376
class TestSafeFileId(TestCase):
378
def test_from_ascii_string(self):
379
self.assertEqual('foobar', osutils.safe_file_id('foobar'))
381
def test_from_unicode_string_ascii_contents(self):
382
self.assertEqual('bargam', osutils.safe_file_id(u'bargam', warn=False))
384
def test_from_unicode_deprecated(self):
385
self.assertEqual('bargam',
386
self.callDeprecated([osutils._file_id_warning],
387
osutils.safe_file_id, u'bargam'))
389
def test_from_unicode_string_unicode_contents(self):
390
self.assertEqual('bargam\xc2\xae',
391
osutils.safe_file_id(u'bargam\xae', warn=False))
393
def test_from_utf8_string(self):
394
self.assertEqual('foo\xc2\xae',
395
osutils.safe_file_id('foo\xc2\xae'))
398
"""Currently, None is a valid revision_id"""
399
self.assertEqual(None, osutils.safe_file_id(None))
402
class TestWin32Funcs(TestCase):
403
"""Test that the _win32 versions of os utilities return appropriate paths."""
405
def test_abspath(self):
406
self.assertEqual('C:/foo', osutils._win32_abspath('C:\\foo'))
407
self.assertEqual('C:/foo', osutils._win32_abspath('C:/foo'))
408
self.assertEqual('//HOST/path', osutils._win32_abspath(r'\\HOST\path'))
409
self.assertEqual('//HOST/path', osutils._win32_abspath('//HOST/path'))
411
def test_realpath(self):
412
self.assertEqual('C:/foo', osutils._win32_realpath('C:\\foo'))
413
self.assertEqual('C:/foo', osutils._win32_realpath('C:/foo'))
415
def test_pathjoin(self):
416
self.assertEqual('path/to/foo', osutils._win32_pathjoin('path', 'to', 'foo'))
417
self.assertEqual('C:/foo', osutils._win32_pathjoin('path\\to', 'C:\\foo'))
418
self.assertEqual('C:/foo', osutils._win32_pathjoin('path/to', 'C:/foo'))
419
self.assertEqual('path/to/foo', osutils._win32_pathjoin('path/to/', 'foo'))
420
self.assertEqual('/foo', osutils._win32_pathjoin('C:/path/to/', '/foo'))
421
self.assertEqual('/foo', osutils._win32_pathjoin('C:\\path\\to\\', '\\foo'))
423
def test_normpath(self):
424
self.assertEqual('path/to/foo', osutils._win32_normpath(r'path\\from\..\to\.\foo'))
425
self.assertEqual('path/to/foo', osutils._win32_normpath('path//from/../to/./foo'))
427
def test_getcwd(self):
428
cwd = osutils._win32_getcwd()
429
os_cwd = os.getcwdu()
430
self.assertEqual(os_cwd[1:].replace('\\', '/'), cwd[1:])
431
# win32 is inconsistent whether it returns lower or upper case
432
# and even if it was consistent the user might type the other
433
# so we force it to uppercase
434
# running python.exe under cmd.exe return capital C:\\
435
# running win32 python inside a cygwin shell returns lowercase
436
self.assertEqual(os_cwd[0].upper(), cwd[0])
438
def test_fixdrive(self):
439
self.assertEqual('H:/foo', osutils._win32_fixdrive('h:/foo'))
440
self.assertEqual('H:/foo', osutils._win32_fixdrive('H:/foo'))
441
self.assertEqual('C:\\foo', osutils._win32_fixdrive('c:\\foo'))
443
def test_win98_abspath(self):
445
self.assertEqual('C:/foo', osutils._win98_abspath('C:\\foo'))
446
self.assertEqual('C:/foo', osutils._win98_abspath('C:/foo'))
448
self.assertEqual('//HOST/path', osutils._win98_abspath(r'\\HOST\path'))
449
self.assertEqual('//HOST/path', osutils._win98_abspath('//HOST/path'))
451
cwd = osutils.getcwd().rstrip('/')
452
drive = osutils._nt_splitdrive(cwd)[0]
453
self.assertEqual(cwd+'/path', osutils._win98_abspath('path'))
454
self.assertEqual(drive+'/path', osutils._win98_abspath('/path'))
457
self.assertEqual(cwd+'/'+u, osutils._win98_abspath(u))
460
class TestWin32FuncsDirs(TestCaseInTempDir):
461
"""Test win32 functions that create files."""
463
def test_getcwd(self):
464
if win32utils.winver == 'Windows 98':
465
raise TestSkipped('Windows 98 cannot handle unicode filenames')
466
# Make sure getcwd can handle unicode filenames
470
raise TestSkipped("Unable to create Unicode filename")
473
# TODO: jam 20060427 This will probably fail on Mac OSX because
474
# it will change the normalization of B\xe5gfors
475
# Consider using a different unicode character, or make
476
# osutils.getcwd() renormalize the path.
477
self.assertEndsWith(osutils._win32_getcwd(), u'mu-\xb5')
479
def test_mkdtemp(self):
480
tmpdir = osutils._win32_mkdtemp(dir='.')
481
self.assertFalse('\\' in tmpdir)
483
def test_rename(self):
491
osutils._win32_rename('b', 'a')
492
self.failUnlessExists('a')
493
self.failIfExists('b')
494
self.assertFileEqual('baz\n', 'a')
496
def test_rename_missing_file(self):
502
osutils._win32_rename('b', 'a')
503
except (IOError, OSError), e:
504
self.assertEqual(errno.ENOENT, e.errno)
505
self.assertFileEqual('foo\n', 'a')
507
def test_rename_missing_dir(self):
510
osutils._win32_rename('b', 'a')
511
except (IOError, OSError), e:
512
self.assertEqual(errno.ENOENT, e.errno)
514
def test_rename_current_dir(self):
517
# You can't rename the working directory
518
# doing rename non-existant . usually
519
# just raises ENOENT, since non-existant
522
osutils._win32_rename('b', '.')
523
except (IOError, OSError), e:
524
self.assertEqual(errno.ENOENT, e.errno)
526
def test_splitpath(self):
527
def check(expected, path):
528
self.assertEqual(expected, osutils.splitpath(path))
531
check(['a', 'b'], 'a/b')
532
check(['a', 'b'], 'a/./b')
533
check(['a', '.b'], 'a/.b')
534
check(['a', '.b'], 'a\\.b')
536
self.assertRaises(errors.BzrError, osutils.splitpath, 'a/../b')
539
class TestMacFuncsDirs(TestCaseInTempDir):
540
"""Test mac special functions that require directories."""
542
def test_getcwd(self):
543
# On Mac, this will actually create Ba\u030agfors
544
# but chdir will still work, because it accepts both paths
546
os.mkdir(u'B\xe5gfors')
548
raise TestSkipped("Unable to create Unicode filename")
550
os.chdir(u'B\xe5gfors')
551
self.assertEndsWith(osutils._mac_getcwd(), u'B\xe5gfors')
553
def test_getcwd_nonnorm(self):
554
# Test that _mac_getcwd() will normalize this path
556
os.mkdir(u'Ba\u030agfors')
558
raise TestSkipped("Unable to create Unicode filename")
560
os.chdir(u'Ba\u030agfors')
561
self.assertEndsWith(osutils._mac_getcwd(), u'B\xe5gfors')
564
class TestSplitLines(TestCase):
566
def test_split_unicode(self):
567
self.assertEqual([u'foo\n', u'bar\xae'],
568
osutils.split_lines(u'foo\nbar\xae'))
569
self.assertEqual([u'foo\n', u'bar\xae\n'],
570
osutils.split_lines(u'foo\nbar\xae\n'))
572
def test_split_with_carriage_returns(self):
573
self.assertEqual(['foo\rbar\n'],
574
osutils.split_lines('foo\rbar\n'))
577
class TestWalkDirs(TestCaseInTempDir):
579
def test_walkdirs(self):
588
self.build_tree(tree)
589
expected_dirblocks = [
591
[('0file', '0file', 'file'),
592
('1dir', '1dir', 'directory'),
593
('2file', '2file', 'file'),
597
[('1dir/0file', '0file', 'file'),
598
('1dir/1dir', '1dir', 'directory'),
601
(('1dir/1dir', './1dir/1dir'),
608
for dirdetail, dirblock in osutils.walkdirs('.'):
609
if len(dirblock) and dirblock[0][1] == '.bzr':
610
# this tests the filtering of selected paths
613
result.append((dirdetail, dirblock))
615
self.assertTrue(found_bzrdir)
616
self.assertEqual(expected_dirblocks,
617
[(dirinfo, [line[0:3] for line in block]) for dirinfo, block in result])
618
# you can search a subdir only, with a supplied prefix.
620
for dirblock in osutils.walkdirs('./1dir', '1dir'):
621
result.append(dirblock)
622
self.assertEqual(expected_dirblocks[1:],
623
[(dirinfo, [line[0:3] for line in block]) for dirinfo, block in result])
625
def test__walkdirs_utf8(self):
634
self.build_tree(tree)
635
expected_dirblocks = [
637
[('0file', '0file', 'file'),
638
('1dir', '1dir', 'directory'),
639
('2file', '2file', 'file'),
643
[('1dir/0file', '0file', 'file'),
644
('1dir/1dir', '1dir', 'directory'),
647
(('1dir/1dir', './1dir/1dir'),
654
for dirdetail, dirblock in osutils._walkdirs_utf8('.'):
655
if len(dirblock) and dirblock[0][1] == '.bzr':
656
# this tests the filtering of selected paths
659
result.append((dirdetail, dirblock))
661
self.assertTrue(found_bzrdir)
662
self.assertEqual(expected_dirblocks,
663
[(dirinfo, [line[0:3] for line in block]) for dirinfo, block in result])
664
# you can search a subdir only, with a supplied prefix.
666
for dirblock in osutils.walkdirs('./1dir', '1dir'):
667
result.append(dirblock)
668
self.assertEqual(expected_dirblocks[1:],
669
[(dirinfo, [line[0:3] for line in block]) for dirinfo, block in result])
671
def _filter_out_stat(self, result):
672
"""Filter out the stat value from the walkdirs result"""
673
for dirdetail, dirblock in result:
675
for info in dirblock:
676
# Ignore info[3] which is the stat
677
new_dirblock.append((info[0], info[1], info[2], info[4]))
678
dirblock[:] = new_dirblock
680
def test_unicode_walkdirs(self):
681
"""Walkdirs should always return unicode paths."""
682
name0 = u'0file-\xb6'
683
name1 = u'1dir-\u062c\u0648'
684
name2 = u'2file-\u0633'
689
name1 + '/' + name1 + '/',
693
self.build_tree(tree)
695
raise TestSkipped('Could not represent Unicode chars'
696
' in current encoding.')
697
expected_dirblocks = [
699
[(name0, name0, 'file', './' + name0),
700
(name1, name1, 'directory', './' + name1),
701
(name2, name2, 'file', './' + name2),
704
((name1, './' + name1),
705
[(name1 + '/' + name0, name0, 'file', './' + name1
707
(name1 + '/' + name1, name1, 'directory', './' + name1
711
((name1 + '/' + name1, './' + name1 + '/' + name1),
716
result = list(osutils.walkdirs('.'))
717
self._filter_out_stat(result)
718
self.assertEqual(expected_dirblocks, result)
719
result = list(osutils.walkdirs(u'./'+name1, name1))
720
self._filter_out_stat(result)
721
self.assertEqual(expected_dirblocks[1:], result)
723
def test_unicode__walkdirs_utf8(self):
724
"""Walkdirs_utf8 should always return utf8 paths.
726
The abspath portion might be in unicode or utf-8
728
name0 = u'0file-\xb6'
729
name1 = u'1dir-\u062c\u0648'
730
name2 = u'2file-\u0633'
735
name1 + '/' + name1 + '/',
739
self.build_tree(tree)
741
raise TestSkipped('Could not represent Unicode chars'
742
' in current encoding.')
743
name0 = name0.encode('utf8')
744
name1 = name1.encode('utf8')
745
name2 = name2.encode('utf8')
747
expected_dirblocks = [
749
[(name0, name0, 'file', './' + name0),
750
(name1, name1, 'directory', './' + name1),
751
(name2, name2, 'file', './' + name2),
754
((name1, './' + name1),
755
[(name1 + '/' + name0, name0, 'file', './' + name1
757
(name1 + '/' + name1, name1, 'directory', './' + name1
761
((name1 + '/' + name1, './' + name1 + '/' + name1),
767
# For ease in testing, if walkdirs_utf8 returns Unicode, assert that
768
# all abspaths are Unicode, and encode them back into utf8.
769
for dirdetail, dirblock in osutils._walkdirs_utf8('.'):
770
self.assertIsInstance(dirdetail[0], str)
771
if isinstance(dirdetail[1], unicode):
772
dirdetail = (dirdetail[0], dirdetail[1].encode('utf8'))
773
dirblock = [list(info) for info in dirblock]
774
for info in dirblock:
775
self.assertIsInstance(info[4], unicode)
776
info[4] = info[4].encode('utf8')
778
for info in dirblock:
779
self.assertIsInstance(info[0], str)
780
self.assertIsInstance(info[1], str)
781
self.assertIsInstance(info[4], str)
782
# Remove the stat information
783
new_dirblock.append((info[0], info[1], info[2], info[4]))
784
result.append((dirdetail, new_dirblock))
785
self.assertEqual(expected_dirblocks, result)
787
def test_unicode__walkdirs_unicode_to_utf8(self):
788
"""walkdirs_unicode_to_utf8 should be a safe fallback everywhere
790
The abspath portion should be in unicode
792
name0u = u'0file-\xb6'
793
name1u = u'1dir-\u062c\u0648'
794
name2u = u'2file-\u0633'
798
name1u + '/' + name0u,
799
name1u + '/' + name1u + '/',
803
self.build_tree(tree)
805
raise TestSkipped('Could not represent Unicode chars'
806
' in current encoding.')
807
name0 = name0u.encode('utf8')
808
name1 = name1u.encode('utf8')
809
name2 = name2u.encode('utf8')
811
# All of the abspaths should be in unicode, all of the relative paths
813
expected_dirblocks = [
815
[(name0, name0, 'file', './' + name0u),
816
(name1, name1, 'directory', './' + name1u),
817
(name2, name2, 'file', './' + name2u),
820
((name1, './' + name1u),
821
[(name1 + '/' + name0, name0, 'file', './' + name1u
823
(name1 + '/' + name1, name1, 'directory', './' + name1u
827
((name1 + '/' + name1, './' + name1u + '/' + name1u),
832
result = list(osutils._walkdirs_unicode_to_utf8('.'))
833
self._filter_out_stat(result)
834
self.assertEqual(expected_dirblocks, result)
836
def assertPathCompare(self, path_less, path_greater):
837
"""check that path_less and path_greater compare correctly."""
838
self.assertEqual(0, osutils.compare_paths_prefix_order(
839
path_less, path_less))
840
self.assertEqual(0, osutils.compare_paths_prefix_order(
841
path_greater, path_greater))
842
self.assertEqual(-1, osutils.compare_paths_prefix_order(
843
path_less, path_greater))
844
self.assertEqual(1, osutils.compare_paths_prefix_order(
845
path_greater, path_less))
847
def test_compare_paths_prefix_order(self):
848
# root before all else
849
self.assertPathCompare("/", "/a")
851
self.assertPathCompare("/a", "/b")
852
self.assertPathCompare("/b", "/z")
853
# high dirs before lower.
854
self.assertPathCompare("/z", "/a/a")
855
# except if the deeper dir should be output first
856
self.assertPathCompare("/a/b/c", "/d/g")
857
# lexical betwen dirs of the same height
858
self.assertPathCompare("/a/z", "/z/z")
859
self.assertPathCompare("/a/c/z", "/a/d/e")
861
# this should also be consistent for no leading / paths
862
# root before all else
863
self.assertPathCompare("", "a")
865
self.assertPathCompare("a", "b")
866
self.assertPathCompare("b", "z")
867
# high dirs before lower.
868
self.assertPathCompare("z", "a/a")
869
# except if the deeper dir should be output first
870
self.assertPathCompare("a/b/c", "d/g")
871
# lexical betwen dirs of the same height
872
self.assertPathCompare("a/z", "z/z")
873
self.assertPathCompare("a/c/z", "a/d/e")
875
def test_path_prefix_sorting(self):
876
"""Doing a sort on path prefix should match our sample data."""
907
sorted(original_paths, key=osutils.path_prefix_key))
908
# using the comparison routine shoudl work too:
911
sorted(original_paths, cmp=osutils.compare_paths_prefix_order))
914
class TestCopyTree(TestCaseInTempDir):
916
def test_copy_basic_tree(self):
917
self.build_tree(['source/', 'source/a', 'source/b/', 'source/b/c'])
918
osutils.copy_tree('source', 'target')
919
self.assertEqual(['a', 'b'], sorted(os.listdir('target')))
920
self.assertEqual(['c'], os.listdir('target/b'))
922
def test_copy_tree_target_exists(self):
923
self.build_tree(['source/', 'source/a', 'source/b/', 'source/b/c',
925
osutils.copy_tree('source', 'target')
926
self.assertEqual(['a', 'b'], sorted(os.listdir('target')))
927
self.assertEqual(['c'], os.listdir('target/b'))
929
def test_copy_tree_symlinks(self):
930
if not osutils.has_symlinks():
932
self.build_tree(['source/'])
933
os.symlink('a/generic/path', 'source/lnk')
934
osutils.copy_tree('source', 'target')
935
self.assertEqual(['lnk'], os.listdir('target'))
936
self.assertEqual('a/generic/path', os.readlink('target/lnk'))
938
def test_copy_tree_handlers(self):
941
def file_handler(from_path, to_path):
942
processed_files.append(('f', from_path, to_path))
943
def dir_handler(from_path, to_path):
944
processed_files.append(('d', from_path, to_path))
945
def link_handler(from_path, to_path):
946
processed_links.append((from_path, to_path))
947
handlers = {'file':file_handler,
948
'directory':dir_handler,
949
'symlink':link_handler,
952
self.build_tree(['source/', 'source/a', 'source/b/', 'source/b/c'])
953
if osutils.has_symlinks():
954
os.symlink('a/generic/path', 'source/lnk')
955
osutils.copy_tree('source', 'target', handlers=handlers)
957
self.assertEqual([('d', 'source', 'target'),
958
('f', 'source/a', 'target/a'),
959
('d', 'source/b', 'target/b'),
960
('f', 'source/b/c', 'target/b/c'),
962
self.failIfExists('target')
963
if osutils.has_symlinks():
964
self.assertEqual([('source/lnk', 'target/lnk')], processed_links)
967
#class TestTerminalEncoding has been moved to test_osutils_encodings.py
968
# [bialix] 2006/12/26
971
class TestSetUnsetEnv(TestCase):
972
"""Test updating the environment"""
975
super(TestSetUnsetEnv, self).setUp()
977
self.assertEqual(None, os.environ.get('BZR_TEST_ENV_VAR'),
978
'Environment was not cleaned up properly.'
979
' Variable BZR_TEST_ENV_VAR should not exist.')
981
if 'BZR_TEST_ENV_VAR' in os.environ:
982
del os.environ['BZR_TEST_ENV_VAR']
984
self.addCleanup(cleanup)
987
"""Test that we can set an env variable"""
988
old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'foo')
989
self.assertEqual(None, old)
990
self.assertEqual('foo', os.environ.get('BZR_TEST_ENV_VAR'))
992
def test_double_set(self):
993
"""Test that we get the old value out"""
994
osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'foo')
995
old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'bar')
996
self.assertEqual('foo', old)
997
self.assertEqual('bar', os.environ.get('BZR_TEST_ENV_VAR'))
999
def test_unicode(self):
1000
"""Environment can only contain plain strings
1002
So Unicode strings must be encoded.
1004
# Try a few different characters, to see if we can get
1005
# one that will be valid in the user_encoding
1006
possible_vals = [u'm\xb5', u'\xe1', u'\u0410']
1007
for uni_val in possible_vals:
1009
env_val = uni_val.encode(bzrlib.user_encoding)
1010
except UnicodeEncodeError:
1011
# Try a different character
1016
raise TestSkipped('Cannot find a unicode character that works in'
1017
' encoding %s' % (bzrlib.user_encoding,))
1019
old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', uni_val)
1020
self.assertEqual(env_val, os.environ.get('BZR_TEST_ENV_VAR'))
1022
def test_unset(self):
1023
"""Test that passing None will remove the env var"""
1024
osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'foo')
1025
old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', None)
1026
self.assertEqual('foo', old)
1027
self.assertEqual(None, os.environ.get('BZR_TEST_ENV_VAR'))
1028
self.failIf('BZR_TEST_ENV_VAR' in os.environ)
1031
class TestLocalTimeOffset(TestCase):
1033
def test_local_time_offset(self):
1034
"""Test that local_time_offset() returns a sane value."""
1035
offset = osutils.local_time_offset()
1036
self.assertTrue(isinstance(offset, int))
1037
# Test that the offset is no more than a eighteen hours in
1039
# Time zone handling is system specific, so it is difficult to
1040
# do more specific tests, but a value outside of this range is
1042
eighteen_hours = 18 * 3600
1043
self.assertTrue(-eighteen_hours < offset < eighteen_hours)
1045
def test_local_time_offset_with_timestamp(self):
1046
"""Test that local_time_offset() works with a timestamp."""
1047
offset = osutils.local_time_offset(1000000000.1234567)
1048
self.assertTrue(isinstance(offset, int))
1049
eighteen_hours = 18 * 3600
1050
self.assertTrue(-eighteen_hours < offset < eighteen_hours)