~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/tests/test_osutils.py

  • Committer: Martin Pool
  • Date: 2005-05-03 08:00:27 UTC
  • Revision ID: mbp@sourcefrog.net-20050503080027-908edb5b39982198
doc

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
 
        probe_unicode_in_user_encoding,
39
 
        StringIOWrapper,
40
 
        TestCase,
41
 
        TestCaseInTempDir,
42
 
        TestSkipped,
43
 
        )
44
 
 
45
 
 
46
 
class TestOSUtils(TestCaseInTempDir):
47
 
 
48
 
    def test_contains_whitespace(self):
49
 
        self.failUnless(osutils.contains_whitespace(u' '))
50
 
        self.failUnless(osutils.contains_whitespace(u'hello there'))
51
 
        self.failUnless(osutils.contains_whitespace(u'hellothere\n'))
52
 
        self.failUnless(osutils.contains_whitespace(u'hello\nthere'))
53
 
        self.failUnless(osutils.contains_whitespace(u'hello\rthere'))
54
 
        self.failUnless(osutils.contains_whitespace(u'hello\tthere'))
55
 
 
56
 
        # \xa0 is "Non-breaking-space" which on some python locales thinks it
57
 
        # is whitespace, but we do not.
58
 
        self.failIf(osutils.contains_whitespace(u''))
59
 
        self.failIf(osutils.contains_whitespace(u'hellothere'))
60
 
        self.failIf(osutils.contains_whitespace(u'hello\xa0there'))
61
 
 
62
 
    def test_fancy_rename(self):
63
 
        # This should work everywhere
64
 
        def rename(a, b):
65
 
            osutils.fancy_rename(a, b,
66
 
                    rename_func=os.rename,
67
 
                    unlink_func=os.unlink)
68
 
 
69
 
        open('a', 'wb').write('something in a\n')
70
 
        rename('a', 'b')
71
 
        self.failIfExists('a')
72
 
        self.failUnlessExists('b')
73
 
        self.check_file_contents('b', 'something in a\n')
74
 
 
75
 
        open('a', 'wb').write('new something in a\n')
76
 
        rename('b', 'a')
77
 
 
78
 
        self.check_file_contents('a', 'something in a\n')
79
 
 
80
 
    def test_rename(self):
81
 
        # Rename should be semi-atomic on all platforms
82
 
        open('a', 'wb').write('something in a\n')
83
 
        osutils.rename('a', 'b')
84
 
        self.failIfExists('a')
85
 
        self.failUnlessExists('b')
86
 
        self.check_file_contents('b', 'something in a\n')
87
 
 
88
 
        open('a', 'wb').write('new something in a\n')
89
 
        osutils.rename('b', 'a')
90
 
 
91
 
        self.check_file_contents('a', 'something in a\n')
92
 
 
93
 
    # TODO: test fancy_rename using a MemoryTransport
94
 
 
95
 
    def test_01_rand_chars_empty(self):
96
 
        result = osutils.rand_chars(0)
97
 
        self.assertEqual(result, '')
98
 
 
99
 
    def test_02_rand_chars_100(self):
100
 
        result = osutils.rand_chars(100)
101
 
        self.assertEqual(len(result), 100)
102
 
        self.assertEqual(type(result), str)
103
 
        self.assertContainsRe(result, r'^[a-z0-9]{100}$')
104
 
 
105
 
    def test_is_inside(self):
106
 
        is_inside = osutils.is_inside
107
 
        self.assertTrue(is_inside('src', 'src/foo.c'))
108
 
        self.assertFalse(is_inside('src', 'srccontrol'))
109
 
        self.assertTrue(is_inside('src', 'src/a/a/a/foo.c'))
110
 
        self.assertTrue(is_inside('foo.c', 'foo.c'))
111
 
        self.assertFalse(is_inside('foo.c', ''))
112
 
        self.assertTrue(is_inside('', 'foo.c'))
113
 
 
114
 
    def test_is_inside_any(self):
115
 
        SRC_FOO_C = pathjoin('src', 'foo.c')
116
 
        for dirs, fn in [(['src', 'doc'], SRC_FOO_C),
117
 
                         (['src'], SRC_FOO_C),
118
 
                         (['src'], 'src'),
119
 
                         ]:
120
 
            self.assert_(is_inside_any(dirs, fn))
121
 
        for dirs, fn in [(['src'], 'srccontrol'),
122
 
                         (['src'], 'srccontrol/foo')]:
123
 
            self.assertFalse(is_inside_any(dirs, fn))
124
 
 
125
 
    def test_is_inside_or_parent_of_any(self):
126
 
        for dirs, fn in [(['src', 'doc'], 'src/foo.c'),
127
 
                         (['src'], 'src/foo.c'),
128
 
                         (['src/bar.c'], 'src'),
129
 
                         (['src/bar.c', 'bla/foo.c'], 'src'),
130
 
                         (['src'], 'src'),
131
 
                         ]:
132
 
            self.assert_(is_inside_or_parent_of_any(dirs, fn))
133
 
            
134
 
        for dirs, fn in [(['src'], 'srccontrol'),
135
 
                         (['srccontrol/foo.c'], 'src'),
136
 
                         (['src'], 'srccontrol/foo')]:
137
 
            self.assertFalse(is_inside_or_parent_of_any(dirs, fn))
138
 
 
139
 
    def test_rmtree(self):
140
 
        # Check to remove tree with read-only files/dirs
141
 
        os.mkdir('dir')
142
 
        f = file('dir/file', 'w')
143
 
        f.write('spam')
144
 
        f.close()
145
 
        # would like to also try making the directory readonly, but at the
146
 
        # moment python shutil.rmtree doesn't handle that properly - it would
147
 
        # need to chmod the directory before removing things inside it - deferred
148
 
        # for now -- mbp 20060505
149
 
        # osutils.make_readonly('dir')
150
 
        osutils.make_readonly('dir/file')
151
 
 
152
 
        osutils.rmtree('dir')
153
 
 
154
 
        self.failIfExists('dir/file')
155
 
        self.failIfExists('dir')
156
 
 
157
 
    def test_file_kind(self):
158
 
        self.build_tree(['file', 'dir/'])
