/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_osutils.py

  • Committer: Martin Pool
  • Date: 2007-09-03 04:45:38 UTC
  • mto: This revision was merged to the branch mainline in revision 2779.
  • Revision ID: mbp@sourcefrog.net-20070903044538-4iglni6xv5nkk737
Move actual tests from inventory_implementations __init__ into basics.py (Aaron)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
 
2
#
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
#
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
#
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
"""Tests for the osutils wrapper."""
 
18
 
 
19
import errno
 
20
import os
 
21
import socket
 
22
import stat
 
23
import sys
 
24
 
 
25
import bzrlib
 
26
from bzrlib import (
 
27
    errors,
 
28
    osutils,
 
29
    win32utils,
 
30
    )
 
31
from bzrlib.errors import BzrBadParameterNotUnicode, InvalidURL
 
32
from bzrlib.osutils import (
 
33
        is_inside_any,
 
34
        is_inside_or_parent_of_any,
 
35
        pathjoin,
 
36
        )
 
37
from bzrlib.tests import (
 
38
        StringIOWrapper,
 
39
        TestCase,
 
40
        TestCaseInTempDir,
 
41
        TestSkipped,
 
42
        )
 
43
 
 
44
 
 
45
class TestOSUtils(TestCaseInTempDir):
 
46
 
 
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'))
 
54
 
 
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'))
 
60
 
 
61
    def test_fancy_rename(self):
 
62
        # This should work everywhere
 
63
        def rename(a, b):
 
64
            osutils.fancy_rename(a, b,
 
65
                    rename_func=os.rename,
 
66
                    unlink_func=os.unlink)
 
67
 
 
68
        open('a', 'wb').write('something in a\n')
 
69
        rename('a', 'b')
 
70
        self.failIfExists('a')
 
71
        self.failUnlessExists('b')
 
72
        self.check_file_contents('b', 'something in a\n')
 
73
 
 
74
        open('a', 'wb').write('new something in a\n')
 
75
        rename('b', 'a')
 
76
 
 
77
        self.check_file_contents('a', 'something in a\n')
 
78
 
 
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')
 
86
 
 
87
        open('a', 'wb').write('new something in a\n')
 
88
        osutils.rename('b', 'a')
 
89
 
 
90
        self.check_file_contents('a', 'something in a\n')
 
91
 
 
92
    # TODO: test fancy_rename using a MemoryTransport
 
93
 
 
94
    def test_01_rand_chars_empty(self):
 
95
        result = osutils.rand_chars(0)
 
96
        self.assertEqual(result, '')
 
97
 
 
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}$')
 
103
 
 
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'))
 
112
 
 
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),
 
117
                         (['src'], 'src'),
 
118
                         ]:
 
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))
 
123
 
 
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'),
 
129
                         (['src'], 'src'),
 
130
                         ]:
 
131
            self.assert_(is_inside_or_parent_of_any(dirs, fn))
 
132
            
 
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))
 
137
 
 
138
    def test_rmtree(self):
 
139
        # Check to remove tree with read-only files/dirs
 
140
        os.mkdir('dir')
 
141
        f = file('dir/file', 'w')
 
142
        f.write('spam')
 
143
        f.close()
 
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')
 
150
 
 
151
        osutils.rmtree('dir')
 
152
 
 
153
        self.failIfExists('dir/file')
 
154
        self.failIfExists('dir')
 
155
 
 
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'))
 
163
        
 
164
        # TODO: jam 20060529 Test a block device
 
165
        try:
 
166
            os.lstat('/dev/null')
 
167
        except OSError, e:
 
168
            if e.errno not in (errno.ENOENT,):
 
169
                raise
 
170
        else:
 
171
            self.assertEquals('chardev', osutils.file_kind('/dev/null'))
 
172
 
 
173
        mkfifo = getattr(os, 'mkfifo', None)
 
174
        if mkfifo:
 
175
            mkfifo('fifo')
 
176
            try:
 
177
                self.assertEquals('fifo', osutils.file_kind('fifo'))
 
