~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-06-21 06:10:18 UTC
  • Revision ID: mbp@sourcefrog.net-20050621061017-12e8f0ff45228338
- move whitebox/blackbox modules into bzrlib.selftest subdirectory

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
 
        self.assertEqual('foobar', osutils.safe_revision_id('foobar'))
354
 
 
355
 
    def test_from_unicode_string_ascii_contents(self):
356
 
        self.assertEqual('bargam',
357
 
                         osutils.safe_revision_id(u'bargam', warn=False))
358
 
 
359
 
    def test_from_unicode_deprecated(self):
360
 
        self.assertEqual('bargam',
361
 
            self.callDeprecated([osutils._revision_id_warning],
362
 
                                osutils.safe_revision_id, u'bargam'))
363
 
 
364
 
    def test_from_unicode_string_unicode_contents(self):
365
 
        self.assertEqual('bargam\xc2\xae',
366
 
                         osutils.safe_revision_id(u'bargam\xae', warn=False))
367
 
 
368
 
    def test_from_utf8_string(self):
369
 
        self.assertEqual('foo\xc2\xae',
370
 
                         osutils.safe_revision_id('foo\xc2\xae'))
371
 
 
372
 
    def test_none(self):
373
 
        """Currently, None is a valid revision_id"""
374
 
        self.assertEqual(None, osutils.safe_revision_id(None))
375
 
 
376
 
 
377
 
class TestSafeFileId(TestCase):
378
 
 
379
 
    def test_from_ascii_string(self):
380
 
        self.assertEqual('foobar', osutils.safe_file_id('foobar'))
381
 
 
382
 
    def test_from_unicode_string_ascii_contents(self):
383
 
        self.assertEqual('bargam', osutils.safe_file_id(u'bargam', warn=False))
384
 
 
385
 
    def test_from_unicode_deprecated(self):
386
 
        self.assertEqual('bargam',
387
 
            self.callDeprecated([osutils._file_id_warning],
388
 
                                osutils.safe_file_id, u'bargam'))
389
 
 
390
 
    def test_from_unicode_string_unicode_contents(self):
391
 
        self.assertEqual('bargam\xc2\xae',
392
 
                         osutils.safe_file_id(u'bargam\xae', warn=False))
393
 
 
394
 
    def test_from_utf8_string(self):
395
 
        self.assertEqual('foo\xc2\xae',
396
 
                         osutils.safe_file_id('foo\xc2\xae'))
397
 
 
398
 
    def test_none(self):
399
 
        """Currently, None is a valid revision_id"""
400
 
        self.assertEqual(None, osutils.safe_file_id(None))
401
 
 
402
 
 
403
 
class TestWin32Funcs(TestCase):
404
 
    """Test that the _win32 versions of os utilities return appropriate paths."""
405
 
 
406
 
    def test_abspath(self):
407
 
        self.assertEqual('C:/foo', osutils._win32_abspath('C:\\foo'))
408
 
        self.assertEqual('C:/foo', osutils._win32_abspath('C:/foo'))
409
 
        self.assertEqual('//HOST/path', osutils._win32_abspath(r'\\HOST\path'))
410
 
        self.assertEqual('//HOST/path', osutils._win32_abspath('//HOST/path'))
411
 
 
412
 
    def test_realpath(self):
413
 
        self.assertEqual('C:/foo', osutils._win32_realpath('C:\\foo'))
414
 
        self.assertEqual('C:/foo', osutils._win32_realpath('C:/foo'))
415
 
 
416
 
    def test_pathjoin(self):
417
 
        self.assertEqual('path/to/foo', osutils._win32_pathjoin('path', 'to', 'foo'))
418
 
        self.assertEqual('C:/foo', osutils._win32_pathjoin('path\\to', 'C:\\foo'))
419
 
        self.assertEqual('C:/foo', osutils._win32_pathjoin('path/to', 'C:/foo'))
420
 
        self.assertEqual('path/to/foo', osutils._win32_pathjoin('path/to/', 'foo'))
421
 
        self.assertEqual('/foo', osutils._win32_pathjoin('C:/path/to/', '/foo'))
422
 
        self.assertEqual('/foo', osutils._win32_pathjoin('C:\\path\\to\\', '\\foo'))