159
 
        self.assertEquals('file', osutils.file_kind('file'))
160
 
        self.assertEquals('directory', osutils.file_kind('dir/'))
161
 
        if osutils.has_symlinks():
162
 
            os.symlink('symlink', 'symlink')
163
 
            self.assertEquals('symlink', osutils.file_kind('symlink'))
164
 
        
165
 
        # TODO: jam 20060529 Test a block device
166
 
        try:
167
 
            os.lstat('/dev/null')
168
 
        except OSError, e:
169
 
            if e.errno not in (errno.ENOENT,):
170
 
                raise
171
 
        else:
172
 
            self.assertEquals('chardev', osutils.file_kind('/dev/null'))
173
 
 
174
 
        mkfifo = getattr(os, 'mkfifo', None)
175
 
        if mkfifo:
176
 
            mkfifo('fifo')
177
 
            try:
178
 
                self.assertEquals('fifo', osutils.file_kind('fifo'))
179
 
            finally:
180
 
                os.remove('fifo')
181
 
 
182
 
        AF_UNIX = getattr(socket, 'AF_UNIX', None)
183
 
        if AF_UNIX:
184
 
            s = socket.socket(AF_UNIX)
185
 
            s.bind('socket')
186
 
            try:
187
 
                self.assertEquals('socket', osutils.file_kind('socket'))
188
 
            finally:
189
 
                os.remove('socket')
190
 
 
191
 
    def test_kind_marker(self):
192
 
        self.assertEqual(osutils.kind_marker('file'), '')
193
 
        self.assertEqual(osutils.kind_marker('directory'), '/')
194
 
        self.assertEqual(osutils.kind_marker('symlink'), '@')
195
 
        self.assertEqual(osutils.kind_marker('tree-reference'), '+')
196
 
 
197
 
    def test_get_umask(self):
198
 
        if sys.platform == 'win32':
199
 
            # umask always returns '0', no way to set it
200
 
            self.assertEqual(0, osutils.get_umask())
201
 
            return
202
 
 
203
 
        orig_umask = osutils.get_umask()
204
 
        try:
205
 
            os.umask(0222)
206
 
            self.assertEqual(0222, osutils.get_umask())
207
 
            os.umask(0022)
208
 
            self.assertEqual(0022, osutils.get_umask())
209
 
            os.umask(0002)
210
 
            self.assertEqual(0002, osutils.get_umask())
211
 
            os.umask(0027)
212
 
            self.assertEqual(0027, osutils.get_umask())
213
 
        finally:
214
 
            os.umask(orig_umask)
215
 
 
216
 
    def assertFormatedDelta(self, expected, seconds):
217
 
        """Assert osutils.format_delta formats as expected"""
218
 
        actual = osutils.format_delta(seconds)
219
 
        self.assertEqual(expected, actual)
220
 
 
221
 
    def test_format_delta(self):
222
 
        self.assertFormatedDelta('0 seconds ago', 0)
223
 
        self.assertFormatedDelta('1 second ago', 1)
224
 
        self.assertFormatedDelta('10 seconds ago', 10)
225
 
        self.assertFormatedDelta('59 seconds ago', 59)
226
 
        self.assertFormatedDelta('89 seconds ago', 89)
227
 
        self.assertFormatedDelta('1 minute, 30 seconds ago', 90)
228
 
        self.assertFormatedDelta('3 minutes, 0 seconds ago', 180)
229
 
        self.assertFormatedDelta('3 minutes, 1 second ago', 181)
230
 
        self.assertFormatedDelta('10 minutes, 15 seconds ago', 615)
231
 
        self.assertFormatedDelta('30 minutes, 59 seconds ago', 1859)
232
 
        self.assertFormatedDelta('31 minutes, 0 seconds ago', 1860)
233
 
        self.assertFormatedDelta('60 minutes, 0 seconds ago', 3600)
234
 
        self.assertFormatedDelta('89 minutes, 59 seconds ago', 5399)
235
 
        self.assertFormatedDelta('1 hour, 30 minutes ago', 5400)
236
 
        self.assertFormatedDelta('2 hours, 30 minutes ago', 9017)
237
 
        self.assertFormatedDelta('10 hours, 0 minutes ago', 36000)
238
 
        self.assertFormatedDelta('24 hours, 0 minutes ago', 86400)
239
 
        self.assertFormatedDelta('35 hours, 59 minutes ago', 129599)
240
 
        self.assertFormatedDelta('36 hours, 0 minutes ago', 129600)
241
 
        self.assertFormatedDelta('36 hours, 0 minutes ago', 129601)
242
 
        self.assertFormatedDelta('36 hours, 1 minute ago', 129660)
243
 
        self.assertFormatedDelta('36 hours, 1 minute ago', 129661)
244
 
        self.assertFormatedDelta('84 hours, 10 minutes ago', 303002)
245
 
 
246
 
        # We handle when time steps the wrong direction because computers
247
 
        # don't have synchronized clocks.
248
 
        self.assertFormatedDelta('84 hours, 10 minutes in the future', -303002)
249
 
        self.assertFormatedDelta('1 second in the future', -1)
250
 
        self.assertFormatedDelta('2 seconds in the future', -2)
251
 
 
252
 
    def test_dereference_path(self):
253
 
        if not osutils.has_symlinks():
254
 
            raise TestSkipped('Symlinks are not supported on this platform')
255
 
        cwd = osutils.realpath('.')
256
 
        os.mkdir('bar')
257
 
        bar_path = osutils.pathjoin(cwd, 'bar')
258
 
        # Using './' to avoid bug #1213894 (first path component not
259
 
        # dereferenced) in Python 2.4.1 and earlier
260
 
        self.assertEqual(bar_path, osutils.realpath('./bar'))
261
 
        os.symlink('bar', 'foo')
262
 
        self.assertEqual(bar_path, osutils.realpath('./foo'))
263
 
        
264
 
        # Does not dereference terminal symlinks
265
 
        foo_path = osutils.pathjoin(cwd, 'foo')
266
 
        self.assertEqual(foo_path, osutils.dereference_path('./foo'))
267
 
 
268
 
        # Dereferences parent symlinks
269
 
        os.mkdir('bar/baz')
270
 
        baz_path = osutils.pathjoin(bar_path, 'baz')
