1
# Copyright (C) 2005, 2006 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."""
30
from bzrlib.errors import BzrBadParameterNotUnicode, InvalidURL
31
from bzrlib.tests import (
39
class TestOSUtils(TestCaseInTempDir):
41
def test_fancy_rename(self):
42
# This should work everywhere
44
osutils.fancy_rename(a, b,
45
rename_func=os.rename,
46
unlink_func=os.unlink)
48
open('a', 'wb').write('something in a\n')
50
self.failIfExists('a')
51
self.failUnlessExists('b')
52
self.check_file_contents('b', 'something in a\n')
54
open('a', 'wb').write('new something in a\n')
57
self.check_file_contents('a', 'something in a\n')
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')
67
open('a', 'wb').write('new something in a\n')
68
osutils.rename('b', 'a')
70
self.check_file_contents('a', 'something in a\n')
72
# TODO: test fancy_rename using a MemoryTransport
74
def test_01_rand_chars_empty(self):
75
result = osutils.rand_chars(0)
76
self.assertEqual(result, '')
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}$')
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'))
93
def test_rmtree(self):
94
# Check to remove tree with read-only files/dirs
96
f = file('dir/file', 'w')
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')
106
osutils.rmtree('dir')
108
self.failIfExists('dir/file')
109
self.failIfExists('dir')
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'))
119
# TODO: jam 20060529 Test a block device
121
os.lstat('/dev/null')
123
if e.errno not in (errno.ENOENT,):
126
self.assertEquals('chardev', osutils.file_kind('/dev/null'))
128
mkfifo = getattr(os, 'mkfifo', None)
132
self.assertEquals('fifo', osutils.file_kind('fifo'))
136
AF_UNIX = getattr(socket, 'AF_UNIX', None)
138
s = socket.socket(AF_UNIX)
141
self.assertEquals('socket', osutils.file_kind('socket'))
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())
151
orig_umask = osutils.get_umask()
154
self.assertEqual(0222, osutils.get_umask())
156
self.assertEqual(0022, osutils.get_umask())
158
self.assertEqual(0002, osutils.get_umask())
160
self.assertEqual(0027, osutils.get_umask())
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)
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)
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)
201
class TestSafeUnicode(TestCase):
203
def test_from_ascii_string(self):
204
self.assertEqual(u'foobar', osutils.safe_unicode('foobar'))
206
def test_from_unicode_string_ascii_contents(self):
207
self.assertEqual(u'bargam', osutils.safe_unicode(u'bargam'))
209
def test_from_unicode_string_unicode_contents(self):
210
self.assertEqual(u'bargam\xae', osutils.safe_unicode(u'bargam\xae'))
212
def test_from_utf8_string(self):
213
self.assertEqual(u'foo\xae', osutils.safe_unicode('foo\xc2\xae'))
215
def test_bad_utf8_string(self):
216
self.assertRaises(BzrBadParameterNotUnicode,
217
osutils.safe_unicode,
221
class TestWin32Funcs(TestCase):
222
"""Test that the _win32 versions of os utilities return appropriate paths."""
224
def test_abspath(self):
225
self.assertEqual('C:/foo', osutils._win32_abspath('C:\\foo'))
226
self.assertEqual('C:/foo', osutils._win32_abspath('C:/foo'))
228
def test_realpath(self):
229
self.assertEqual('C:/foo', osutils._win32_realpath('C:\\foo'))
230
self.assertEqual('C:/foo', osutils._win32_realpath('C:/foo'))
232
def test_pathjoin(self):
233
self.assertEqual('path/to/foo', osutils._win32_pathjoin('path', 'to', 'foo'))
234
self.assertEqual('C:/foo', osutils._win32_pathjoin('path\\to', 'C:\\foo'))
235
self.assertEqual('C:/foo', osutils._win32_pathjoin('path/to', 'C:/foo'))
236
self.assertEqual('path/to/foo', osutils._win32_pathjoin('path/to/', 'foo'))
237
self.assertEqual('/foo', osutils._win32_pathjoin('C:/path/to/', '/foo'))
238
self.assertEqual('/foo', osutils._win32_pathjoin('C:\\path\\to\\', '\\foo'))
240
def test_normpath(self):
241
self.assertEqual('path/to/foo', osutils._win32_normpath(r'path\\from\..\to\.\foo'))
242
self.assertEqual('path/to/foo', osutils._win32_normpath('path//from/../to/./foo'))
244
def test_getcwd(self):
245
cwd = osutils._win32_getcwd()
246
os_cwd = os.getcwdu()
247
self.assertEqual(os_cwd[1:].replace('\\', '/'), cwd[1:])
248
# win32 is inconsistent whether it returns lower or upper case
249
# and even if it was consistent the user might type the other
250
# so we force it to uppercase
251
# running python.exe under cmd.exe return capital C:\\
252
# running win32 python inside a cygwin shell returns lowercase
253
self.assertEqual(os_cwd[0].upper(), cwd[0])
255
def test_fixdrive(self):
256
self.assertEqual('H:/foo', osutils._win32_fixdrive('h:/foo'))
257
self.assertEqual('H:/foo', osutils._win32_fixdrive('H:/foo'))
258
self.assertEqual('C:\\foo', osutils._win32_fixdrive('c:\\foo'))
261
class TestWin32FuncsDirs(TestCaseInTempDir):
262
"""Test win32 functions that create files."""
264
def test_getcwd(self):
265
# Make sure getcwd can handle unicode filenames
269
raise TestSkipped("Unable to create Unicode filename")
272
# TODO: jam 20060427 This will probably fail on Mac OSX because
273
# it will change the normalization of B\xe5gfors
274
# Consider using a different unicode character, or make
275
# osutils.getcwd() renormalize the path.
276
self.assertEndsWith(osutils._win32_getcwd(), u'mu-\xb5')
278
def test_mkdtemp(self):
279
tmpdir = osutils._win32_mkdtemp(dir='.')
280
self.assertFalse('\\' in tmpdir)
282
def test_rename(self):
290
osutils._win32_rename('b', 'a')
291
self.failUnlessExists('a')
292
self.failIfExists('b')
293
self.assertFileEqual('baz\n', 'a')
295
def test_rename_missing_file(self):
301
osutils._win32_rename('b', 'a')
302
except (IOError, OSError), e:
303
self.assertEqual(errno.ENOENT, e.errno)
304
self.assertFileEqual('foo\n', 'a')
306
def test_rename_missing_dir(self):
309
osutils._win32_rename('b', 'a')
310
except (IOError, OSError), e:
311
self.assertEqual(errno.ENOENT, e.errno)
313
def test_rename_current_dir(self):
316
# You can't rename the working directory
317
# doing rename non-existant . usually
318
# just raises ENOENT, since non-existant
321
osutils._win32_rename('b', '.')
322
except (IOError, OSError), e:
323
self.assertEqual(errno.ENOENT, e.errno)
325
def test_splitpath(self):
326
def check(expected, path):
327
self.assertEqual(expected, osutils.splitpath(path))
330
check(['a', 'b'], 'a/b')
331
check(['a', 'b'], 'a/./b')
332
check(['a', '.b'], 'a/.b')
333
check(['a', '.b'], 'a\\.b')
335
self.assertRaises(errors.BzrError, osutils.splitpath, 'a/../b')
338
class TestMacFuncsDirs(TestCaseInTempDir):
339
"""Test mac special functions that require directories."""
341
def test_getcwd(self):
342
# On Mac, this will actually create Ba\u030agfors
343
# but chdir will still work, because it accepts both paths
345
os.mkdir(u'B\xe5gfors')
347
raise TestSkipped("Unable to create Unicode filename")
349
os.chdir(u'B\xe5gfors')
350
self.assertEndsWith(osutils._mac_getcwd(), u'B\xe5gfors')
352
def test_getcwd_nonnorm(self):
353
# Test that _mac_getcwd() will normalize this path
355
os.mkdir(u'Ba\u030agfors')
357
raise TestSkipped("Unable to create Unicode filename")
359
os.chdir(u'Ba\u030agfors')
360
self.assertEndsWith(osutils._mac_getcwd(), u'B\xe5gfors')
363
class TestSplitLines(TestCase):
365
def test_split_unicode(self):
366
self.assertEqual([u'foo\n', u'bar\xae'],
367
osutils.split_lines(u'foo\nbar\xae'))
368
self.assertEqual([u'foo\n', u'bar\xae\n'],
369
osutils.split_lines(u'foo\nbar\xae\n'))
371
def test_split_with_carriage_returns(self):
372
self.assertEqual(['foo\rbar\n'],
373
osutils.split_lines('foo\rbar\n'))
376
class TestWalkDirs(TestCaseInTempDir):
378
def test_walkdirs(self):
387
self.build_tree(tree)
388
expected_dirblocks = [
390
[('0file', '0file', 'file'),
391
('1dir', '1dir', 'directory'),
392
('2file', '2file', 'file'),
396
[('1dir/0file', '0file', 'file'),
397
('1dir/1dir', '1dir', 'directory'),
400
(('1dir/1dir', './1dir/1dir'),
407
for dirdetail, dirblock in osutils.walkdirs('.'):
408
if len(dirblock) and dirblock[0][1] == '.bzr':
409
# this tests the filtering of selected paths
412
result.append((dirdetail, dirblock))
414
self.assertTrue(found_bzrdir)
415
self.assertEqual(expected_dirblocks,
416
[(dirinfo, [line[0:3] for line in block]) for dirinfo, block in result])
417
# you can search a subdir only, with a supplied prefix.
419
for dirblock in osutils.walkdirs('./1dir', '1dir'):
420
result.append(dirblock)
421
self.assertEqual(expected_dirblocks[1:],
422
[(dirinfo, [line[0:3] for line in block]) for dirinfo, block in result])
424
def assertPathCompare(self, path_less, path_greater):
425
"""check that path_less and path_greater compare correctly."""
426
self.assertEqual(0, osutils.compare_paths_prefix_order(
427
path_less, path_less))
428
self.assertEqual(0, osutils.compare_paths_prefix_order(
429
path_greater, path_greater))
430
self.assertEqual(-1, osutils.compare_paths_prefix_order(
431
path_less, path_greater))
432
self.assertEqual(1, osutils.compare_paths_prefix_order(
433
path_greater, path_less))
435
def test_compare_paths_prefix_order(self):
436
# root before all else
437
self.assertPathCompare("/", "/a")
439
self.assertPathCompare("/a", "/b")
440
self.assertPathCompare("/b", "/z")
441
# high dirs before lower.
442
self.assertPathCompare("/z", "/a/a")
443
# except if the deeper dir should be output first
444
self.assertPathCompare("/a/b/c", "/d/g")
445
# lexical betwen dirs of the same height
446
self.assertPathCompare("/a/z", "/z/z")
447
self.assertPathCompare("/a/c/z", "/a/d/e")
449
# this should also be consistent for no leading / paths
450
# root before all else
451
self.assertPathCompare("", "a")
453
self.assertPathCompare("a", "b")
454
self.assertPathCompare("b", "z")
455
# high dirs before lower.
456
self.assertPathCompare("z", "a/a")
457
# except if the deeper dir should be output first
458
self.assertPathCompare("a/b/c", "d/g")
459
# lexical betwen dirs of the same height
460
self.assertPathCompare("a/z", "z/z")
461
self.assertPathCompare("a/c/z", "a/d/e")
463
def test_path_prefix_sorting(self):
464
"""Doing a sort on path prefix should match our sample data."""
495
sorted(original_paths, key=osutils.path_prefix_key))
496
# using the comparison routine shoudl work too:
499
sorted(original_paths, cmp=osutils.compare_paths_prefix_order))
502
class TestCopyTree(TestCaseInTempDir):
504
def test_copy_basic_tree(self):
505
self.build_tree(['source/', 'source/a', 'source/b/', 'source/b/c'])
506
osutils.copy_tree('source', 'target')
507
self.assertEqual(['a', 'b'], sorted(os.listdir('target')))
508
self.assertEqual(['c'], os.listdir('target/b'))
510
def test_copy_tree_target_exists(self):
511
self.build_tree(['source/', 'source/a', 'source/b/', 'source/b/c',
513
osutils.copy_tree('source', 'target')
514
self.assertEqual(['a', 'b'], sorted(os.listdir('target')))
515
self.assertEqual(['c'], os.listdir('target/b'))
517
def test_copy_tree_symlinks(self):
518
if not osutils.has_symlinks():
520
self.build_tree(['source/'])
521
os.symlink('a/generic/path', 'source/lnk')
522
osutils.copy_tree('source', 'target')
523
self.assertEqual(['lnk'], os.listdir('target'))
524
self.assertEqual('a/generic/path', os.readlink('target/lnk'))
526
def test_copy_tree_handlers(self):
529
def file_handler(from_path, to_path):
530
processed_files.append(('f', from_path, to_path))
531
def dir_handler(from_path, to_path):
532
processed_files.append(('d', from_path, to_path))
533
def link_handler(from_path, to_path):
534
processed_links.append((from_path, to_path))
535
handlers = {'file':file_handler,
536
'directory':dir_handler,
537
'symlink':link_handler,
540
self.build_tree(['source/', 'source/a', 'source/b/', 'source/b/c'])
541
if osutils.has_symlinks():
542
os.symlink('a/generic/path', 'source/lnk')
543
osutils.copy_tree('source', 'target', handlers=handlers)
545
self.assertEqual([('d', 'source', 'target'),
546
('f', 'source/a', 'target/a'),
547
('d', 'source/b', 'target/b'),
548
('f', 'source/b/c', 'target/b/c'),
550
self.failIfExists('target')
551
if osutils.has_symlinks():
552
self.assertEqual([('source/lnk', 'target/lnk')], processed_links)
555
class TestTerminalEncoding(TestCase):
556
"""Test the auto-detection of proper terminal encoding."""
559
self._stdout = sys.stdout
560
self._stderr = sys.stderr
561
self._stdin = sys.stdin
562
self._user_encoding = bzrlib.user_encoding
564
self.addCleanup(self._reset)
566
sys.stdout = StringIOWrapper()
567
sys.stdout.encoding = 'stdout_encoding'
568
sys.stderr = StringIOWrapper()
569
sys.stderr.encoding = 'stderr_encoding'
570
sys.stdin = StringIOWrapper()
571
sys.stdin.encoding = 'stdin_encoding'
572
bzrlib.user_encoding = 'user_encoding'
575
sys.stdout = self._stdout
576
sys.stderr = self._stderr
577
sys.stdin = self._stdin
578
bzrlib.user_encoding = self._user_encoding
580
def test_get_terminal_encoding(self):
581
# first preference is stdout encoding
582
self.assertEqual('stdout_encoding', osutils.get_terminal_encoding())
584
sys.stdout.encoding = None
585
# if sys.stdout is None, fall back to sys.stdin
586
self.assertEqual('stdin_encoding', osutils.get_terminal_encoding())
588
sys.stdin.encoding = None
589
# and in the worst case, use bzrlib.user_encoding
590
self.assertEqual('user_encoding', osutils.get_terminal_encoding())
593
class TestSetUnsetEnv(TestCase):
594
"""Test updating the environment"""
597
super(TestSetUnsetEnv, self).setUp()
599
self.assertEqual(None, os.environ.get('BZR_TEST_ENV_VAR'),
600
'Environment was not cleaned up properly.'
601
' Variable BZR_TEST_ENV_VAR should not exist.')
603
if 'BZR_TEST_ENV_VAR' in os.environ:
604
del os.environ['BZR_TEST_ENV_VAR']
606
self.addCleanup(cleanup)
609
"""Test that we can set an env variable"""
610
old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'foo')
611
self.assertEqual(None, old)
612
self.assertEqual('foo', os.environ.get('BZR_TEST_ENV_VAR'))
614
def test_double_set(self):
615
"""Test that we get the old value out"""
616
osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'foo')
617
old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'bar')
618
self.assertEqual('foo', old)
619
self.assertEqual('bar', os.environ.get('BZR_TEST_ENV_VAR'))
621
def test_unicode(self):
622
"""Environment can only contain plain strings
624
So Unicode strings must be encoded.
626
# Try a few different characters, to see if we can get
627
# one that will be valid in the user_encoding
628
possible_vals = [u'm\xb5', u'\xe1', u'\u0410']
629
for uni_val in possible_vals:
631
env_val = uni_val.encode(bzrlib.user_encoding)
632
except UnicodeEncodeError:
633
# Try a different character
638
raise TestSkipped('Cannot find a unicode character that works in'
639
' encoding %s' % (bzrlib.user_encoding,))
641
old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', uni_val)
642
self.assertEqual(env_val, os.environ.get('BZR_TEST_ENV_VAR'))
644
def test_unset(self):
645
"""Test that passing None will remove the env var"""
646
osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'foo')
647
old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', None)
648
self.assertEqual('foo', old)
649
self.assertEqual(None, os.environ.get('BZR_TEST_ENV_VAR'))
650
self.failIf('BZR_TEST_ENV_VAR' in os.environ)