423
 
 
424
 
    def test_normpath(self):
425
 
        self.assertEqual('path/to/foo', osutils._win32_normpath(r'path\\from\..\to\.\foo'))
426
 
        self.assertEqual('path/to/foo', osutils._win32_normpath('path//from/../to/./foo'))
427
 
 
428
 
    def test_getcwd(self):
429
 
        cwd = osutils._win32_getcwd()
430
 
        os_cwd = os.getcwdu()
431
 
        self.assertEqual(os_cwd[1:].replace('\\', '/'), cwd[1:])
432
 
        # win32 is inconsistent whether it returns lower or upper case
433
 
        # and even if it was consistent the user might type the other
434
 
        # so we force it to uppercase
435
 
        # running python.exe under cmd.exe return capital C:\\
436
 
        # running win32 python inside a cygwin shell returns lowercase
437
 
        self.assertEqual(os_cwd[0].upper(), cwd[0])
438
 
 
439
 
    def test_fixdrive(self):
440
 
        self.assertEqual('H:/foo', osutils._win32_fixdrive('h:/foo'))
441
 
        self.assertEqual('H:/foo', osutils._win32_fixdrive('H:/foo'))
442
 
        self.assertEqual('C:\\foo', osutils._win32_fixdrive('c:\\foo'))
443
 
 
444
 
    def test_win98_abspath(self):
445
 
        # absolute path
446
 
        self.assertEqual('C:/foo', osutils._win98_abspath('C:\\foo'))
447
 
        self.assertEqual('C:/foo', osutils._win98_abspath('C:/foo'))
448
 
        # UNC path
449
 
        self.assertEqual('//HOST/path', osutils._win98_abspath(r'\\HOST\path'))
450
 
        self.assertEqual('//HOST/path', osutils._win98_abspath('//HOST/path'))
451
 
        # relative path
452
 
        cwd = osutils.getcwd().rstrip('/')
453
 
        drive = osutils._nt_splitdrive(cwd)[0]
454
 
        self.assertEqual(cwd+'/path', osutils._win98_abspath('path'))
455
 
        self.assertEqual(drive+'/path', osutils._win98_abspath('/path'))
456
 
        # unicode path
457
 
        u = u'\u1234'
458
 
        self.assertEqual(cwd+'/'+u, osutils._win98_abspath(u))
459
 
 
460
 
 
461
 
class TestWin32FuncsDirs(TestCaseInTempDir):
462
 
    """Test win32 functions that create files."""
463
 
    
464
 
    def test_getcwd(self):
465
 
        if win32utils.winver == 'Windows 98':
466
 
            raise TestSkipped('Windows 98 cannot handle unicode filenames')
467
 
        # Make sure getcwd can handle unicode filenames
468
 
        try:
469
 
            os.mkdir(u'mu-\xb5')
470
 
        except UnicodeError:
471
 
            raise TestSkipped("Unable to create Unicode filename")
472
 
 
473
 
        os.chdir(u'mu-\xb5')
474
 
        # TODO: jam 20060427 This will probably fail on Mac OSX because
475
 
        #       it will change the normalization of B\xe5gfors
476
 
        #       Consider using a different unicode character, or make
477
 
        #       osutils.getcwd() renormalize the path.
478
 
        self.assertEndsWith(osutils._win32_getcwd(), u'mu-\xb5')
479
 
 
480
 
    def test_minimum_path_selection(self):
481
 
        self.assertEqual(set(),
482
 
            osutils.minimum_path_selection([]))
483
 
        self.assertEqual(set(['a', 'b']),
484
 
            osutils.minimum_path_selection(['a', 'b']))
485
 
        self.assertEqual(set(['a/', 'b']),
486
 
            osutils.minimum_path_selection(['a/', 'b']))
487
 
        self.assertEqual(set(['a/', 'b']),
488
 
            osutils.minimum_path_selection(['a/c', 'a/', 'b']))
489
 
 
490
 
    def test_mkdtemp(self):
491
 
        tmpdir = osutils._win32_mkdtemp(dir='.')
492
 
        self.assertFalse('\\' in tmpdir)
493
 
 
494
 
    def test_rename(self):
495
 
        a = open('a', 'wb')
496
 
        a.write('foo\n')