178
            finally:
 
179
                os.remove('fifo')
 
180
 
 
181
        AF_UNIX = getattr(socket, 'AF_UNIX', None)
 
182
        if AF_UNIX:
 
183
            s = socket.socket(AF_UNIX)
 
184
            s.bind('socket')
 
185
            try:
 
186
                self.assertEquals('socket', osutils.file_kind('socket'))
 
187
            finally:
 
188
                os.remove('socket')
 
189
 
 
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'), '+')
 
195
 
 
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())
 
200
            return
 
201
 
 
202
        orig_umask = osutils.get_umask()
 
203
        try:
 
204
            os.umask(0222)
 
205
            self.assertEqual(0222, osutils.get_umask())
 
206
            os.umask(0022)
 
207
            self.assertEqual(0022, osutils.get_umask())
 
208
            os.umask(0002)
 
209
            self.assertEqual(0002, osutils.get_umask())
 
210
            os.umask(0027)
 
211
            self.assertEqual(0027, osutils.get_umask())
 
212
        finally:
 
213
            os.umask(orig_umask)
 
214
 
 
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)
 
219
 
 
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)
 
244
 
 
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)
 
250
 
 
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('.')
 
255
        os.mkdir('bar')
 
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'))
 
262
        
 
263
        # Does not dereference terminal symlinks
 
264
        foo_path = osutils.pathjoin(cwd, 'foo')
 
265
        self.assertEqual(foo_path, osutils.dereference_path('./foo'))
 
266
 
 
267
        # Dereferences parent symlinks
 
268
        os.mkdir('bar/baz')
 
269
        baz_path = osutils.pathjoin(bar_path, 'baz')
 
270
        self.assertEqual(baz_path, osutils.dereference_path('./foo/baz'))
 
271
 
 
272
        # Dereferences parent symlinks that are the first path element
 
273
        self.assertEqual(baz_path, osutils.dereference_path('foo/baz'))
 
274
 
 
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))
 
278
 
 
279
 
 
280
    def test_changing_access(self):
 
281
        f = file('file', 'w')
 
282
        f.write('monkey')
 
283
        f.close()
 
284
 
 
285
        # Make a file readonly
 
286
        osutils.make_readonly('file')
 
287
        mode = osutils.lstat('file').st_mode
 
288
        self.assertEqual(mode, mode & 0777555)
 
289
 
 
290
        # Make a file writable
 
291
        osutils.make_writable('file')
 
292
        mode = osutils.lstat('file').st_mode
 
293
        self.assertEqual(mode, mode | 0200)
 
294
 
 
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')
 
300
 
 
301
 
 
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")
 
307
 
 
308
 
 
309
class TestSafeUnicode(TestCase):
 
310
 
 
311
    def test_from_ascii_string(self):
 
312
        self.assertEqual(u'foobar', osutils.safe_unicode('foobar'))
 
313
 
 
314
    def test_from_unicode_string_ascii_contents(self):
 
315
        self.assertEqual(u'bargam', osutils.safe_unicode(u'bargam'))
 
316
 
 
317
    def test_from_unicode_string_unicode_contents(self):
 
318
        self.assertEqual(u'bargam\xae', osutils.safe_unicode(u'bargam\xae'))
 
319
 
 
320
    def test_from_utf8_string(self):
 
321
        self.assertEqual(u'foo\xae', osutils.safe_unicode('foo\xc2\xae'))
 
322
 
 
323
    def test_bad_utf8_string(self):
 
324
        self.assertRaises(BzrBadParameterNotUnicode,
 
325
                          osutils.safe_unicode,
 
326
                          '\xbb\xbb')
 
327
 
 
328
 
 
329
class TestSafeUtf8(TestCase):
 
330
 
 
331
    def test_from_ascii_string(self):
 
332
        f = 'foobar'
 
333
        self.assertEqual('foobar', osutils.safe_utf8(f))
 
334
 
 
335
    def test_from_unicode_string_ascii_contents(self):
 