271
 
        self.assertEqual(baz_path, osutils.dereference_path('./foo/baz'))
272
 
 
273
 
        # Dereferences parent symlinks that are the first path element
274
 
        self.assertEqual(baz_path, osutils.dereference_path('foo/baz'))
275
 
 
276
 
        # Dereferences parent symlinks in absolute paths
277
 
        foo_baz_path = osutils.pathjoin(foo_path, 'baz')
278
 
        self.assertEqual(baz_path, osutils.dereference_path(foo_baz_path))
279
 
 
280
 
 
281
 
    def test_changing_access(self):
282
 
        f = file('file', 'w')
283
 
        f.write('monkey')
284
 
        f.close()
285
 
 
286
 
        # Make a file readonly
287
 
        osutils.make_readonly('file')
288
 
        mode = osutils.lstat('file').st_mode
289
 
        self.assertEqual(mode, mode & 0777555)
290
 
 
291
 
        # Make a file writable
292
 
        osutils.make_writable('file')
293
 
        mode = osutils.lstat('file').st_mode
294
 
        self.assertEqual(mode, mode | 0200)
295
 
 
296
 
        if osutils.has_symlinks():
297
 
            # should not error when handed a symlink
298
 
            os.symlink('nonexistent', 'dangling')
299
 
            osutils.make_readonly('dangling')
300
 
            osutils.make_writable('dangling')
301
 
 
302
 
 
303
 
    def test_kind_marker(self):
304
 
        self.assertEqual("", osutils.kind_marker("file"))
305
 
        self.assertEqual("/", osutils.kind_marker(osutils._directory_kind))
306
 
        self.assertEqual("@", osutils.kind_marker("symlink"))
307
 
        self.assertRaises(errors.BzrError, osutils.kind_marker, "unknown")
308
 
 
309
 
 
310
 
class TestSafeUnicode(TestCase):
311
 
 
312
 
    def test_from_ascii_string(self):
313
 
        self.assertEqual(u'foobar', osutils.safe_unicode('foobar'))
314
 
 
315
 
    def test_from_unicode_string_ascii_contents(self):
316
 
        self.assertEqual(u'bargam', osutils.safe_unicode(u'bargam'))
317
 
 
318
 
    def test_from_unicode_string_unicode_contents(self):
319
 
        self.assertEqual(u'bargam\xae', osutils.safe_unicode(u'bargam\xae'))
320
 
 
321
 
    def test_from_utf8_string(self):
322
 
        self.assertEqual(u'foo\xae', osutils.safe_unicode('foo\xc2\xae'))
323
 
 
324
 
    def test_bad_utf8_string(self):
325
 
        self.assertRaises(BzrBadParameterNotUnicode,
326
 
                          osutils.safe_unicode,
327
 
                          '\xbb\xbb')
328
 
 
329
 
 
330
 
class TestSafeUtf8(TestCase):
331
 
 
332
 
    def test_from_ascii_string(self):
333
 
        f = 'foobar'
334
 
        self.assertEqual('foobar', osutils.safe_utf8(f))
335
 
 
336
 
    def test_from_unicode_string_ascii_contents(self):
337
 
        self.assertEqual('bargam', osutils.safe_utf8(u'bargam'))
338
 
 
339
 
    def test_from_unicode_string_unicode_contents(self):
340
 
        self.assertEqual('bargam\xc2\xae', osutils.safe_utf8(u'bargam\xae'))
341
 
 
342
 
    def test_from_utf8_string(self):
343
 
        self.assertEqual('foo\xc2\xae', osutils.safe_utf8('foo\xc2\xae'))
344
 
 
345
 
    def test_bad_utf8_string(self):
346
 
        self.assertRaises(BzrBadParameterNotUnicode,
347
 
                          osutils.safe_utf8, '\xbb\xbb')
348
 
 
349
 
 
350
 
class TestSafeRevisionId(TestCase):
351
 
 
352
 
    def test_from_ascii_string(self):
353
 
        # this shouldn't give a warning because it's getting an ascii string
354
 
        self.assertEqual('foobar', osutils.safe_revision_id('foobar'))
355
 
 
356
 
    def test_from_unicode_string_ascii_contents(self):
357
 
        self.assertEqual('bargam',
358
 
                         osutils.safe_revision_id(u'bargam', warn=False))
359
 
 
360
 
    def test_from_unicode_deprecated(self):
361
 
        self.assertEqual('bargam',
362
 
            self.callDeprecated([osutils._revision_id_warning],
363
 
                                osutils.safe_revision_id, u'bargam'))
364
 
 
365
 
    def test_from_unicode_string_unicode_contents(self):
366
 
        self.assertEqual('bargam\xc2\xae',
367
 
                         osutils.safe_revision_id(u'bargam\xae', warn=False))
368
 
 
369
 
    def test_from_utf8_string(self):
370
 
        self.assertEqual('foo\xc2\xae',
371
 
                         osutils.safe_revision_id('foo\xc2\xae'))
372
 
 
373
 
    def test_none(self):
374
 
        """Currently, None is a valid revision_id"""
375
 
        self.assertEqual(None, osutils.safe_revision_id(None))
376
 
 
377
 
 
378
 
class TestSafeFileId(TestCase):
379
 
 
380
 
    def test_from_ascii_string(self):
381
 
        self.assertEqual('foobar', osutils.safe_file_id('foobar'))
382
 
 
383
 
    def test_from_unicode_string_ascii_contents(self):
384
 
        self.assertEqual('bargam', osutils.safe_file_id(u'bargam', warn=False))
385
 
 
386
 
    def test_from_unicode_deprecated(self):
387
 
        self.assertEqual('bargam',
388
 
            self.callDeprecated([osutils._file_id_warning],
389
 
                                osutils.safe_file_id, u'bargam'))
390
 
 
391
 
    def test_from_unicode_string_unicode_contents(self):
392
 
        self.assertEqual('bargam\xc2\xae',
393
 
                         osutils.safe_file_id(u'bargam\xae', warn=False))
394
 
 
395
 
    def test_from_utf8_string(self):
396
 
        self.assertEqual('foo\xc2\xae',
397
 
                         osutils.safe_file_id('foo\xc2\xae'))