497
 
        a.close()
498
 
        b = open('b', 'wb')
499
 
        b.write('baz\n')
500
 
        b.close()
501
 
 
502
 
        osutils._win32_rename('b', 'a')
503
 
        self.failUnlessExists('a')
504
 
        self.failIfExists('b')
505
 
        self.assertFileEqual('baz\n', 'a')
506
 
 
507
 
    def test_rename_missing_file(self):
508
 
        a = open('a', 'wb')
509
 
        a.write('foo\n')
510
 
        a.close()
511
 
 
512
 
        try:
513
 
            osutils._win32_rename('b', 'a')
514
 
        except (IOError, OSError), e:
515
 
            self.assertEqual(errno.ENOENT, e.errno)
516
 
        self.assertFileEqual('foo\n', 'a')
517
 
 
518
 
    def test_rename_missing_dir(self):
519
 
        os.mkdir('a')
520
 
        try:
521
 
            osutils._win32_rename('b', 'a')
522
 
        except (IOError, OSError), e:
523
 
            self.assertEqual(errno.ENOENT, e.errno)
524
 
 
525
 
    def test_rename_current_dir(self):
526
 
        os.mkdir('a')
527
 
        os.chdir('a')
528
 
        # You can't rename the working directory
529
 
        # doing rename non-existant . usually
530
 
        # just raises ENOENT, since non-existant
531
 
        # doesn't exist.
532
 
        try:
533
 
            osutils._win32_rename('b', '.')
534
 
        except (IOError, OSError), e:
535
 
            self.assertEqual(errno.ENOENT, e.errno)
536
 
 
537
 
    def test_splitpath(self):
538
 
        def check(expected, path):
539
 
            self.assertEqual(expected, osutils.splitpath(path))
540
 
 
541
 
        check(['a'], 'a')
542
 
        check(['a', 'b'], 'a/b')
543
 
        check(['a', 'b'], 'a/./b')
544
 
        check(['a', '.b'], 'a/.b')
545
 
        check(['a', '.b'], 'a\\.b')
546
 
 
547
 
        self.assertRaises(errors.BzrError, osutils.splitpath, 'a/../b')
548
 
 
549
 
 
550
 
class TestMacFuncsDirs(TestCaseInTempDir):
551
 
    """Test mac special functions that require directories."""
552
 
 
553
 
    def test_getcwd(self):
554
 
        # On Mac, this will actually create Ba\u030agfors
555
 
        # but chdir will still work, because it accepts both paths
556
 
        try:
557
 
            os.mkdir(u'B\xe5gfors')
558
 
        except UnicodeError:
559
 
            raise TestSkipped("Unable to create Unicode filename")
560
 
 
561
 
        os.chdir(u'B\xe5gfors')
562
 
        self.assertEndsWith(osutils._mac_getcwd(), u'B\xe5gfors')
563
 
 
564
 
    def test_getcwd_nonnorm(self):
565
 
        # Test that _mac_getcwd() will normalize this path
566
 
        try:
567
 
            os.mkdir(u'Ba\u030agfors')
568
 
        except UnicodeError:
569
 
            raise TestSkipped("Unable to create Unicode filename")
570
 
 
571
 
        os.chdir(u'Ba\u030agfors')
572
 
        self.assertEndsWith(osutils._mac_getcwd(), u'B\xe5gfors')
573
 
 
574
 
 
575
 
class TestSplitLines(TestCase):
576
 
 
577
 
    def test_split_unicode(self):
578
 
        self.assertEqual([u'foo\n', u'bar\xae'],
579
 
                         osutils.split_lines(u'foo\nbar\xae'))
580
 
        self.assertEqual([u'foo\n', u'bar\xae\n'],
581
 
                         osutils.split_lines(u'foo\nbar\xae\n'))
582
 
 
583
 
    def test_split_with_carriage_returns(self):
584
 
        self.assertEqual(['foo\rbar\n'],
585
 
                         osutils.split_lines('foo\rbar\n'))
586
 
 
587
 
 
588
 
class TestWalkDirs(TestCaseInTempDir):
589
 
 
590
 
    def test_walkdirs(self):