336
        self.assertEqual('bargam', osutils.safe_utf8(u'bargam'))
 
337
 
 
338
    def test_from_unicode_string_unicode_contents(self):
 
339
        self.assertEqual('bargam\xc2\xae', osutils.safe_utf8(u'bargam\xae'))
 
340
 
 
341
    def test_from_utf8_string(self):
 
342
        self.assertEqual('foo\xc2\xae', osutils.safe_utf8('foo\xc2\xae'))
 
343
 
 
344
    def test_bad_utf8_string(self):
 
345
        self.assertRaises(BzrBadParameterNotUnicode,
 
346
                          osutils.safe_utf8, '\xbb\xbb')
 
347
 
 
348
 
 
349
class TestSafeRevisionId(TestCase):
 
350
 
 
351
    def test_from_ascii_string(self):
 
352
        self.assertEqual('foobar', osutils.safe_revision_id('foobar'))
 
353
 
 
354
    def test_from_unicode_string_ascii_contents(self):
 
355
        self.assertEqual('bargam',
 
356
                         osutils.safe_revision_id(u'bargam', warn=False))
 
357
 
 
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'))
 
362
 
 
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))
 
366
 
 
367
    def test_from_utf8_string(self):
 
368
        self.assertEqual('foo\xc2\xae',
 
369
                         osutils.safe_revision_id('foo\xc2\xae'))
 
370
 
 
371
    def test_none(self):
 
372
        """Currently, None is a valid revision_id"""
 
373
        self.assertEqual(None, osutils.safe_revision_id(None))
 
374
 
 
375
 
 
376
class TestSafeFileId(TestCase):
 
377
 
 
378
    def test_from_ascii_string(self):
 
379
        self.assertEqual('foobar', osutils.safe_file_id('foobar'))
 
380
 
 
381
    def test_from_unicode_string_ascii_contents(self):
 
382
        self.assertEqual('bargam', osutils.safe_file_id(u'bargam', warn=False))
 
383
 
 
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'))
 
388
 
 
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))
 
392
 
 
393
    def test_from_utf8_string(self):
 
394
        self.assertEqual('foo\xc2\xae',
 
395
                         osutils.safe_file_id('foo\xc2\xae'))
 
396
 
 
397
    def test_none(self):
 
398
        """Currently, None is a valid revision_id"""
 
399
        self.assertEqual(None, osutils.safe_file_id(None))
 
400
 
 
401
 
 
402
class TestWin32Funcs(TestCase):
 
403
    """Test that the _win32 versions of os utilities return appropriate paths."""
 
404
 
 
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'))
 
410
 
 
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'))
 
414
 
 
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'))
 
422
 
 
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'))
 
426
 
 
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])
 
437
 
 
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'))
 
442
 
 
443
    def test_win98_abspath(self):
 
444
        # absolute path
 
445
        self.assertEqual('C:/foo', osutils._win98_abspath('C:\\foo'))
 
446
        self.assertEqual('C:/foo', osutils._win98_abspath('C:/foo'))
 
447
        # UNC path
 
448
        self.assertEqual('//HOST/path', osutils._win98_abspath(r'\\HOST\path'))
 
449
        self.assertEqual('//HOST/path', osutils._win98_abspath('//HOST/path'))
 
450
        # relative 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'))
 
455
        # unicode path
 
456
        u = u'\u1234'
 
457
        self.assertEqual(cwd+'/'+u, osutils._win98_abspath(u))
 
458
 
 
459
 
 
460
class TestWin32FuncsDirs(TestCaseInTempDir):
 
461
    """Test win32 functions that create files."""
 
462
    
 
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
 
467
        try:
 
468
            os.mkdir(u'mu-\xb5')
 
469
        except UnicodeError:
 
470
            raise TestSkipped("Unable to create Unicode filename")
 
471
 
 
472
        os.chdir(u'mu-\xb5')
 
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')
 
478
 
 
479
    def test_mkdtemp(self):
 
480
        tmpdir = osutils._win32_mkdtemp(dir='.')
 