398
 
 
399
 
    def test_none(self):
400
 
        """Currently, None is a valid revision_id"""
401
 
        self.assertEqual(None, osutils.safe_file_id(None))
402
 
 
403
 
 
404
 
class TestWin32Funcs(TestCase):
405
 
    """Test that the _win32 versions of os utilities return appropriate paths."""
406
 
 
407
 
    def test_abspath(self):
408
 
        self.assertEqual('C:/foo', osutils._win32_abspath('C:\\foo'))
409
 
        self.assertEqual('C:/foo', osutils._win32_abspath('C:/foo'))
410
 
        self.assertEqual('//HOST/path', osutils._win32_abspath(r'\\HOST\path'))
411
 
        self.assertEqual('//HOST/path', osutils._win32_abspath('//HOST/path'))
412
 
 
413
 
    def test_realpath(self):
414
 
        self.assertEqual('C:/foo', osutils._win32_realpath('C:\\foo'))
415
 
        self.assertEqual('C:/foo', osutils._win32_realpath('C:/foo'))
416
 
 
417
 
    def test_pathjoin(self):
418
 
        self.assertEqual('path/to/foo', osutils._win32_pathjoin('path', 'to', 'foo'))
419
 
        self.assertEqual('C:/foo', osutils._win32_pathjoin('path\\to', 'C:\\foo'))
420
 
        self.assertEqual('C:/foo', osutils._win32_pathjoin('path/to', 'C:/foo'))
421
 
        self.assertEqual('path/to/foo', osutils._win32_pathjoin('path/to/', 'foo'))
422
 
        self.assertEqual('/foo', osutils._win32_pathjoin('C:/path/to/', '/foo'))
423
 
        self.assertEqual('/foo', osutils._win32_pathjoin('C:\\path\\to\\', '\\foo'))
424
 
 
425
 
    def test_normpath(self):
426
 
        self.assertEqual('path/to/foo', osutils._win32_normpath(r'path\\from\..\to\.\foo'))
427
 
        self.assertEqual('path/to/foo', osutils._win32_normpath('path//from/../to/./foo'))
428
 
 
429
 
    def test_getcwd(self):
430
 
        cwd = osutils._win32_getcwd()
431
 
        os_cwd = os.getcwdu()
432
 
        self.assertEqual(os_cwd[1:].replace('\\', '/'), cwd[1:])
433
 
        # win32 is inconsistent whether it returns lower or upper case
434
 
        # and even if it was consistent the user might type the other
435
 
        # so we force it to uppercase
436
 
        # running python.exe under cmd.exe return capital C:\\
437
 
        # running win32 python inside a cygwin shell returns lowercase
438
 
        self.assertEqual(os_cwd[0].upper(), cwd[0])
439
 
 
440
 
    def test_fixdrive(self):
441
 
        self.assertEqual('H:/foo', osutils._win32_fixdrive('h:/foo'))
442
 
        self.assertEqual('H:/foo', osutils._win32_fixdrive('H:/foo'))
443
 
        self.assertEqual('C:\\foo', osutils._win32_fixdrive('c:\\foo'))
444
 
 
445
 
    def test_win98_abspath(self):
446
 
        # absolute path
447
 
        self.assertEqual('C:/foo', osutils._win98_abspath('C:\\foo'))
448
 
        self.assertEqual('C:/foo', osutils._win98_abspath('C:/foo'))
449
 
        # UNC path
450
 
        self.assertEqual('//HOST/path', osutils._win98_abspath(r'\\HOST\path'))
451
 
        self.assertEqual('//HOST/path', osutils._win98_abspath('//HOST/path'))
452
 
        # relative path
453
 
        cwd = osutils.getcwd().rstrip('/')
454
 
        drive = osutils._nt_splitdrive(cwd)[0]
455
 
        self.assertEqual(cwd+'/path', osutils._win98_abspath('path'))
456
 
        self.assertEqual(drive+'/path', osutils._win98_abspath('/path'))
457
 
        # unicode path
458
 
        u = u'\u1234'
459
 
        self.assertEqual(cwd+'/'+u, osutils._win98_abspath(u))
460
 
 
461
 
 
462
 
class TestWin32FuncsDirs(TestCaseInTempDir):
463
 
    """Test win32 functions that create files."""
464
 
    
465
 
    def test_getcwd(self):
466
 
        if win32utils.winver == 'Windows 98':
467
 
            raise TestSkipped('Windows 98 cannot handle unicode filenames')
468
 
        # Make sure getcwd can handle unicode filenames
469
 
        try:
470
 
            os.mkdir(u'mu-\xb5')
471
 
        except UnicodeError:
472
 
            raise TestSkipped("Unable to create Unicode filename")
473
 
 
474
 
        os.chdir(u'mu-\xb5')
475
 
        # TODO: jam 20060427 This will probably fail on Mac OSX because
476
 
        #       it will change the normalization of B\xe5gfors
477
 
        #       Consider using a different unicode character, or make
478
 
        #       osutils.getcwd() renormalize the path.
479
 
        self.assertEndsWith(osutils._win32_getcwd(), u'mu-\xb5')
480
 
 
481
 
    def test_minimum_path_selection(self):
482
 
        self.assertEqual(set(),
483
 
            osutils.minimum_path_selection([]))
484
 
        self.assertEqual(set(['a', 'b']),
485
 
            osutils.minimum_path_selection(['a', 'b']))
486
 
        self.assertEqual(set(['a/', 'b']),
487
 
            osutils.minimum_path_selection(['a/', 'b']))
488
 
        self.assertEqual(set(['a/', 'b']),
489
 
            osutils.minimum_path_selection(['a/c', 'a/', 'b']))
490
 
 
491
 
    def test_mkdtemp(self):
492
 
        tmpdir = osutils._win32_mkdtemp(dir='.')
493
 
        self.assertFalse('\\' in tmpdir)
494
 
 
495
 
    def test_rename(self):
496
 
        a = open('a', 'wb')
497
 
        a.write('foo\n')
498
 
        a.close()
499
 
        b = open('b', 'wb')
500
 
        b.write('baz\n')
501
 
        b.close()
502
 
 
503
 
        osutils._win32_rename('b', 'a')
