1
# Copyright (C) 2005 by 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())
165
class TestSafeUnicode(TestCase):
167
def test_from_ascii_string(self):
168
self.assertEqual(u'foobar', osutils.safe_unicode('foobar'))
170
def test_from_unicode_string_ascii_contents(self):
171
self.assertEqual(u'bargam', osutils.safe_unicode(u'bargam'))
173
def test_from_unicode_string_unicode_contents(self):
174
self.assertEqual(u'bargam\xae', osutils.safe_unicode(u'bargam\xae'))
176
def test_from_utf8_string(self):
177
self.assertEqual(u'foo\xae', osutils.safe_unicode('foo\xc2\xae'))
179
def test_bad_utf8_string(self):
180
self.assertRaises(BzrBadParameterNotUnicode,
181
osutils.safe_unicode,
185
class TestWin32Funcs(TestCase):
186
"""Test that the _win32 versions of os utilities return appropriate paths."""
188
def test_abspath(self):
189
self.assertEqual('C:/foo', osutils._win32_abspath('C:\\foo'))
190
self.assertEqual('C:/foo', osutils._win32_abspath('C:/foo'))
192
def test_realpath(self):
193
self.assertEqual('C:/foo', osutils._win32_realpath('C:\\foo'))
194
self.assertEqual('C:/foo', osutils._win32_realpath('C:/foo'))
196
def test_pathjoin(self):
197
self.assertEqual('path/to/foo', osutils._win32_pathjoin('path', 'to', 'foo'))
198
self.assertEqual('C:/foo', osutils._win32_pathjoin('path\\to', 'C:\\foo'))
199
self.assertEqual('C:/foo', osutils._win32_pathjoin('path/to', 'C:/foo'))
200
self.assertEqual('path/to/foo', osutils._win32_pathjoin('path/to/', 'foo'))
201
self.assertEqual('/foo', osutils._win32_pathjoin('C:/path/to/', '/foo'))
202
self.assertEqual('/foo', osutils._win32_pathjoin('C:\\path\\to\\', '\\foo'))
204
def test_normpath(self):
205
self.assertEqual('path/to/foo', osutils._win32_normpath(r'path\\from\..\to\.\foo'))
206
self.assertEqual('path/to/foo', osutils._win32_normpath('path//from/../to/./foo'))
208
def test_getcwd(self):
209
cwd = osutils._win32_getcwd()
210
os_cwd = os.getcwdu()
211
self.assertEqual(os_cwd[1:].replace('\\', '/'), cwd[1:])
212
# win32 is inconsistent whether it returns lower or upper case
213
# and even if it was consistent the user might type the other
214
# so we force it to uppercase
215
# running python.exe under cmd.exe return capital C:\\
216
# running win32 python inside a cygwin shell returns lowercase
217
self.assertEqual(os_cwd[0].upper(), cwd[0])
219
def test_fixdrive(self):
220
self.assertEqual('H:/foo', osutils._win32_fixdrive('h:/foo'))
221
self.assertEqual('H:/foo', osutils._win32_fixdrive('H:/foo'))
222
self.assertEqual('C:\\foo', osutils._win32_fixdrive('c:\\foo'))
225
class TestWin32FuncsDirs(TestCaseInTempDir):
226
"""Test win32 functions that create files."""
228
def test_getcwd(self):
229
# Make sure getcwd can handle unicode filenames
233
raise TestSkipped("Unable to create Unicode filename")
236
# TODO: jam 20060427 This will probably fail on Mac OSX because
237
# it will change the normalization of B\xe5gfors
238
# Consider using a different unicode character, or make
239
# osutils.getcwd() renormalize the path.
240
self.assertEndsWith(osutils._win32_getcwd(), u'mu-\xb5')
242
def test_mkdtemp(self):
243
tmpdir = osutils._win32_mkdtemp(dir='.')
244
self.assertFalse('\\' in tmpdir)
246
def test_rename(self):
254
osutils._win32_rename('b', 'a')
255
self.failUnlessExists('a')
256
self.failIfExists('b')
257
self.assertFileEqual('baz\n', 'a')
259
def test_rename_missing_file(self):
265
osutils._win32_rename('b', 'a')
266
except (IOError, OSError), e:
267
self.assertEqual(errno.ENOENT, e.errno)
268
self.assertFileEqual('foo\n', 'a')
270
def test_rename_missing_dir(self):
273
osutils._win32_rename('b', 'a')
274
except (IOError, OSError), e:
275
self.assertEqual(errno.ENOENT, e.errno)
277
def test_rename_current_dir(self):
280
# You can't rename the working directory
281
# doing rename non-existant . usually
282
# just raises ENOENT, since non-existant
285
osutils._win32_rename('b', '.')
286
except (IOError, OSError), e:
287
self.assertEqual(errno.ENOENT, e.errno)
289
def test_splitpath(self):
290
def check(expected, path):
291
self.assertEqual(expected, osutils.splitpath(path))
294
check(['a', 'b'], 'a/b')
295
check(['a', 'b'], 'a/./b')
296
check(['a', '.b'], 'a/.b')
297
check(['a', '.b'], 'a\\.b')
299
self.assertRaises(errors.BzrError, osutils.splitpath, 'a/../b')
302
class TestMacFuncsDirs(TestCaseInTempDir):
303
"""Test mac special functions that require directories."""
305
def test_getcwd(self):
306
# On Mac, this will actually create Ba\u030agfors
307
# but chdir will still work, because it accepts both paths
309
os.mkdir(u'B\xe5gfors')
311
raise TestSkipped("Unable to create Unicode filename")
313
os.chdir(u'B\xe5gfors')
314
self.assertEndsWith(osutils._mac_getcwd(), u'B\xe5gfors')
316
def test_getcwd_nonnorm(self):
317
# Test that _mac_getcwd() will normalize this path
319
os.mkdir(u'Ba\u030agfors')
321
raise TestSkipped("Unable to create Unicode filename")
323
os.chdir(u'Ba\u030agfors')
324
self.assertEndsWith(osutils._mac_getcwd(), u'B\xe5gfors')
327
class TestSplitLines(TestCase):
329
def test_split_unicode(self):
330
self.assertEqual([u'foo\n', u'bar\xae'],
331
osutils.split_lines(u'foo\nbar\xae'))
332
self.assertEqual([u'foo\n', u'bar\xae\n'],
333
osutils.split_lines(u'foo\nbar\xae\n'))
335
def test_split_with_carriage_returns(self):
336
self.assertEqual(['foo\rbar\n'],
337
osutils.split_lines('foo\rbar\n'))
340
class TestWalkDirs(TestCaseInTempDir):
342
def test_walkdirs(self):
351
self.build_tree(tree)
352
expected_dirblocks = [
354
[('0file', '0file', 'file'),
355
('1dir', '1dir', 'directory'),
356
('2file', '2file', 'file'),
360
[('1dir/0file', '0file', 'file'),
361
('1dir/1dir', '1dir', 'directory'),
364
(('1dir/1dir', './1dir/1dir'),
371
for dirdetail, dirblock in osutils.walkdirs('.'):
372
if len(dirblock) and dirblock[0][1] == '.bzr':
373
# this tests the filtering of selected paths
376
result.append((dirdetail, dirblock))
378
self.assertTrue(found_bzrdir)
379
self.assertEqual(expected_dirblocks,
380
[(dirinfo, [line[0:3] for line in block]) for dirinfo, block in result])
381
# you can search a subdir only, with a supplied prefix.
383
for dirblock in osutils.walkdirs('./1dir', '1dir'):
384
result.append(dirblock)
385
self.assertEqual(expected_dirblocks[1:],
386
[(dirinfo, [line[0:3] for line in block]) for dirinfo, block in result])
388
def assertPathCompare(self, path_less, path_greater):
389
"""check that path_less and path_greater compare correctly."""
390
self.assertEqual(0, osutils.compare_paths_prefix_order(
391
path_less, path_less))
392
self.assertEqual(0, osutils.compare_paths_prefix_order(
393
path_greater, path_greater))
394
self.assertEqual(-1, osutils.compare_paths_prefix_order(
395
path_less, path_greater))
396
self.assertEqual(1, osutils.compare_paths_prefix_order(
397
path_greater, path_less))
399
def test_compare_paths_prefix_order(self):
400
# root before all else
401
self.assertPathCompare("/", "/a")
403
self.assertPathCompare("/a", "/b")
404
self.assertPathCompare("/b", "/z")
405
# high dirs before lower.
406
self.assertPathCompare("/z", "/a/a")
407
# except if the deeper dir should be output first
408
self.assertPathCompare("/a/b/c", "/d/g")
409
# lexical betwen dirs of the same height
410
self.assertPathCompare("/a/z", "/z/z")
411
self.assertPathCompare("/a/c/z", "/a/d/e")
413
# this should also be consistent for no leading / paths
414
# root before all else
415
self.assertPathCompare("", "a")
417
self.assertPathCompare("a", "b")
418
self.assertPathCompare("b", "z")
419
# high dirs before lower.
420
self.assertPathCompare("z", "a/a")
421
# except if the deeper dir should be output first
422
self.assertPathCompare("a/b/c", "d/g")
423
# lexical betwen dirs of the same height
424
self.assertPathCompare("a/z", "z/z")
425
self.assertPathCompare("a/c/z", "a/d/e")
427
def test_path_prefix_sorting(self):
428
"""Doing a sort on path prefix should match our sample data."""
459
sorted(original_paths, key=osutils.path_prefix_key))
460
# using the comparison routine shoudl work too:
463
sorted(original_paths, cmp=osutils.compare_paths_prefix_order))
466
class TestCopyTree(TestCaseInTempDir):
468
def test_copy_basic_tree(self):
469
self.build_tree(['source/', 'source/a', 'source/b/', 'source/b/c'])
470
osutils.copy_tree('source', 'target')
471
self.assertEqual(['a', 'b'], os.listdir('target'))
472
self.assertEqual(['c'], os.listdir('target/b'))
474
def test_copy_tree_target_exists(self):
475
self.build_tree(['source/', 'source/a', 'source/b/', 'source/b/c',
477
osutils.copy_tree('source', 'target')
478
self.assertEqual(['a', 'b'], os.listdir('target'))
479
self.assertEqual(['c'], os.listdir('target/b'))
481
def test_copy_tree_symlinks(self):
482
if not osutils.has_symlinks():
484
self.build_tree(['source/'])
485
os.symlink('a/generic/path', 'source/lnk')
486
osutils.copy_tree('source', 'target')
487
self.assertEqual(['lnk'], os.listdir('target'))
488
self.assertEqual('a/generic/path', os.readlink('target/lnk'))
490
def test_copy_tree_handlers(self):
493
def file_handler(from_path, to_path):
494
processed_files.append(('f', from_path, to_path))
495
def dir_handler(from_path, to_path):
496
processed_files.append(('d', from_path, to_path))
497
def link_handler(from_path, to_path):
498
processed_links.append((from_path, to_path))
499
handlers = {'file':file_handler,
500
'directory':dir_handler,
501
'symlink':link_handler,
504
self.build_tree(['source/', 'source/a', 'source/b/', 'source/b/c'])
505
if osutils.has_symlinks():
506
os.symlink('a/generic/path', 'source/lnk')
507
osutils.copy_tree('source', 'target', handlers=handlers)
509
self.assertEqual([('d', 'source', 'target'),
510
('f', 'source/a', 'target/a'),
511
('d', 'source/b', 'target/b'),
512
('f', 'source/b/c', 'target/b/c'),
514
self.failIfExists('target')
515
if osutils.has_symlinks():
516
self.assertEqual([('source/lnk', 'target/lnk')], processed_links)
519
class TestTerminalEncoding(TestCase):
520
"""Test the auto-detection of proper terminal encoding."""
523
self._stdout = sys.stdout
524
self._stderr = sys.stderr
525
self._stdin = sys.stdin
526
self._user_encoding = bzrlib.user_encoding
528
self.addCleanup(self._reset)
530
sys.stdout = StringIOWrapper()
531
sys.stdout.encoding = 'stdout_encoding'
532
sys.stderr = StringIOWrapper()
533
sys.stderr.encoding = 'stderr_encoding'
534
sys.stdin = StringIOWrapper()
535
sys.stdin.encoding = 'stdin_encoding'
536
bzrlib.user_encoding = 'user_encoding'
539
sys.stdout = self._stdout
540
sys.stderr = self._stderr
541
sys.stdin = self._stdin
542
bzrlib.user_encoding = self._user_encoding
544
def test_get_terminal_encoding(self):
545
# first preference is stdout encoding
546
self.assertEqual('stdout_encoding', osutils.get_terminal_encoding())
548
sys.stdout.encoding = None
549
# if sys.stdout is None, fall back to sys.stdin
550
self.assertEqual('stdin_encoding', osutils.get_terminal_encoding())
552
sys.stdin.encoding = None
553
# and in the worst case, use bzrlib.user_encoding
554
self.assertEqual('user_encoding', osutils.get_terminal_encoding())
557
class TestSetUnsetEnv(TestCase):
558
"""Test updating the environment"""
561
super(TestSetUnsetEnv, self).setUp()
563
self.assertEqual(None, os.environ.get('BZR_TEST_ENV_VAR'),
564
'Environment was not cleaned up properly.'
565
' Variable BZR_TEST_ENV_VAR should not exist.')
567
if 'BZR_TEST_ENV_VAR' in os.environ:
568
del os.environ['BZR_TEST_ENV_VAR']
570
self.addCleanup(cleanup)
573
"""Test that we can set an env variable"""
574
old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'foo')
575
self.assertEqual(None, old)
576
self.assertEqual('foo', os.environ.get('BZR_TEST_ENV_VAR'))
578
def test_double_set(self):
579
"""Test that we get the old value out"""
580
osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'foo')
581
old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'bar')
582
self.assertEqual('foo', old)
583
self.assertEqual('bar', os.environ.get('BZR_TEST_ENV_VAR'))
585
def test_unicode(self):
586
"""Environment can only contain plain strings
588
So Unicode strings must be encoded.
590
# Try a few different characters, to see if we can get
591
# one that will be valid in the user_encoding
592
possible_vals = [u'm\xb5', u'\xe1', u'\u0410']
593
for uni_val in possible_vals:
595
env_val = uni_val.encode(bzrlib.user_encoding)
596
except UnicodeEncodeError:
597
# Try a different character
602
raise TestSkipped('Cannot find a unicode character that works in'
603
' encoding %s' % (bzrlib.user_encoding,))
605
old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', uni_val)
606
self.assertEqual(env_val, os.environ.get('BZR_TEST_ENV_VAR'))
608
def test_unset(self):
609
"""Test that passing None will remove the env var"""
610
osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'foo')
611
old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', None)
612
self.assertEqual('foo', old)
613
self.assertEqual(None, os.environ.get('BZR_TEST_ENV_VAR'))
614
self.failIf('BZR_TEST_ENV_VAR' in os.environ)