481
        self.assertFalse('\\' in tmpdir)
 
482
 
 
483
    def test_rename(self):
 
484
        a = open('a', 'wb')
 
485
        a.write('foo\n')
 
486
        a.close()
 
487
        b = open('b', 'wb')
 
488
        b.write('baz\n')
 
489
        b.close()
 
490
 
 
491
        osutils._win32_rename('b', 'a')
 
492
        self.failUnlessExists('a')
 
493
        self.failIfExists('b')
 
494
        self.assertFileEqual('baz\n', 'a')
 
495
 
 
496
    def test_rename_missing_file(self):
 
497
        a = open('a', 'wb')
 
498
        a.write('foo\n')
 
499
        a.close()
 
500
 
 
501
        try:
 
502
            osutils._win32_rename('b', 'a')
 
503
        except (IOError, OSError), e:
 
504
            self.assertEqual(errno.ENOENT, e.errno)
 
505
        self.assertFileEqual('foo\n', 'a')
 
506
 
 
507
    def test_rename_missing_dir(self):
 
508
        os.mkdir('a')
 
509
        try:
 
510
            osutils._win32_rename('b', 'a')
 
511
        except (IOError, OSError), e:
 
512
            self.assertEqual(errno.ENOENT, e.errno)
 
513
 
 
514
    def test_rename_current_dir(self):
 
515
        os.mkdir('a')
 
516
        os.chdir('a')
 
517
        # You can't rename the working directory
 
518
        # doing rename non-existant . usually
 
519
        # just raises ENOENT, since non-existant
 
520
        # doesn't exist.
 
521
        try:
 
522
            osutils._win32_rename('b', '.')
 
523
        except (IOError, OSError), e:
 
524
            self.assertEqual(errno.ENOENT, e.errno)
 
525
 
 
526
    def test_splitpath(self):
 
527
        def check(expected, path):
 
528
            self.assertEqual(expected, osutils.splitpath(path))
 
529
 
 
530
        check(['a'], 'a')
 
531
        check(['a', 'b'], 'a/b')
 
532
        check(['a', 'b'], 'a/./b')
 
533
        check(['a', '.b'], 'a/.b')
 
534
        check(['a', '.b'], 'a\\.b')
 
535
 
 
536
        self.assertRaises(errors.BzrError, osutils.splitpath, 'a/../b')
 
537
 
 
538
 
 
539
class TestMacFuncsDirs(TestCaseInTempDir):
 
540
    """Test mac special functions that require directories."""
 
541
 
 
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
 
545
        try:
 
546
            os.mkdir(u'B\xe5gfors')
 
547
        except UnicodeError:
 
548
            raise TestSkipped("Unable to create Unicode filename")
 
549
 
 
550
        os.chdir(u'B\xe5gfors')
 
551
        self.assertEndsWith(osutils._mac_getcwd(), u'B\xe5gfors')
 
552
 
 
553
    def test_getcwd_nonnorm(self):
 
554
        # Test that _mac_getcwd() will normalize this path
 
555
        try:
 
556
            os.mkdir(u'Ba\u030agfors')
 
557
        except UnicodeError:
 
558
            raise TestSkipped("Unable to create Unicode filename")
 
559
 
 
560
        os.chdir(u'Ba\u030agfors')
 
561
        self.assertEndsWith(osutils._mac_getcwd(), u'B\xe5gfors')
 
562
 
 
563
 
 
564
class TestSplitLines(TestCase):
 
565
 
 
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'))
 
571
 
 
572
    def test_split_with_carriage_returns(self):
 
573
        self.assertEqual(['foo\rbar\n'],
 
574
                         osutils.split_lines('foo\rbar\n'))
 
575
 
 
576
 
 
577
class TestWalkDirs(TestCaseInTempDir):
 
578
 
 
579
    def test_walkdirs(self):
 
580
        tree = [
 
581
            '.bzr',
 
582
            '0file',
 
583
            '1dir/',
 
584
            '1dir/0file',
 
585
            '1dir/1dir/',
 
586
            '2file'
 
587
            ]
 