591
 
        tree = [
592
 
            '.bzr',
593
 
            '0file',
594
 
            '1dir/',
595
 
            '1dir/0file',
596
 
            '1dir/1dir/',
597
 
            '2file'
598
 
            ]
599
 
        self.build_tree(tree)
600
 
        expected_dirblocks = [
601
 
                (('', '.'),
602
 
                 [('0file', '0file', 'file'),
603
 
                  ('1dir', '1dir', 'directory'),
604
 
                  ('2file', '2file', 'file'),
605
 
                 ]
606
 
                ),
607
 
                (('1dir', './1dir'),
608
 
                 [('1dir/0file', '0file', 'file'),
609
 
                  ('1dir/1dir', '1dir', 'directory'),
610
 
                 ]
611
 
                ),
612
 
                (('1dir/1dir', './1dir/1dir'),
613
 
                 [
614
 
                 ]
615
 
                ),
616
 
            ]
617
 
        result = []
618
 
        found_bzrdir = False
619
 
        for dirdetail, dirblock in osutils.walkdirs('.'):
620
 
            if len(dirblock) and dirblock[0][1] == '.bzr':
621
 
                # this tests the filtering of selected paths
622
 
                found_bzrdir = True
623
 
                del dirblock[0]
624
 
            result.append((dirdetail, dirblock))
625
 
 
626
 
        self.assertTrue(found_bzrdir)
627
 
        self.assertEqual(expected_dirblocks,
628
 
            [(dirinfo, [line[0:3] for line in block]) for dirinfo, block in result])
629
 
        # you can search a subdir only, with a supplied prefix.
630
 
        result = []
631
 
        for dirblock in osutils.walkdirs('./1dir', '1dir'):
632
 
            result.append(dirblock)
633
 
        self.assertEqual(expected_dirblocks[1:],
634
 
            [(dirinfo, [line[0:3] for line in block]) for dirinfo, block in result])
635
 
 
636
 
    def test__walkdirs_utf8(self):
637
 
        tree = [
638
 
            '.bzr',
639
 
            '0file',
640
 
            '1dir/',
641
 
            '1dir/0file',
642
 
            '1dir/1dir/',
643
 
            '2file'
644
 
            ]
645
 
        self.build_tree(tree)
646
 
        expected_dirblocks = [
647
 
                (('', '.'),
648
 
                 [('0file', '0file', 'file'),
649
 
                  ('1dir', '1dir', 'directory'),
650
 
                  ('2file', '2file', 'file'),
651
 
                 ]
652
 
                ),
653
 
                (('1dir', './1dir'),
654
 
                 [('1dir/0file', '0file', 'file'),
655
 
                  ('1dir/1dir', '1dir', 'directory'),
656
 
                 ]
657
 
                ),
658
 
                (('1dir/1dir', './1dir/1dir'),
659
 
                 [
660
 
                 ]
661
 
                ),
662
 
            ]
663
 
        result = []
664
 
        found_bzrdir = False
665
 
        for dirdetail, dirblock in osutils._walkdirs_utf8('.'):
666
 
            if len(dirblock) and dirblock[0][1] == '.bzr':
667
 
                # this tests the filtering of selected paths
668
 
                found_bzrdir = True
669
 
                del dirblock[0]
670
 
            result.append((dirdetail, dirblock))
671
 
 
672
 
        self.assertTrue(found_bzrdir)
673
 
        self.assertEqual(expected_dirblocks,
674
 
            [(dirinfo, [line[0:3] for line in block]) for dirinfo, block in result])
675
 
        # you can search a subdir only, with a supplied prefix.
676
 
        result = []
677
 
        for dirblock in osutils.walkdirs('./1dir', '1dir'):
678
 
            result.append(dirblock)
679
 
        self.assertEqual(expected_dirblocks[1:],
680
 
            [(dirinfo, [line[0:3] for line in block]) for dirinfo, block in result])
681
 
 
682
 
    def _filter_out_stat(self, result):
683
 
        """Filter out the stat value from the walkdirs result"""
684
 
        for dirdetail, dirblock in result:
685
 
            new_dirblock = []
686
 
            for info in dirblock:
687
 
                # Ignore info[3] which is the stat
688
 
                new_dirblock.append((info[0], info[1], info[2], info[4]))
689
 
            dirblock[:] = new_dirblock