504
 
        self.failUnlessExists('a')
505
 
        self.failIfExists('b')
506
 
        self.assertFileEqual('baz\n', 'a')
507
 
 
508
 
    def test_rename_missing_file(self):
509
 
        a = open('a', 'wb')
510
 
        a.write('foo\n')
511
 
        a.close()
512
 
 
513
 
        try:
514
 
            osutils._win32_rename('b', 'a')
515
 
        except (IOError, OSError), e:
516
 
            self.assertEqual(errno.ENOENT, e.errno)
517
 
        self.assertFileEqual('foo\n', 'a')
518
 
 
519
 
    def test_rename_missing_dir(self):
520
 
        os.mkdir('a')
521
 
        try:
522
 
            osutils._win32_rename('b', 'a')
523
 
        except (IOError, OSError), e:
524
 
            self.assertEqual(errno.ENOENT, e.errno)
525
 
 
526
 
    def test_rename_current_dir(self):
527
 
        os.mkdir('a')
528
 
        os.chdir('a')
529
 
        # You can't rename the working directory
530
 
        # doing rename non-existant . usually
531
 
        # just raises ENOENT, since non-existant
532
 
        # doesn't exist.
533
 
        try:
534
 
            osutils._win32_rename('b', '.')
535
 
        except (IOError, OSError), e:
536
 
            self.assertEqual(errno.ENOENT, e.errno)
537
 
 
538
 
    def test_splitpath(self):
539
 
        def check(expected, path):
540
 
            self.assertEqual(expected, osutils.splitpath(path))
541
 
 
542
 
        check(['a'], 'a')
543
 
        check(['a', 'b'], 'a/b')
544
 
        check(['a', 'b'], 'a/./b')
545
 
        check(['a', '.b'], 'a/.b')
546
 
        check(['a', '.b'], 'a\\.b')
547
 
 
548
 
        self.assertRaises(errors.BzrError, osutils.splitpath, 'a/../b')
549
 
 
550
 
 
551
 
class TestMacFuncsDirs(TestCaseInTempDir):
552
 
    """Test mac special functions that require directories."""
553
 
 
554
 
    def test_getcwd(self):
555
 
        # On Mac, this will actually create Ba\u030agfors
556
 
        # but chdir will still work, because it accepts both paths
557
 
        try:
558
 
            os.mkdir(u'B\xe5gfors')
559
 
        except UnicodeError:
560
 
            raise TestSkipped("Unable to create Unicode filename")
561
 
 
562
 
        os.chdir(u'B\xe5gfors')
563
 
        self.assertEndsWith(osutils._mac_getcwd(), u'B\xe5gfors')
564
 
 
565
 
    def test_getcwd_nonnorm(self):
566
 
        # Test that _mac_getcwd() will normalize this path
567
 
        try:
568
 
            os.mkdir(u'Ba\u030agfors')
569
 
        except UnicodeError:
570
 
            raise TestSkipped("Unable to create Unicode filename")
571
 
 
572
 
        os.chdir(u'Ba\u030agfors')
573
 
        self.assertEndsWith(osutils._mac_getcwd(), u'B\xe5gfors')
574
 
 
575
 
 
576
 
class TestSplitLines(TestCase):
577
 
 
578
 
    def test_split_unicode(self):
579
 
        self.assertEqual([u'foo\n', u'bar\xae'],
580
 
                         osutils.split_lines(u'foo\nbar\xae'))
581
 
        self.assertEqual([u'foo\n', u'bar\xae\n'],
582
 
                         osutils.split_lines(u'foo\nbar\xae\n'))
583
 
 
584
 
    def test_split_with_carriage_returns(self):
585
 
        self.assertEqual(['foo\rbar\n'],
586
 
                         osutils.split_lines('foo\rbar\n'))
587
 
 
588
 
 
589
 
class TestWalkDirs(TestCaseInTempDir):
590
 
 
591
 
    def test_walkdirs(self):
592
 
        tree = [
593
 
            '.bzr',
594
 
            '0file',
595
 
            '1dir/',
596
 
            '1dir/0file',
597
 
            '1dir/1dir/',
598
 
            '2file'
599
 
            ]
600
 
        self.build_tree(tree)
601
 
        expected_dirblocks = [
602
 
                (('', '.'),
603
 
                 [('0file', '0file', 'file'),
604
 
                  ('1dir', '1dir', 'directory'),
605
 
                  ('2file', '2file', 'file'),
606
 
                 ]
607
 
                ),
608
 
                (('1dir', './1dir'),
609
 
                 [('1dir/0file', '0file', 'file'),
610
 
                  ('1dir/1dir', '1dir', 'directory'),
611
 
                 ]
612
 
                ),
613
 
                (('1dir/1dir', './1dir/1dir'),
614
 
                 [
615
 
                 ]
616
 
                ),
617
 
            ]
618
 
        result = []
619
 
        found_bzrdir = False
620
 
        for dirdetail, dirblock in osutils.walkdirs('.'):
621
 
            if len(dirblock) and dirblock[0][1] == '.bzr':
622
 
                # this tests the filtering of selected paths
623
 
                found_bzrdir = True
624
 
                del dirblock[0]
625
 
            result.append((dirdetail, dirblock))
626
 
 
627
 
        self.assertTrue(found_bzrdir)
628
 
        self.assertEqual(expected_dirblocks,
629
 
            [(dirinfo, [line[0:3] for line in block]) for dirinfo, block in result])
630
 
        # you can search a subdir only, with a supplied prefix.
631
 
        result = []
632
 
        for dirblock in osutils.walkdirs('./1dir', '1dir'):
633
 
            result.append(dirblock)
634
 
        self.assertEqual(expected_dirblocks[1:],
635
 
            [(dirinfo, [line[0:3] for line in block]) for dirinfo, block in result])
636
 
 
637
 
    def test__walkdirs_utf8(self):
638
 
        tree = [
639
 
            '.bzr',
640
 
            '0file',
641
 
            '1dir/',
642
 
            '1dir/0file',
643
 
            '1dir/1dir/',
644
 
            '2file'
645
 
            ]
646
 
        self.build_tree(tree)