588
        self.build_tree(tree)
 
589
        expected_dirblocks = [
 
590
                (('', '.'),
 
591
                 [('0file', '0file', 'file'),
 
592
                  ('1dir', '1dir', 'directory'),
 
593
                  ('2file', '2file', 'file'),
 
594
                 ]
 
595
                ),
 
596
                (('1dir', './1dir'),
 
597
                 [('1dir/0file', '0file', 'file'),
 
598
                  ('1dir/1dir', '1dir', 'directory'),
 
599
                 ]
 
600
                ),
 
601
                (('1dir/1dir', './1dir/1dir'),
 
602
                 [
 
603
                 ]
 
604
                ),
 
605
            ]
 
606
        result = []
 
607
        found_bzrdir = False
 
608
        for dirdetail, dirblock in osutils.walkdirs('.'):
 
609
            if len(dirblock) and dirblock[0][1] == '.bzr':
 
610
                # this tests the filtering of selected paths
 
611
                found_bzrdir = True
 
612
                del dirblock[0]
 
613
            result.append((dirdetail, dirblock))
 
614
 
 
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.
 
619
        result = []
 
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])
 
624
 
 
625
    def test__walkdirs_utf8(self):
 
626
        tree = [
 
627
            '.bzr',
 
628
            '0file',
 
629
            '1dir/',
 
630
            '1dir/0file',
 
631
            '1dir/1dir/',
 
632
            '2file'
 
633
            ]
 
634
        self.build_tree(tree)
 
635
        expected_dirblocks = [
 
636
                (('', '.'),
 
637
                 [('0file', '0file', 'file'),
 
638
                  ('1dir', '1dir', 'directory'),
 
639
                  ('2file', '2file', 'file'),
 
640
                 ]
 
641
                ),
 
642
                (('1dir', './1dir'),
 
643
                 [('1dir/0file', '0file', 'file'),
 
644
                  ('1dir/1dir', '1dir', 'directory'),
 
645
                 ]
 
646
                ),
 
647
                (('1dir/1dir', './1dir/1dir'),
 
648
                 [
 
649
                 ]
 
650
                ),
 
651
            ]
 
652
        result = []
 
653
        found_bzrdir = False
 
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
 
657
                found_bzrdir = True
 
658
                del dirblock[0]
 
659
            result.append((dirdetail, dirblock))
 
660
 
 
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.
 
665
        result = []
 
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])
 
670
 
 
671
    def _filter_out_stat(self, result):
 
672
        """Filter out the stat value from the walkdirs result"""
 
673
        for dirdetail, dirblock in result:
 
674
            new_dirblock = []
 
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
 
679
 
 
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'
 
685
        tree = [
 
686
            name0,
 
687
            name1 + '/',
 
688
            name1 + '/' + name0,
 
689
            name1 + '/' + name1 + '/',
 
690
            name2,
 
691
            ]
 
692
        try:
 
693
            self.build_tree(tree)
 
694
        except UnicodeError:
 
695
            raise TestSkipped('Could not represent Unicode chars'
 
696
                              ' in current encoding.')
 
697
        expected_dirblocks = [
 
698
                ((u'', u'.'),
 
699
                 [(name0, name0, 'file', './' + name0),
 
700
                  (name1, name1, 'directory', './' + name1),
 
701
                  (name2, name2, 'file', './' + name2),
 
702
                 ]
 
703
                ),
 
704
                ((name1, './' + name1),
 
705
                 [(name1 + '/' + name0, name0, 'file', './' + name1
 
706
                                                        + '/' + name0),
 
707
                  (name1 + '/' + name1, name1, 'directory', './' + name1
 
708
                                                            + '/' + name1),
 
709
                 ]
 
710
                ),
 
711
                ((name1 + '/' + name1, './' + name1 + '/' + name1),
 
712
                 [
 
713
                 ]
 
714
                ),
 
715
            ]
 
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)
 
722
 
 
723
    def test_unicode__walkdirs_utf8(self):
 