690
 
 
691
 
    def test_unicode_walkdirs(self):
692
 
        """Walkdirs should always return unicode paths."""
693
 
        name0 = u'0file-\xb6'
694
 
        name1 = u'1dir-\u062c\u0648'
695
 
        name2 = u'2file-\u0633'
696
 
        tree = [
697
 
            name0,
698
 
            name1 + '/',
699
 
            name1 + '/' + name0,
700
 
            name1 + '/' + name1 + '/',
701
 
            name2,
702
 
            ]
703
 
        try:
704
 
            self.build_tree(tree)
705
 
        except UnicodeError:
706
 
            raise TestSkipped('Could not represent Unicode chars'
707
 
                              ' in current encoding.')
708
 
        expected_dirblocks = [
709
 
                ((u'', u'.'),
710
 
                 [(name0, name0, 'file', './' + name0),
711
 
                  (name1, name1, 'directory', './' + name1),
712
 
                  (name2, name2, 'file', './' + name2),
713
 
                 ]
714
 
                ),
715
 
                ((name1, './' + name1),
716
 
                 [(name1 + '/' + name0, name0, 'file', './' + name1
717
 
                                                        + '/' + name0),
718
 
                  (name1 + '/' + name1, name1, 'directory', './' + name1
719
 
                                                            + '/' + name1),
720
 
                 ]
721
 
                ),
722
 
                ((name1 + '/' + name1, './' + name1 + '/' + name1),
723
 
                 [
724
 
                 ]
725
 
                ),
726
 
            ]
727
 
        result = list(osutils.walkdirs('.'))
728
 
        self._filter_out_stat(result)
729
 
        self.assertEqual(expected_dirblocks, result)
730
 
        result = list(osutils.walkdirs(u'./'+name1, name1))
731
 
        self._filter_out_stat(result)
732
 
        self.assertEqual(expected_dirblocks[1:], result)
733
 
 
734
 
    def test_unicode__walkdirs_utf8(self):
735
 
        """Walkdirs_utf8 should always return utf8 paths.
736
 
 
737
 
        The abspath portion might be in unicode or utf-8
738
 
        """
739
 
        name0 = u'0file-\xb6'
740
 
        name1 = u'1dir-\u062c\u0648'
741
 
        name2 = u'2file-\u0633'
742
 
        tree = [
743
 
            name0,
744
 
            name1 + '/',
745
 
            name1 + '/' + name0,
746
 
            name1 + '/' + name1 + '/',
747
 
            name2,
748
 
            ]
749
 
        try:
750
 
            self.build_tree(tree)
751
 
        except UnicodeError:
752
 
            raise TestSkipped('Could not represent Unicode chars'
753
 
                              ' in current encoding.')
754
 
        name0 = name0.encode('utf8')
755
 
        name1 = name1.encode('utf8')
756
 
        name2 = name2.encode('utf8')
757
 
 
758
 
        expected_dirblocks = [
759
 
                (('', '.'),
760
 
                 [(name0, name0, 'file', './' + name0),
761
 
                  (name1, name1, 'directory', './' + name1),
762
 
                  (name2, name2, 'file', './' + name2),
763
 
                 ]
764
 
                ),
765
 
                ((name1, './' + name1),
766
 
                 [(name1 + '/' + name0, name0, 'file', './' + name1
767
 
                                                        + '/' + name0),
768
 
                  (name1 + '/' + name1, name1, 'directory', './' + name1
769
 
                                                            + '/' + name1),
770
 
                 ]
771
 
                ),
772
 
                ((name1 + '/' + name1, './' + name1 + '/' + name1),
773
 
                 [
774
 
                 ]
775
 
                ),
776
 
            ]
777
 
        result = []
778
 
        # For ease in testing, if walkdirs_utf8 returns Unicode, assert that
779
 
        # all abspaths are Unicode, and encode them back into utf8.
780
 
        for dirdetail, dirblock in osutils._walkdirs_utf8('.'):
781
 
            self.assertIsInstance(dirdetail[0], str)
782
 
            if isinstance(dirdetail[1], unicode):
783
 
                dirdetail = (dirdetail[0], dirdetail[1].encode('utf8'))
784
 
                dirblock = [list(info) for info in dirblock]