647
 
        expected_dirblocks = [
648
 
                (('', '.'),
649
 
                 [('0file', '0file', 'file'),
650
 
                  ('1dir', '1dir', 'directory'),
651
 
                  ('2file', '2file', 'file'),
652
 
                 ]
653
 
                ),
654
 
                (('1dir', './1dir'),
655
 
                 [('1dir/0file', '0file', 'file'),
656
 
                  ('1dir/1dir', '1dir', 'directory'),
657
 
                 ]
658
 
                ),
659
 
                (('1dir/1dir', './1dir/1dir'),
660
 
                 [
661
 
                 ]
662
 
                ),
663
 
            ]
664
 
        result = []
665
 
        found_bzrdir = False
666
 
        for dirdetail, dirblock in osutils._walkdirs_utf8('.'):
667
 
            if len(dirblock) and dirblock[0][1] == '.bzr':
668
 
                # this tests the filtering of selected paths
669
 
                found_bzrdir = True
670
 
                del dirblock[0]
671
 
            result.append((dirdetail, dirblock))
672
 
 
673
 
        self.assertTrue(found_bzrdir)
674
 
        self.assertEqual(expected_dirblocks,
675
 
            [(dirinfo, [line[0:3] for line in block]) for dirinfo, block in result])
676
 
        # you can search a subdir only, with a supplied prefix.
677
 
        result = []
678
 
        for dirblock in osutils.walkdirs('./1dir', '1dir'):
679
 
            result.append(dirblock)
680
 
        self.assertEqual(expected_dirblocks[1:],
681
 
            [(dirinfo, [line[0:3] for line in block]) for dirinfo, block in result])
682
 
 
683
 
    def _filter_out_stat(self, result):
684
 
        """Filter out the stat value from the walkdirs result"""
685
 
        for dirdetail, dirblock in result:
686
 
            new_dirblock = []
687
 
            for info in dirblock:
688
 
                # Ignore info[3] which is the stat
689
 
                new_dirblock.append((info[0], info[1], info[2], info[4]))
690
 
            dirblock[:] = new_dirblock
691
 
 
692
 
    def test_unicode_walkdirs(self):
693
 
        """Walkdirs should always return unicode paths."""
694
 
        name0 = u'0file-\xb6'
695
 
        name1 = u'1dir-\u062c\u0648'
696
 
        name2 = u'2file-\u0633'
697
 
        tree = [
698
 
            name0,
699
 
            name1 + '/',
700
 
            name1 + '/' + name0,
701
 
            name1 + '/' + name1 + '/',
702
 
            name2,
703
 
            ]
704
 
        try:
705
 
            self.build_tree(tree)
706
 
        except UnicodeError:
707
 
            raise TestSkipped('Could not represent Unicode chars'
708
 
                              ' in current encoding.')
709
 
        expected_dirblocks = [
710
 
                ((u'', u'.'),
711
 
                 [(name0, name0, 'file', './' + name0),
712
 
                  (name1, name1, 'directory', './' + name1),
713
 
                  (name2, name2, 'file', './' + name2),
714
 
                 ]
715
 
                ),
716
 
                ((name1, './' + name1),
717
 
                 [(name1 + '/' + name0, name0, 'file', './' + name1
718
 
                                                        + '/' + name0),
719
 
                  (name1 + '/' + name1, name1, 'directory', './' + name1
720
 
                                                            + '/' + name1),
721
 
                 ]
722
 
                ),
723
 
                ((name1 + '/' + name1, './' + name1 + '/' + name1),
724
 
                 [
725
 
                 ]
726
 
                ),
727
 
            ]
728
 
        result = list(osutils.walkdirs('.'))
729
 
        self._filter_out_stat(result)
730
 
        self.assertEqual(expected_dirblocks, result)
731
 
        result = list(osutils.walkdirs(u'./'+name1, name1))
732
 
        self._filter_out_stat(result)
733
 
        self.assertEqual(expected_dirblocks[1:], result)
734
 
 
735
 
    def test_unicode__walkdirs_utf8(self):
736
 
        """Walkdirs_utf8 should always return utf8 paths.
737
 
 
738
 
        The abspath portion might be in unicode or utf-8
739
 
        """
740
 
        name0 = u'0file-\xb6'
741
 
        name1 = u'1dir-\u062c\u0648'
742
 
        name2 = u'2file-\u0633'
743
 
        tree = [
744
 
            name0,
745
 
            name1 + '/',
746
 
            name1 + '/' + name0,
747
 
            name1 + '/' + name1 + '/',
748
 
            name2,
749
 
            ]
750
 
        try:
751
 
            self.build_tree(tree)
752
 
        except UnicodeError:
753
 
            raise TestSkipped('Could not represent Unicode chars'
754
 
                              ' in current encoding.')
755
 
        name0 = name0.encode('utf8')
756
 
        name1 = name1.encode('utf8')
757
 
        name2 = name2.encode('utf8')
758
 
 
759
 
        expected_dirblocks = [
760
 
                (('', '.'),
761
 
                 [(name0, name0, 'file', './' + name0),
762
 
                  (name1, name1, 'directory', './' + name1),
763
 
                  (name2, name2, 'file', './' + name2),
764
 
                 ]
765
 
                ),
766
 
                ((name1, './' + name1),
767
 
                 [(name1 + '/' + name0, name0, 'file', './' + name1
768
 
                                                        + '/' + name0),
769
 
                  (name1 + '/' + name1, name1, 'directory', './' + name1
770
 
                                                            + '/' + name1),
771
 
                 ]
772
 
                ),
773
 
                ((name1 + '/' + name1, './' + name1 + '/' + name1),
774
 
                 [
775
 
                 ]
776
 
                ),
777
 
            ]
778
 
        result = []
779
 
        # For ease in testing, if walkdirs_utf8 returns Unicode, assert that
780
 
        # all abspaths are Unicode, and encode them back into utf8.
781
 
        for dirdetail, dirblock in osutils._walkdirs_utf8('.'):
782
 
            self.assertIsInstance(dirdetail[0], str)
783
 
            if isinstance(dirdetail[1], unicode):
784
 
                dirdetail = (dirdetail[0], dirdetail[1].encode('utf8'))
785
 
                dirblock = [list(info) for info in dirblock]