724
        """Walkdirs_utf8 should always return utf8 paths.
 
725
 
 
726
        The abspath portion might be in unicode or utf-8
 
727
        """
 
728
        name0 = u'0file-\xb6'
 
729
        name1 = u'1dir-\u062c\u0648'
 
730
        name2 = u'2file-\u0633'
 
731
        tree = [
 
732
            name0,
 
733
            name1 + '/',
 
734
            name1 + '/' + name0,
 
735
            name1 + '/' + name1 + '/',
 
736
            name2,
 
737
            ]
 
738
        try:
 
739
            self.build_tree(tree)
 
740
        except UnicodeError:
 
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')
 
746
 
 
747
        expected_dirblocks = [
 
748
                (('', '.'),
 
749
                 [(name0, name0, 'file', './' + name0),
 
750
                  (name1, name1, 'directory', './' + name1),
 
751
                  (name2, name2, 'file', './' + name2),
 
752
                 ]
 
753
                ),
 
754
                ((name1, './' + name1),
 
755
                 [(name1 + '/' + name0, name0, 'file', './' + name1
 
756
                                                        + '/' + name0),
 
757
                  (name1 + '/' + name1, name1, 'directory', './' + name1
 
758
                                                            + '/' + name1),
 
759
                 ]
 
760
                ),
 
761
                ((name1 + '/' + name1, './' + name1 + '/' + name1),
 
762
                 [
 
763
                 ]
 
764
                ),
 
765
            ]
 
766
        result = []
 
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')
 
777
            new_dirblock = []
 
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)
 
786
 
 
787
    def test_unicode__walkdirs_unicode_to_utf8(self):
 
788
        """walkdirs_unicode_to_utf8 should be a safe fallback everywhere
 
789
 
 
790
        The abspath portion should be in unicode
 
791
        """
 
792
        name0u = u'0file-\xb6'
 
793
        name1u = u'1dir-\u062c\u0648'
 
794
        name2u = u'2file-\u0633'
 
795
        tree = [
 
796
            name0u,
 
797
            name1u + '/',
 
798
            name1u + '/' + name0u,
 
799
            name1u + '/' + name1u + '/',
 
800
            name2u,
 
801
            ]
 
802
        try:
 
803
            self.build_tree(tree)
 
804
        except UnicodeError:
 
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')
 
810
 
 
811
        # All of the abspaths should be in unicode, all of the relative paths
 
812
        # should be in utf8
 
813
        expected_dirblocks = [
 
814
                (('', '.'),
 
815
                 [(name0, name0, 'file', './' + name0u),
 
816
                  (name1, name1, 'directory', './' + name1u),
 
817
                  (name2, name2, 'file', './' + name2u),
 
818
                 ]
 
819
                ),
 
820
                ((name1, './' + name1u),
 
821
                 [(name1 + '/' + name0, name0, 'file', './' + name1u
 
822
                                                        + '/' + name0u),
 
823
                  (name1 + '/' + name1, name1, 'directory', './' + name1u
 
824
                                                            + '/' + name1u),
 
825
                 ]
 
826
                ),
 
827
                ((name1 + '/' + name1, './' + name1u + '/' + name1u),
 
828
                 [
 
829
                 ]
 
830
                ),
 
831
            ]
 
832
        result = list(osutils._walkdirs_unicode_to_utf8('.'))
 
833
        self._filter_out_stat(result)
 
834
        self.assertEqual(expected_dirblocks, result)
 
835
 
 
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))
 
846
 
 
847
    def test_compare_paths_prefix_order(self):
 
848
        # root before all else
 
849
        self.assertPathCompare("/", "/a")
 
850
        # alpha within a dir
 
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")
 
860
 
 
861
        # this should also be consistent for no leading / paths
 
862
        # root before all else
 
863
        self.assertPathCompare("", "a")
 
864
        # alpha within a dir
 
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")
 
874
 
 
875
    def test_path_prefix_sorting(self):
 
876
        """Doing a sort on path prefix should match our sample data."""
 