785
 
                for info in dirblock:
786
 
                    self.assertIsInstance(info[4], unicode)
787
 
                    info[4] = info[4].encode('utf8')
788
 
            new_dirblock = []
789
 
            for info in dirblock:
790
 
                self.assertIsInstance(info[0], str)
791
 
                self.assertIsInstance(info[1], str)
792
 
                self.assertIsInstance(info[4], str)
793
 
                # Remove the stat information
794
 
                new_dirblock.append((info[0], info[1], info[2], info[4]))
795
 
            result.append((dirdetail, new_dirblock))
796
 
        self.assertEqual(expected_dirblocks, result)
797
 
 
798
 
    def test_unicode__walkdirs_unicode_to_utf8(self):
799
 
        """walkdirs_unicode_to_utf8 should be a safe fallback everywhere
800
 
 
801
 
        The abspath portion should be in unicode
802
 
        """
803
 
        name0u = u'0file-\xb6'
804
 
        name1u = u'1dir-\u062c\u0648'
805
 
        name2u = u'2file-\u0633'
806
 
        tree = [
807
 
            name0u,
808
 
            name1u + '/',
809
 
            name1u + '/' + name0u,
810
 
            name1u + '/' + name1u + '/',
811
 
            name2u,
812
 
            ]
813
 
        try:
814
 
            self.build_tree(tree)
815
 
        except UnicodeError:
816
 
            raise TestSkipped('Could not represent Unicode chars'
817
 
                              ' in current encoding.')
818
 
        name0 = name0u.encode('utf8')
819
 
        name1 = name1u.encode('utf8')
820
 
        name2 = name2u.encode('utf8')
821
 
 
822
 
        # All of the abspaths should be in unicode, all of the relative paths
823
 
        # should be in utf8
824
 
        expected_dirblocks = [
825
 
                (('', '.'),
826
 
                 [(name0, name0, 'file', './' + name0u),
827
 
                  (name1, name1, 'directory', './' + name1u),
828
 
                  (name2, name2, 'file', './' + name2u),
829
 
                 ]
830
 
                ),
831
 
                ((name1, './' + name1u),
832
 
                 [(name1 + '/' + name0, name0, 'file', './' + name1u
833
 
                                                        + '/' + name0u),
834
 
                  (name1 + '/' + name1, name1, 'directory', './' + name1u
835
 
                                                            + '/' + name1u),
836
 
                 ]
837
 
                ),
838
 
                ((name1 + '/' + name1, './' + name1u + '/' + name1u),
839
 
                 [
840
 
                 ]
841
 
                ),
842
 
            ]
843
 
        result = list(osutils._walkdirs_unicode_to_utf8('.'))
844
 
        self._filter_out_stat(result)
845
 
        self.assertEqual(expected_dirblocks, result)
846
 
 
847
 
    def assertPathCompare(self, path_less, path_greater):
848
 
        """check that path_less and path_greater compare correctly."""
849
 
        self.assertEqual(0, osutils.compare_paths_prefix_order(
850
 
            path_less, path_less))
851
 
        self.assertEqual(0, osutils.compare_paths_prefix_order(
852
 
            path_greater, path_greater))
853
 
        self.assertEqual(-1, osutils.compare_paths_prefix_order(
854
 
            path_less, path_greater))
855
 
        self.assertEqual(1, osutils.compare_paths_prefix_order(
856
 
            path_greater, path_less))
857
 
 
858
 
    def test_compare_paths_prefix_order(self):
859
 
        # root before all else
860
 
        self.assertPathCompare("/", "/a")
861
 
        # alpha within a dir
862
 
        self.assertPathCompare("/a", "/b")
863
 
        self.assertPathCompare("/b", "/z")
864
 
        # high dirs before lower.
865
 
        self.assertPathCompare("/z", "/a/a")
866
 
        # except if the deeper dir should be output first
867
 
        self.assertPathCompare("/a/b/c", "/d/g")
868
 
        # lexical betwen dirs of the same height
869
 
        self.assertPathCompare("/a/z", "/z/z")
870
 
        self.assertPathCompare("/a/c/z", "/a/d/e")
871
 
 
872
 
        # this should also be consistent for no leading / paths