786
 
                for info in dirblock:
787
 
                    self.assertIsInstance(info[4], unicode)
788
 
                    info[4] = info[4].encode('utf8')
789
 
            new_dirblock = []
790
 
            for info in dirblock:
791
 
                self.assertIsInstance(info[0], str)
792
 
                self.assertIsInstance(info[1], str)
793
 
                self.assertIsInstance(info[4], str)
794
 
                # Remove the stat information
795
 
                new_dirblock.append((info[0], info[1], info[2], info[4]))
796
 
            result.append((dirdetail, new_dirblock))
797
 
        self.assertEqual(expected_dirblocks, result)
798
 
 
799
 
    def test_unicode__walkdirs_unicode_to_utf8(self):
800
 
        """walkdirs_unicode_to_utf8 should be a safe fallback everywhere
801
 
 
802
 
        The abspath portion should be in unicode
803
 
        """
804
 
        name0u = u'0file-\xb6'
805
 
        name1u = u'1dir-\u062c\u0648'
806
 
        name2u = u'2file-\u0633'
807
 
        tree = [
808
 
            name0u,
809
 
            name1u + '/',
810
 
            name1u + '/' + name0u,
811
 
            name1u + '/' + name1u + '/',
812
 
            name2u,
813
 
            ]
814
 
        try:
815
 
            self.build_tree(tree)
816
 
        except UnicodeError:
817
 
            raise TestSkipped('Could not represent Unicode chars'
818
 
                              ' in current encoding.')
819
 
        name0 = name0u.encode('utf8')
820
 
        name1 = name1u.encode('utf8')
821
 
        name2 = name2u.encode('utf8')
822
 
 
823
 
        # All of the abspaths should be in unicode, all of the relative paths
824
 
        # should be in utf8
825
 
        expected_dirblocks = [
826
 
                (('', '.'),
827
 
                 [(name0, name0, 'file', './' + name0u),
828
 
                  (name1, name1, 'directory', './' + name1u),
829
 
                  (name2, name2, 'file', './' + name2u),
830
 
                 ]
831
 
                ),
832
 
                ((name1, './' + name1u),
833
 
                 [(name1 + '/' + name0, name0, 'file', './' + name1u
834
 
                                                        + '/' + name0u),
835
 
                  (name1 + '/' + name1, name1, 'directory', './' + name1u
836
 
                                                            + '/' + name1u),
837
 
                 ]
838
 
                ),
839
 
                ((name1 + '/' + name1, './' + name1u + '/' + name1u),
840
 
                 [
841
 
                 ]
842
 
                ),
843
 
            ]
844
 
        result = list(osutils._walkdirs_unicode_to_utf8('.'))
845
 
        self._filter_out_stat(result)
846
 
        self.assertEqual(expected_dirblocks, result)
847
 
 
848
 
    def assertPathCompare(self, path_less, path_greater):
849
 
        """check that path_less and path_greater compare correctly."""
850
 
        self.assertEqual(0, osutils.compare_paths_prefix_order(
851
 
            path_less, path_less))
852
 
        self.assertEqual(0, osutils.compare_paths_prefix_order(
853
 
            path_greater, path_greater))
854
 
        self.assertEqual(-1, osutils.compare_paths_prefix_order(
855
 
            path_less, path_greater))
856
 
        self.assertEqual(1, osutils.compare_paths_prefix_order(
857
 
            path_greater, path_less))
858
 
 
859
 
    def test_compare_paths_prefix_order(self):
860
 
        # root before all else
861
 
        self.assertPathCompare("/", "/a")
862
 
        # alpha within a dir
863
 
        self.assertPathCompare("/a", "/b")
864
 
        self.assertPathCompare("/b", "/z")
865
 
        # high dirs before lower.
866
 
        self.assertPathCompare("/z", "/a/a")
867
 
        # except if the deeper dir should be output first
868
 
        self.assertPathCompare("/a/b/c", "/d/g")
869
 
        # lexical betwen dirs of the same height
870
 
        self.assertPathCompare("/a/z", "/z/z")
871
 
        self.assertPathCompare("/a/c/z", "/a/d/e")
872
 
 
873
 
        # this should also be consistent for no leading / paths
874
 
        # root before all else
875
 
        self.assertPathCompare("", "a")
876
 
        # alpha within a dir
877
 
        self.assertPathCompare("a", "b")
878
 
        self.assertPathCompare("b", "z")
879
 
        # high dirs before lower.
880
 
        self.assertPathCompare("z", "a/a")
881
 
        # except if the deeper dir should be output first
882
 
        self.assertPathCompare("a/b/c", "d/g")
883
 
        # lexical betwen dirs of the same height
884
 
        self.assertPathCompare("a/z", "z/z")
885
 
        self.assertPathCompare("a/c/z", "a/d/e")
886
 
 
887
 
    def test_path_prefix_sorting(self):
888
 
        """Doing a sort on path prefix should match our sample data."""
889
 
        original_paths = [
890
 
            'a',
891
 
            'a/b',
892
 
            'a/b/c',
893
 
            'b',
894
 
            'b/c',
895
 
            'd',
896
 
            'd/e',
897
 
            'd/e/f',
898
 
            'd/f',
899
 
            'd/g',
900
 
            'g',
901
 
            ]
902
 
 
903
 
        dir_sorted_paths = [
904
 
            'a',
905
 
            'b',
906
 
            'd',
907
 
            'g',
908
 
            'a/b',
909
 
            'a/b/c',
910
 
            'b/c',
911
 
            'd/e',
912
 
            'd/f',
913
 
            'd/g',
914
 
            'd/e/f',
915
 
            ]
916
 
 
917
 
        self.assertEqual(
918
 
            dir_sorted_paths,
919
 
            sorted(original_paths, key=osutils.path_prefix_key))
920
 
        # using the comparison routine shoudl work too:
921
 
        self.assertEqual(
922
 
            dir_sorted_paths,
923
 
            sorted(original_paths, cmp=osutils.compare_paths_prefix_order))
924
 
 
925
 
 
926
 
class TestCopyTree(TestCaseInTempDir):
927
 
    
928
 
    def test_copy_basic_tree(self):
929
 
        self.build_tree(['source/', 'source/a', 'source/b/', 'source/b/c'])