877
        original_paths = [
 
878
            'a',
 
879
            'a/b',
 
880
            'a/b/c',
 
881
            'b',
 
882
            'b/c',
 
883
            'd',
 
884
            'd/e',
 
885
            'd/e/f',
 
886
            'd/f',
 
887
            'd/g',
 
888
            'g',
 
889
            ]
 
890
 
 
891
        dir_sorted_paths = [
 
892
            'a',
 
893
            'b',
 
894
            'd',
 
895
            'g',
 
896
            'a/b',
 
897
            'a/b/c',
 
898
            'b/c',
 
899
            'd/e',
 
900
            'd/f',
 
901
            'd/g',
 
902
            'd/e/f',
 
903
            ]
 
904
 
 
905
        self.assertEqual(
 
906
            dir_sorted_paths,
 
907
            sorted(original_paths, key=osutils.path_prefix_key))
 
908
        # using the comparison routine shoudl work too:
 
909
        self.assertEqual(
 
910
            dir_sorted_paths,
 
911
            sorted(original_paths, cmp=osutils.compare_paths_prefix_order))
 
912
 
 
913
 
 
914
class TestCopyTree(TestCaseInTempDir):
 
915
    
 
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'))
 
921
 
 
922
    def test_copy_tree_target_exists(self):
 
923
        self.build_tree(['source/', 'source/a', 'source/b/', 'source/b/c',
 
924
                         'target/'])
 
925
        osutils.copy_tree('source', 'target')
 
926
        self.assertEqual(['a', 'b'], sorted(os.listdir('target')))
 
927
        self.assertEqual(['c'], os.listdir('target/b'))
 
928
 
 
929
    def test_copy_tree_symlinks(self):
 
930
        if not osutils.has_symlinks():
 
931
            return
 
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'))
 
937
 
 
938
    def test_copy_tree_handlers(self):
 
939
        processed_files = []
 
940
        processed_links = []
 
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,
 
950
                   }
 
951
 
 
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)
 
956
 
 
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'),
 
961
                         ], processed_files)
 
962
        self.failIfExists('target')
 
963
        if osutils.has_symlinks():
 
964
            self.assertEqual([('source/lnk', 'target/lnk')], processed_links)
 
965
 
 
966
 
 
967
#class TestTerminalEncoding has been moved to test_osutils_encodings.py
 
968
# [bialix] 2006/12/26
 
969
 
 
970
 
 
971
class TestSetUnsetEnv(TestCase):
 
972
    """Test updating the environment"""
 
973
 
 
974
    def setUp(self):
 
975
        super(TestSetUnsetEnv, self).setUp()
 
976
 
 
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.')
 
980
        def cleanup():
 
981
            if 'BZR_TEST_ENV_VAR' in os.environ:
 
982
                del os.environ['BZR_TEST_ENV_VAR']
 
983
 
 
984
        self.addCleanup(cleanup)
 
985
 
 
986
    def test_set(self):
 
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'))
 
991
 
 
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'))
 
998
 
 
999
    def test_unicode(self):
 
1000
        """Environment can only contain plain strings
 
1001
        
 
1002
        So Unicode strings must be encoded.
 
1003
        """
 
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:
 
1008
            try:
 
1009
                env_val = uni_val.encode(bzrlib.user_encoding)
 
1010
            except UnicodeEncodeError:
 
1011
                # Try a different character
 
1012
                pass
 
1013
            else:
 
1014
                break
 
1015
        else:
 
1016
            raise TestSkipped('Cannot find a unicode character that works in'
 
1017
                              ' encoding %s' % (bzrlib.user_encoding,))
 
1018
 
 
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'))
 
1021
 
 
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)
 
1029
 
 
1030
 
 
1031
class TestLocalTimeOffset(TestCase):
 
1032
 
 
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
 
1038
        # either direction.
 
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
 
1041
        # probably wrong.
 
1042
        eighteen_hours = 18 * 3600
 
1043
        self.assertTrue(-eighteen_hours < offset < eighteen_hours)
 
1044
 
 
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)