873
 
        # root before all else
874
 
        self.assertPathCompare("", "a")
875
 
        # alpha within a dir
876
 
        self.assertPathCompare("a", "b")
877
 
        self.assertPathCompare("b", "z")
878
 
        # high dirs before lower.
879
 
        self.assertPathCompare("z", "a/a")
880
 
        # except if the deeper dir should be output first
881
 
        self.assertPathCompare("a/b/c", "d/g")
882
 
        # lexical betwen dirs of the same height
883
 
        self.assertPathCompare("a/z", "z/z")
884
 
        self.assertPathCompare("a/c/z", "a/d/e")
885
 
 
886
 
    def test_path_prefix_sorting(self):
887
 
        """Doing a sort on path prefix should match our sample data."""
888
 
        original_paths = [
889
 
            'a',
890
 
            'a/b',
891
 
            'a/b/c',
892
 
            'b',
893
 
            'b/c',
894
 
            'd',
895
 
            'd/e',
896
 
            'd/e/f',
897
 
            'd/f',
898
 
            'd/g',
899
 
            'g',
900
 
            ]
901
 
 
902
 
        dir_sorted_paths = [
903
 
            'a',
904
 
            'b',
905
 
            'd',
906
 
            'g',
907
 
            'a/b',
908
 
            'a/b/c',
909
 
            'b/c',
910
 
            'd/e',
911
 
            'd/f',
912
 
            'd/g',
913
 
            'd/e/f',
914
 
            ]
915
 
 
916
 
        self.assertEqual(
917
 
            dir_sorted_paths,
918
 
            sorted(original_paths, key=osutils.path_prefix_key))
919
 
        # using the comparison routine shoudl work too:
920
 
        self.assertEqual(
921
 
            dir_sorted_paths,
922
 
            sorted(original_paths, cmp=osutils.compare_paths_prefix_order))
923
 
 
924
 
 
925
 
class TestCopyTree(TestCaseInTempDir):
926
 
    
927
 
    def test_copy_basic_tree(self):
928
 
        self.build_tree(['source/', 'source/a', 'source/b/', 'source/b/c'])
929
 
        osutils.copy_tree('source', 'target')
930
 
        self.assertEqual(['a', 'b'], sorted(os.listdir('target')))
931
 
        self.assertEqual(['c'], os.listdir('target/b'))
932
 
 
933
 
    def test_copy_tree_target_exists(self):
934
 
        self.build_tree(['source/', 'source/a', 'source/b/', 'source/b/c',
935
 
                         'target/'])
936
 
        osutils.copy_tree('source', 'target')
937
 
        self.assertEqual(['a', 'b'], sorted(os.listdir('target')))
938
 
        self.assertEqual(['c'], os.listdir('target/b'))
939
 
 
940
 
    def test_copy_tree_symlinks(self):
941
 
        if not osutils.has_symlinks():
942
 
            return
943
 
        self.build_tree(['source/'])
944
 
        os.symlink('a/generic/path', 'source/lnk')
945
 
        osutils.copy_tree('source', 'target')
946
 
        self.assertEqual(['lnk'], os.listdir('target'))
947
 
        self.assertEqual('a/generic/path', os.readlink('target/lnk'))
948
 
 
949
 
    def test_copy_tree_handlers(self):
950
 
        processed_files = []
951
 
        processed_links = []
952
 
        def file_handler(from_path, to_path):
953
 
            processed_files.append(('f', from_path, to_path))
954
 
        def dir_handler(from_path, to_path):
955
 
            processed_files.append(('d', from_path, to_path))
956
 
        def link_handler(from_path, to_path):
957
 
            processed_links.append((from_path, to_path))
958
 
        handlers = {'file':file_handler,
959
 
                    'directory':dir_handler,
960
 
                    'symlink':link_handler,
961
 
                   }
962
 
 
963
 
        self.build_tree(['source/', 'source/a', 'source/b/', 'source/b/c'])
964
 
        if osutils.has_symlinks():
965
 
            os.symlink('a/generic/path', 'source/lnk')
966
 
        osutils.copy_tree('source', 'target', handlers=handlers)