930
 
        osutils.copy_tree('source', 'target')
931
 
        self.assertEqual(['a', 'b'], sorted(os.listdir('target')))
932
 
        self.assertEqual(['c'], os.listdir('target/b'))
933
 
 
934
 
    def test_copy_tree_target_exists(self):
935
 
        self.build_tree(['source/', 'source/a', 'source/b/', 'source/b/c',
936
 
                         'target/'])
937
 
        osutils.copy_tree('source', 'target')
938
 
        self.assertEqual(['a', 'b'], sorted(os.listdir('target')))
939
 
        self.assertEqual(['c'], os.listdir('target/b'))
940
 
 
941
 
    def test_copy_tree_symlinks(self):
942
 
        if not osutils.has_symlinks():
943
 
            return
944
 
        self.build_tree(['source/'])
945
 
        os.symlink('a/generic/path', 'source/lnk')
946
 
        osutils.copy_tree('source', 'target')
947
 
        self.assertEqual(['lnk'], os.listdir('target'))
948
 
        self.assertEqual('a/generic/path', os.readlink('target/lnk'))
949
 
 
950
 
    def test_copy_tree_handlers(self):
951
 
        processed_files = []
952
 
        processed_links = []
953
 
        def file_handler(from_path, to_path):
954
 
            processed_files.append(('f', from_path, to_path))
955
 
        def dir_handler(from_path, to_path):
956
 
            processed_files.append(('d', from_path, to_path))
957
 
        def link_handler(from_path, to_path):
958
 
            processed_links.append((from_path, to_path))
959
 
        handlers = {'file':file_handler,
960
 
                    'directory':dir_handler,
961
 
                    'symlink':link_handler,
962
 
                   }
963
 
 
964
 
        self.build_tree(['source/', 'source/a', 'source/b/', 'source/b/c'])
965
 
        if osutils.has_symlinks():
966
 
            os.symlink('a/generic/path', 'source/lnk')
967
 
        osutils.copy_tree('source', 'target', handlers=handlers)
968
 
 
969
 
        self.assertEqual([('d', 'source', 'target'),
970
 
                          ('f', 'source/a', 'target/a'),
971
 
                          ('d', 'source/b', 'target/b'),
972
 
                          ('f', 'source/b/c', 'target/b/c'),
973
 
                         ], processed_files)
974
 
        self.failIfExists('target')
975
 
        if osutils.has_symlinks():
976
 
            self.assertEqual([('source/lnk', 'target/lnk')], processed_links)
977
 
 
978
 
 
979
 
#class TestTerminalEncoding has been moved to test_osutils_encodings.py
980
 
# [bialix] 2006/12/26
981
 
 
982
 
 
983
 
class TestSetUnsetEnv(TestCase):
984
 
    """Test updating the environment"""
985
 
 
986
 
    def setUp(self):
987
 
        super(TestSetUnsetEnv, self).setUp()
988
 
 
989
 
        self.assertEqual(None, os.environ.get('BZR_TEST_ENV_VAR'),
990
 
                         'Environment was not cleaned up properly.'
991
 
                         ' Variable BZR_TEST_ENV_VAR should not exist.')
992
 
        def cleanup():
993
 
            if 'BZR_TEST_ENV_VAR' in os.environ:
994
 
                del os.environ['BZR_TEST_ENV_VAR']
995
 
 
996
 
        self.addCleanup(cleanup)
997
 
 
998
 
    def test_set(self):
999
 
        """Test that we can set an env variable"""
1000
 
        old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'foo')
1001
 
        self.assertEqual(None, old)
1002
 
        self.assertEqual('foo', os.environ.get('BZR_TEST_ENV_VAR'))
1003
 
 
1004
 
    def test_double_set(self):
1005
 
        """Test that we get the old value out"""
1006
 
        osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'foo')
1007
 
        old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'bar')
1008
 
        self.assertEqual('foo', old)
1009
 
        self.assertEqual('bar', os.environ.get('BZR_TEST_ENV_VAR'))
1010
 
 
1011
 
    def test_unicode(self):
1012
 
        """Environment can only contain plain strings
1013
 
        
1014
 
        So Unicode strings must be encoded.
1015
 
        """
1016
 
        uni_val, env_val = probe_unicode_in_user_encoding()
1017
 
        if uni_val is None:
1018
 
            raise TestSkipped('Cannot find a unicode character that works in'
1019
 
                              ' encoding %s' % (bzrlib.user_encoding,))
1020
 
 
1021
 
        old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', uni_val)
1022
 
        self.assertEqual(env_val, os.environ.get('BZR_TEST_ENV_VAR'))
1023
 
 
1024
 
    def test_unset(self):
1025
 
        """Test that passing None will remove the env var"""
1026
 
        osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'foo')
1027
 
        old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', None)
1028
 
        self.assertEqual('foo', old)
1029
 
        self.assertEqual(None, os.environ.get('BZR_TEST_ENV_VAR'))
1030
 
        self.failIf('BZR_TEST_ENV_VAR' in os.environ)
1031
 
 
1032
 
 
1033
 
class TestLocalTimeOffset(TestCase):
1034
 
 
1035
 
    def test_local_time_offset(self):
1036
 
        """Test that local_time_offset() returns a sane value."""
1037
 
        offset = osutils.local_time_offset()
1038
 
        self.assertTrue(isinstance(offset, int))
1039
 
        # Test that the offset is no more than a eighteen hours in
1040
 
        # either direction.
1041
 
        # Time zone handling is system specific, so it is difficult to
1042
 
        # do more specific tests, but a value outside of this range is
1043
 
        # probably wrong.
1044
 
        eighteen_hours = 18 * 3600
1045
 
        self.assertTrue(-eighteen_hours < offset < eighteen_hours)
1046
 
 
1047
 
    def test_local_time_offset_with_timestamp(self):
1048
 
        """Test that local_time_offset() works with a timestamp."""
1049
 
        offset = osutils.local_time_offset(1000000000.1234567)
1050
 
        self.assertTrue(isinstance(offset, int))
1051
 
        eighteen_hours = 18 * 3600
1052
 
        self.assertTrue(-eighteen_hours < offset < eighteen_hours)