967
 
 
968
 
        self.assertEqual([('d', 'source', 'target'),
969
 
                          ('f', 'source/a', 'target/a'),
970
 
                          ('d', 'source/b', 'target/b'),
971
 
                          ('f', 'source/b/c', 'target/b/c'),
972
 
                         ], processed_files)
973
 
        self.failIfExists('target')
974
 
        if osutils.has_symlinks():
975
 
            self.assertEqual([('source/lnk', 'target/lnk')], processed_links)
976
 
 
977
 
 
978
 
#class TestTerminalEncoding has been moved to test_osutils_encodings.py
979
 
# [bialix] 2006/12/26
980
 
 
981
 
 
982
 
class TestSetUnsetEnv(TestCase):
983
 
    """Test updating the environment"""
984
 
 
985
 
    def setUp(self):
986
 
        super(TestSetUnsetEnv, self).setUp()
987
 
 
988
 
        self.assertEqual(None, os.environ.get('BZR_TEST_ENV_VAR'),
989
 
                         'Environment was not cleaned up properly.'
990
 
                         ' Variable BZR_TEST_ENV_VAR should not exist.')
991
 
        def cleanup():
992
 
            if 'BZR_TEST_ENV_VAR' in os.environ:
993
 
                del os.environ['BZR_TEST_ENV_VAR']
994
 
 
995
 
        self.addCleanup(cleanup)
996
 
 
997
 
    def test_set(self):
998
 
        """Test that we can set an env variable"""
999
 
        old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'foo')
1000
 
        self.assertEqual(None, old)
1001
 
        self.assertEqual('foo', os.environ.get('BZR_TEST_ENV_VAR'))
1002
 
 
1003
 
    def test_double_set(self):
1004
 
        """Test that we get the old value out"""
1005
 
        osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'foo')
1006
 
        old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'bar')
1007
 
        self.assertEqual('foo', old)
1008
 
        self.assertEqual('bar', os.environ.get('BZR_TEST_ENV_VAR'))
1009
 
 
1010
 
    def test_unicode(self):
1011
 
        """Environment can only contain plain strings
1012
 
        
1013
 
        So Unicode strings must be encoded.
1014
 
        """
1015
 
        uni_val, env_val = probe_unicode_in_user_encoding()
1016
 
        if uni_val is None:
1017
 
            raise TestSkipped('Cannot find a unicode character that works in'
1018
 
                              ' encoding %s' % (bzrlib.user_encoding,))
1019
 
 
1020
 
        old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', uni_val)
1021
 
        self.assertEqual(env_val, os.environ.get('BZR_TEST_ENV_VAR'))
1022
 
 
1023
 
    def test_unset(self):
1024
 
        """Test that passing None will remove the env var"""
1025
 
        osutils.set_or_unset_env('BZR_TEST_ENV_VAR', 'foo')
1026
 
        old = osutils.set_or_unset_env('BZR_TEST_ENV_VAR', None)
1027
 
        self.assertEqual('foo', old)
1028
 
        self.assertEqual(None, os.environ.get('BZR_TEST_ENV_VAR'))
1029
 
        self.failIf('BZR_TEST_ENV_VAR' in os.environ)
1030
 
 
1031
 
 
1032
 
class TestLocalTimeOffset(TestCase):
1033
 
 
1034
 
    def test_local_time_offset(self):
1035
 
        """Test that local_time_offset() returns a sane value."""
1036
 
        offset = osutils.local_time_offset()
1037
 
        self.assertTrue(isinstance(offset, int))
1038
 
        # Test that the offset is no more than a eighteen hours in
1039
 
        # either direction.
1040
 
        # Time zone handling is system specific, so it is difficult to
1041
 
        # do more specific tests, but a value outside of this range is
1042
 
        # probably wrong.
1043
 
        eighteen_hours = 18 * 3600
1044
 
        self.assertTrue(-eighteen_hours < offset < eighteen_hours)
1045
 
 
1046
 
    def test_local_time_offset_with_timestamp(self):
1047
 
        """Test that local_time_offset() works with a timestamp."""
1048
 
        offset = osutils.local_time_offset(1000000000.1234567)
1049
 
        self.assertTrue(isinstance(offset, int))
1050
 
        eighteen_hours = 18 * 3600
1051
 
        self.assertTrue(-eighteen_hours < offset < eighteen_hours)