~soren/nova/iptables-security-groups

« back to all changes in this revision

Viewing changes to vendor/Twisted-10.0.0/twisted/test/test_paths.py

  • Committer: Jesse Andrews
  • Date: 2010-05-28 06:05:26 UTC
  • Revision ID: git-v1:bf6e6e718cdc7488e2da87b21e258ccc065fe499
initial commit

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (c) 2001-2009 Twisted Matrix Laboratories.
 
2
# See LICENSE for details.
 
3
 
 
4
"""
 
5
Test cases covering L{twisted.python.filepath} and L{twisted.python.zippath}.
 
6
"""
 
7
 
 
8
import os, time, pickle, errno, zipfile, stat
 
9
 
 
10
from twisted.python.compat import set
 
11
from twisted.python.win32 import WindowsError, ERROR_DIRECTORY
 
12
from twisted.python import filepath
 
13
from twisted.python.zippath import ZipArchive
 
14
from twisted.python.runtime import platform
 
15
 
 
16
from twisted.trial import unittest
 
17
 
 
18
 
 
19
class AbstractFilePathTestCase(unittest.TestCase):
 
20
 
 
21
    f1content = "file 1"
 
22
    f2content = "file 2"
 
23
 
 
24
    def _mkpath(self, *p):
 
25
        x = os.path.abspath(os.path.join(self.cmn, *p))
 
26
        self.all.append(x)
 
27
        return x
 
28
 
 
29
 
 
30
    def subdir(self, *dirname):
 
31
        os.mkdir(self._mkpath(*dirname))
 
32
 
 
33
 
 
34
    def subfile(self, *dirname):
 
35
        return open(self._mkpath(*dirname), "wb")
 
36
 
 
37
 
 
38
    def setUp(self):
 
39
        self.now = time.time()
 
40
        cmn = self.cmn = os.path.abspath(self.mktemp())
 
41
        self.all = [cmn]
 
42
        os.mkdir(cmn)
 
43
        self.subdir("sub1")
 
44
        f = self.subfile("file1")
 
45
        f.write(self.f1content)
 
46
        f.close()
 
47
        f = self.subfile("sub1", "file2")
 
48
        f.write(self.f2content)
 
49
        f.close()
 
50
        self.subdir('sub3')
 
51
        f = self.subfile("sub3", "file3.ext1")
 
52
        f.close()
 
53
        f = self.subfile("sub3", "file3.ext2")
 
54
        f.close()
 
55
        f = self.subfile("sub3", "file3.ext3")
 
56
        f.close()
 
57
        self.path = filepath.FilePath(cmn)
 
58
        self.root = filepath.FilePath("/")
 
59
 
 
60
 
 
61
    def test_segmentsFromPositive(self):
 
62
        """
 
63
        Verify that the segments between two paths are correctly identified.
 
64
        """
 
65
        self.assertEquals(
 
66
            self.path.child("a").child("b").child("c").segmentsFrom(self.path),
 
67
            ["a", "b", "c"])
 
68
 
 
69
    def test_segmentsFromNegative(self):
 
70
        """Verify that segmentsFrom notices when the ancestor isn't an ancestor.
 
71
        """
 
72
        self.assertRaises(
 
73
            ValueError,
 
74
            self.path.child("a").child("b").child("c").segmentsFrom,
 
75
                self.path.child("d").child("c").child("e"))
 
76
 
 
77
 
 
78
    def test_walk(self):
 
79
        """
 
80
        Verify that walking the path gives the same result as the known file
 
81
        hierarchy.
 
82
        """
 
83
        x = [foo.path for foo in self.path.walk()]
 
84
        self.assertEquals(set(x), set(self.all))
 
85
 
 
86
 
 
87
    def test_parents(self):
 
88
        """
 
89
        L{FilePath.parents()} should return an iterator of every ancestor of
 
90
        the L{FilePath} in question.
 
91
        """
 
92
        L = []
 
93
        pathobj = self.path.child("a").child("b").child("c")
 
94
        fullpath = pathobj.path
 
95
        lastpath = fullpath
 
96
        thispath = os.path.dirname(fullpath)
 
97
        while lastpath != self.root.path:
 
98
            L.append(thispath)
 
99
            lastpath = thispath
 
100
            thispath = os.path.dirname(thispath)
 
101
        self.assertEquals([x.path for x in pathobj.parents()], L)
 
102
 
 
103
 
 
104
    def test_validSubdir(self):
 
105
        """Verify that a valid subdirectory will show up as a directory, but not as a
 
106
        file, not as a symlink, and be listable.
 
107
        """
 
108
        sub1 = self.path.child('sub1')
 
109
        self.failUnless(sub1.exists(),
 
110
                        "This directory does exist.")
 
111
        self.failUnless(sub1.isdir(),
 
112
                        "It's a directory.")
 
113
        self.failUnless(not sub1.isfile(),
 
114
                        "It's a directory.")
 
115
        self.failUnless(not sub1.islink(),
 
116
                        "It's a directory.")
 
117
        self.failUnlessEqual(sub1.listdir(),
 
118
                             ['file2'])
 
119
 
 
120
 
 
121
    def test_invalidSubdir(self):
 
122
        """
 
123
        Verify that a subdirectory that doesn't exist is reported as such.
 
124
        """
 
125
        sub2 = self.path.child('sub2')
 
126
        self.failIf(sub2.exists(),
 
127
                    "This directory does not exist.")
 
128
 
 
129
    def test_validFiles(self):
 
130
        """
 
131
        Make sure that we can read existent non-empty files.
 
132
        """
 
133
        f1 = self.path.child('file1')
 
134
        self.failUnlessEqual(f1.open().read(), self.f1content)
 
135
        f2 = self.path.child('sub1').child('file2')
 
136
        self.failUnlessEqual(f2.open().read(), self.f2content)
 
137
 
 
138
 
 
139
    def test_dictionaryKeys(self):
 
140
        """
 
141
        Verify that path instances are usable as dictionary keys.
 
142
        """
 
143
        f1 = self.path.child('file1')
 
144
        f1prime = self.path.child('file1')
 
145
        f2 = self.path.child('file2')
 
146
        dictoid = {}
 
147
        dictoid[f1] = 3
 
148
        dictoid[f1prime] = 4
 
149
        self.assertEquals(dictoid[f1], 4)
 
150
        self.assertEquals(dictoid.keys(), [f1])
 
151
        self.assertIdentical(dictoid.keys()[0], f1)
 
152
        self.assertNotIdentical(dictoid.keys()[0], f1prime) # sanity check
 
153
        dictoid[f2] = 5
 
154
        self.assertEquals(dictoid[f2], 5)
 
155
        self.assertEquals(len(dictoid), 2)
 
156
 
 
157
 
 
158
    def test_dictionaryKeyWithString(self):
 
159
        """
 
160
        Verify that path instances are usable as dictionary keys which do not clash
 
161
        with their string counterparts.
 
162
        """
 
163
        f1 = self.path.child('file1')
 
164
        dictoid = {f1: 'hello'}
 
165
        dictoid[f1.path] = 'goodbye'
 
166
        self.assertEquals(len(dictoid), 2)
 
167
 
 
168
 
 
169
    def test_childrenNonexistentError(self):
 
170
        """
 
171
        Verify that children raises the appropriate exception for non-existent
 
172
        directories.
 
173
        """
 
174
        self.assertRaises(filepath.UnlistableError,
 
175
                          self.path.child('not real').children)
 
176
 
 
177
    def test_childrenNotDirectoryError(self):
 
178
        """
 
179
        Verify that listdir raises the appropriate exception for attempting to list
 
180
        a file rather than a directory.
 
181
        """
 
182
        self.assertRaises(filepath.UnlistableError,
 
183
                          self.path.child('file1').children)
 
184
 
 
185
 
 
186
    def test_newTimesAreFloats(self):
 
187
        """
 
188
        Verify that all times returned from the various new time functions are ints
 
189
        (and hopefully therefore 'high precision').
 
190
        """
 
191
        for p in self.path, self.path.child('file1'):
 
192
            self.failUnlessEqual(type(p.getAccessTime()), float)
 
193
            self.failUnlessEqual(type(p.getModificationTime()), float)
 
194
            self.failUnlessEqual(type(p.getStatusChangeTime()), float)
 
195
 
 
196
 
 
197
    def test_oldTimesAreInts(self):
 
198
        """
 
199
        Verify that all times returned from the various time functions are
 
200
        integers, for compatibility.
 
201
        """
 
202
        for p in self.path, self.path.child('file1'):
 
203
            self.failUnlessEqual(type(p.getatime()), int)
 
204
            self.failUnlessEqual(type(p.getmtime()), int)
 
205
            self.failUnlessEqual(type(p.getctime()), int)
 
206
 
 
207
 
 
208
 
 
209
class FakeWindowsPath(filepath.FilePath):
 
210
    """
 
211
    A test version of FilePath which overrides listdir to raise L{WindowsError}.
 
212
    """
 
213
 
 
214
    def listdir(self):
 
215
        """
 
216
        @raise WindowsError: always.
 
217
        """
 
218
        raise WindowsError(
 
219
            ERROR_DIRECTORY,
 
220
            "A directory's validness was called into question")
 
221
 
 
222
 
 
223
class ListingCompatibilityTests(unittest.TestCase):
 
224
    """
 
225
    These tests verify compatibility with legacy behavior of directory listing.
 
226
    """
 
227
 
 
228
    def test_windowsErrorExcept(self):
 
229
        """
 
230
        Verify that when a WindowsError is raised from listdir, catching
 
231
        WindowsError works.
 
232
        """
 
233
        fwp = FakeWindowsPath(self.mktemp())
 
234
        self.assertRaises(filepath.UnlistableError, fwp.children)
 
235
        self.assertRaises(WindowsError, fwp.children)
 
236
 
 
237
 
 
238
    def test_alwaysCatchOSError(self):
 
239
        """
 
240
        Verify that in the normal case where a directory does not exist, we will
 
241
        get an OSError.
 
242
        """
 
243
        fp = filepath.FilePath(self.mktemp())
 
244
        self.assertRaises(OSError, fp.children)
 
245
 
 
246
 
 
247
    def test_keepOriginalAttributes(self):
 
248
        """
 
249
        Verify that the Unlistable exception raised will preserve the attributes of
 
250
        the previously-raised exception.
 
251
        """
 
252
        fp = filepath.FilePath(self.mktemp())
 
253
        ose = self.assertRaises(OSError, fp.children)
 
254
        d1 = ose.__dict__.keys()
 
255
        d1.remove('originalException')
 
256
        d2 = ose.originalException.__dict__.keys()
 
257
        d1.sort()
 
258
        d2.sort()
 
259
        self.assertEquals(d1, d2)
 
260
 
 
261
 
 
262
 
 
263
def zipit(dirname, zfname):
 
264
    """
 
265
    create a zipfile on zfname, containing the contents of dirname'
 
266
    """
 
267
    zf = zipfile.ZipFile(zfname, "w")
 
268
    basedir = os.path.basename(dirname)
 
269
    for root, dirs, files, in os.walk(dirname):
 
270
        for fname in files:
 
271
            fspath = os.path.join(root, fname)
 
272
            arcpath = os.path.join(root, fname)[len(dirname)+1:]
 
273
            # print fspath, '=>', arcpath
 
274
            zf.write(fspath, arcpath)
 
275
    zf.close()
 
276
 
 
277
 
 
278
 
 
279
class ZipFilePathTestCase(AbstractFilePathTestCase):
 
280
    """
 
281
    Test various L{ZipPath} path manipulations as well as reprs for L{ZipPath}
 
282
    and L{ZipArchive}.
 
283
    """
 
284
    def setUp(self):
 
285
        AbstractFilePathTestCase.setUp(self)
 
286
        zipit(self.cmn, self.cmn + '.zip')
 
287
        self.path = ZipArchive(self.cmn + '.zip')
 
288
        self.root = self.path
 
289
        self.all = [x.replace(self.cmn, self.cmn + '.zip') for x in self.all]
 
290
 
 
291
 
 
292
    def test_zipPathRepr(self):
 
293
        """
 
294
        Make sure that invoking ZipPath's repr prints the correct class name
 
295
        and an absolute path to the zip file.
 
296
        """
 
297
        child = self.path.child("foo")
 
298
        pathRepr = "ZipPath(%r)" % (
 
299
            os.path.abspath(self.cmn + ".zip" + os.sep + 'foo'),)
 
300
 
 
301
        # Check for an absolute path
 
302
        self.assertEquals(repr(child), pathRepr)
 
303
 
 
304
        # Create a path to the file rooted in the current working directory
 
305
        relativeCommon = self.cmn.replace(os.getcwd() + os.sep, "", 1) + ".zip"
 
306
        relpath = ZipArchive(relativeCommon)
 
307
        child = relpath.child("foo")
 
308
 
 
309
        # Check using a path without the cwd prepended
 
310
        self.assertEquals(repr(child), pathRepr)
 
311
 
 
312
 
 
313
    def test_zipArchiveRepr(self):
 
314
        """
 
315
        Make sure that invoking ZipArchive's repr prints the correct class
 
316
        name and an absolute path to the zip file.
 
317
        """
 
318
        pathRepr = 'ZipArchive(%r)' % (os.path.abspath(self.cmn + '.zip'),)
 
319
 
 
320
        # Check for an absolute path
 
321
        self.assertEquals(repr(self.path), pathRepr)
 
322
 
 
323
        # Create a path to the file rooted in the current working directory
 
324
        relativeCommon = self.cmn.replace(os.getcwd() + os.sep, "", 1) + ".zip"
 
325
        relpath = ZipArchive(relativeCommon)
 
326
 
 
327
        # Check using a path without the cwd prepended
 
328
        self.assertEquals(repr(relpath), pathRepr)
 
329
 
 
330
 
 
331
 
 
332
class FilePathTestCase(AbstractFilePathTestCase):
 
333
 
 
334
    def test_chmod(self):
 
335
        """
 
336
        Make sure that calling L{FilePath.chmod} modifies the permissions of
 
337
        the passed file as expected (using C{os.stat} to check). We use some
 
338
        basic modes that should work everywhere (even on Windows).
 
339
        """
 
340
        for mode in (0555, 0777):
 
341
            self.path.child("sub1").chmod(mode)
 
342
            self.assertEquals(
 
343
                stat.S_IMODE(os.stat(self.path.child("sub1").path).st_mode),
 
344
                mode)
 
345
 
 
346
 
 
347
    def symlink(self, target, name):
 
348
        """
 
349
        Create a symbolic link named C{name} pointing at C{target}.
 
350
 
 
351
        @type target: C{str}
 
352
        @type name: C{str}
 
353
        @raise SkipTest: raised if symbolic links are not supported on the
 
354
            host platform.
 
355
        """
 
356
        if getattr(os, 'symlink', None) is None:
 
357
            raise unittest.SkipTest(
 
358
                "Platform does not support symbolic links.")
 
359
        os.symlink(target, name)
 
360
 
 
361
 
 
362
    def createLinks(self):
 
363
        """
 
364
        Create several symbolic links to files and directories.
 
365
        """
 
366
        subdir = self.path.child("sub1")
 
367
        self.symlink(subdir.path, self._mkpath("sub1.link"))
 
368
        self.symlink(subdir.child("file2").path, self._mkpath("file2.link"))
 
369
        self.symlink(subdir.child("file2").path,
 
370
                     self._mkpath("sub1", "sub1.file2.link"))
 
371
 
 
372
 
 
373
    def test_realpathSymlink(self):
 
374
        """
 
375
        L{FilePath.realpath} returns the path of the ultimate target of a
 
376
        symlink.
 
377
        """
 
378
        self.createLinks()
 
379
        self.symlink(self.path.child("file2.link").path,
 
380
                     self.path.child("link.link").path)
 
381
        self.assertEquals(self.path.child("link.link").realpath(),
 
382
                          self.path.child("sub1").child("file2"))
 
383
 
 
384
 
 
385
    def test_realpathCyclicalSymlink(self):
 
386
        """
 
387
        L{FilePath.realpath} raises L{filepath.LinkError} if the path is a
 
388
        symbolic link which is part of a cycle.
 
389
        """
 
390
        self.symlink(self.path.child("link1").path, self.path.child("link2").path)
 
391
        self.symlink(self.path.child("link2").path, self.path.child("link1").path)
 
392
        self.assertRaises(filepath.LinkError,
 
393
                          self.path.child("link2").realpath)
 
394
 
 
395
 
 
396
    def test_realpathNoSymlink(self):
 
397
        """
 
398
        L{FilePath.realpath} returns the path itself if the path is not a
 
399
        symbolic link.
 
400
        """
 
401
        self.assertEquals(self.path.child("sub1").realpath(),
 
402
                          self.path.child("sub1"))
 
403
 
 
404
 
 
405
    def test_walkCyclicalSymlink(self):
 
406
        """
 
407
        Verify that walking a path with a cyclical symlink raises an error
 
408
        """
 
409
        self.createLinks()
 
410
        self.symlink(self.path.child("sub1").path,
 
411
                     self.path.child("sub1").child("sub1.loopylink").path)
 
412
        def iterateOverPath():
 
413
            return [foo.path for foo in self.path.walk()]
 
414
        self.assertRaises(filepath.LinkError, iterateOverPath)
 
415
 
 
416
 
 
417
    def test_walkObeysDescendWithCyclicalSymlinks(self):
 
418
        """
 
419
        Verify that, after making a path with cyclical symlinks, when the
 
420
        supplied C{descend} predicate returns C{False}, the target is not
 
421
        traversed, as if it was a simple symlink.
 
422
        """
 
423
        self.createLinks()
 
424
        # we create cyclical symlinks
 
425
        self.symlink(self.path.child("sub1").path,
 
426
                     self.path.child("sub1").child("sub1.loopylink").path)
 
427
        def noSymLinks(path):
 
428
            return not path.islink()
 
429
        def iterateOverPath():
 
430
            return [foo.path for foo in self.path.walk(descend=noSymLinks)]
 
431
        self.assertTrue(iterateOverPath())
 
432
 
 
433
 
 
434
    def test_walkObeysDescend(self):
 
435
        """
 
436
        Verify that when the supplied C{descend} predicate returns C{False},
 
437
        the target is not traversed.
 
438
        """
 
439
        self.createLinks()
 
440
        def noSymLinks(path):
 
441
            return not path.islink()
 
442
        x = [foo.path for foo in self.path.walk(descend=noSymLinks)]
 
443
        self.assertEquals(set(x), set(self.all))
 
444
 
 
445
 
 
446
    def test_getAndSet(self):
 
447
        content = 'newcontent'
 
448
        self.path.child('new').setContent(content)
 
449
        newcontent = self.path.child('new').getContent()
 
450
        self.failUnlessEqual(content, newcontent)
 
451
        content = 'content'
 
452
        self.path.child('new').setContent(content, '.tmp')
 
453
        newcontent = self.path.child('new').getContent()
 
454
        self.failUnlessEqual(content, newcontent)
 
455
 
 
456
 
 
457
    def test_symbolicLink(self):
 
458
        """
 
459
        Verify the behavior of the C{isLink} method against links and
 
460
        non-links. Also check that the symbolic link shares the directory
 
461
        property with its target.
 
462
        """
 
463
        s4 = self.path.child("sub4")
 
464
        s3 = self.path.child("sub3")
 
465
        self.symlink(s3.path, s4.path)
 
466
        self.assertTrue(s4.islink())
 
467
        self.assertFalse(s3.islink())
 
468
        self.assertTrue(s4.isdir())
 
469
        self.assertTrue(s3.isdir())
 
470
 
 
471
 
 
472
    def test_linkTo(self):
 
473
        """
 
474
        Verify that symlink creates a valid symlink that is both a link and a
 
475
        file if its target is a file, or a directory if its target is a
 
476
        directory.
 
477
        """
 
478
        targetLinks = [
 
479
            (self.path.child("sub2"), self.path.child("sub2.link")),
 
480
            (self.path.child("sub2").child("file3.ext1"),
 
481
             self.path.child("file3.ext1.link"))
 
482
            ]
 
483
        for target, link in targetLinks:
 
484
            target.linkTo(link)
 
485
            self.assertTrue(link.islink(), "This is a link")
 
486
            self.assertEquals(target.isdir(), link.isdir())
 
487
            self.assertEquals(target.isfile(), link.isfile())
 
488
 
 
489
 
 
490
    def test_linkToErrors(self):
 
491
        """
 
492
        Verify C{linkTo} fails in the following case:
 
493
            - the target is in a directory that doesn't exist
 
494
            - the target already exists
 
495
        """
 
496
        self.assertRaises(OSError, self.path.child("file1").linkTo,
 
497
                          self.path.child('nosub').child('file1'))
 
498
        self.assertRaises(OSError, self.path.child("file1").linkTo,
 
499
                          self.path.child('sub1').child('file2'))
 
500
 
 
501
 
 
502
    if not getattr(os, "symlink", None):
 
503
        skipMsg = "Your platform does not support symbolic links."
 
504
        test_symbolicLink.skip = skipMsg
 
505
        test_linkTo.skip = skipMsg
 
506
        test_linkToErrors.skip = skipMsg
 
507
 
 
508
 
 
509
    def testMultiExt(self):
 
510
        f3 = self.path.child('sub3').child('file3')
 
511
        exts = '.foo','.bar', 'ext1','ext2','ext3'
 
512
        self.failIf(f3.siblingExtensionSearch(*exts))
 
513
        f3e = f3.siblingExtension(".foo")
 
514
        f3e.touch()
 
515
        self.failIf(not f3.siblingExtensionSearch(*exts).exists())
 
516
        self.failIf(not f3.siblingExtensionSearch('*').exists())
 
517
        f3e.remove()
 
518
        self.failIf(f3.siblingExtensionSearch(*exts))
 
519
 
 
520
    def testPreauthChild(self):
 
521
        fp = filepath.FilePath('.')
 
522
        fp.preauthChild('foo/bar')
 
523
        self.assertRaises(filepath.InsecurePath, fp.child, '/foo')
 
524
 
 
525
    def testStatCache(self):
 
526
        p = self.path.child('stattest')
 
527
        p.touch()
 
528
        self.failUnlessEqual(p.getsize(), 0)
 
529
        self.failUnlessEqual(abs(p.getmtime() - time.time()) // 20, 0)
 
530
        self.failUnlessEqual(abs(p.getctime() - time.time()) // 20, 0)
 
531
        self.failUnlessEqual(abs(p.getatime() - time.time()) // 20, 0)
 
532
        self.failUnlessEqual(p.exists(), True)
 
533
        self.failUnlessEqual(p.exists(), True)
 
534
        # OOB removal: FilePath.remove() will automatically restat
 
535
        os.remove(p.path)
 
536
        # test caching
 
537
        self.failUnlessEqual(p.exists(), True)
 
538
        p.restat(reraise=False)
 
539
        self.failUnlessEqual(p.exists(), False)
 
540
        self.failUnlessEqual(p.islink(), False)
 
541
        self.failUnlessEqual(p.isdir(), False)
 
542
        self.failUnlessEqual(p.isfile(), False)
 
543
 
 
544
    def testPersist(self):
 
545
        newpath = pickle.loads(pickle.dumps(self.path))
 
546
        self.failUnlessEqual(self.path.__class__, newpath.__class__)
 
547
        self.failUnlessEqual(self.path.path, newpath.path)
 
548
 
 
549
    def testInsecureUNIX(self):
 
550
        self.assertRaises(filepath.InsecurePath, self.path.child, "..")
 
551
        self.assertRaises(filepath.InsecurePath, self.path.child, "/etc")
 
552
        self.assertRaises(filepath.InsecurePath, self.path.child, "../..")
 
553
 
 
554
    def testInsecureWin32(self):
 
555
        self.assertRaises(filepath.InsecurePath, self.path.child, r"..\..")
 
556
        self.assertRaises(filepath.InsecurePath, self.path.child, r"C:randomfile")
 
557
 
 
558
    if platform.getType() != 'win32':
 
559
        testInsecureWin32.skip = "Consider yourself lucky."
 
560
 
 
561
    def testInsecureWin32Whacky(self):
 
562
        """Windows has 'special' filenames like NUL and CON and COM1 and LPR
 
563
        and PRN and ... god knows what else.  They can be located anywhere in
 
564
        the filesystem.  For obvious reasons, we do not wish to normally permit
 
565
        access to these.
 
566
        """
 
567
        self.assertRaises(filepath.InsecurePath, self.path.child, "CON")
 
568
        self.assertRaises(filepath.InsecurePath, self.path.child, "C:CON")
 
569
        self.assertRaises(filepath.InsecurePath, self.path.child, r"C:\CON")
 
570
 
 
571
    if platform.getType() != 'win32':
 
572
        testInsecureWin32Whacky.skip = "Consider yourself lucky."
 
573
 
 
574
    def testComparison(self):
 
575
        self.assertEquals(filepath.FilePath('a'),
 
576
                          filepath.FilePath('a'))
 
577
        self.failUnless(filepath.FilePath('z') >
 
578
                        filepath.FilePath('a'))
 
579
        self.failUnless(filepath.FilePath('z') >=
 
580
                        filepath.FilePath('a'))
 
581
        self.failUnless(filepath.FilePath('a') >=
 
582
                        filepath.FilePath('a'))
 
583
        self.failUnless(filepath.FilePath('a') <=
 
584
                        filepath.FilePath('a'))
 
585
        self.failUnless(filepath.FilePath('a') <
 
586
                        filepath.FilePath('z'))
 
587
        self.failUnless(filepath.FilePath('a') <=
 
588
                        filepath.FilePath('z'))
 
589
        self.failUnless(filepath.FilePath('a') !=
 
590
                        filepath.FilePath('z'))
 
591
        self.failUnless(filepath.FilePath('z') !=
 
592
                        filepath.FilePath('a'))
 
593
 
 
594
        self.failIf(filepath.FilePath('z') !=
 
595
                    filepath.FilePath('z'))
 
596
 
 
597
    def testSibling(self):
 
598
        p = self.path.child('sibling_start')
 
599
        ts = p.sibling('sibling_test')
 
600
        self.assertEquals(ts.dirname(), p.dirname())
 
601
        self.assertEquals(ts.basename(), 'sibling_test')
 
602
        ts.createDirectory()
 
603
        self.assertIn(ts, self.path.children())
 
604
 
 
605
    def testTemporarySibling(self):
 
606
        ts = self.path.temporarySibling()
 
607
        self.assertEquals(ts.dirname(), self.path.dirname())
 
608
        self.assertNotIn(ts.basename(), self.path.listdir())
 
609
        ts.createDirectory()
 
610
        self.assertIn(ts, self.path.parent().children())
 
611
 
 
612
    def testRemove(self):
 
613
        self.path.remove()
 
614
        self.failIf(self.path.exists())
 
615
 
 
616
 
 
617
    def test_removeWithSymlink(self):
 
618
        """
 
619
        For a path which is a symbolic link, L{FilePath.remove} just deletes
 
620
        the link, not the target.
 
621
        """
 
622
        link = self.path.child("sub1.link")
 
623
        # setUp creates the sub1 child
 
624
        self.symlink(self.path.child("sub1").path, link.path)
 
625
        link.remove()
 
626
        self.assertFalse(link.exists())
 
627
        self.assertTrue(self.path.child("sub1").exists())
 
628
 
 
629
 
 
630
    def test_copyTo(self):
 
631
        self.assertRaises((OSError, IOError), self.path.copyTo, self.path.child('file1'))
 
632
        oldPaths = list(self.path.walk()) # Record initial state
 
633
        fp = filepath.FilePath(self.mktemp())
 
634
        self.path.copyTo(fp)
 
635
        self.path.remove()
 
636
        fp.copyTo(self.path)
 
637
        newPaths = list(self.path.walk()) # Record double-copy state
 
638
        newPaths.sort()
 
639
        oldPaths.sort()
 
640
        self.assertEquals(newPaths, oldPaths)
 
641
 
 
642
 
 
643
    def test_copyToWithSymlink(self):
 
644
        """
 
645
        Verify that copying with followLinks=True copies symlink targets
 
646
        instead of symlinks
 
647
        """
 
648
        self.symlink(self.path.child("sub1").path,
 
649
                     self.path.child("link1").path)
 
650
        fp = filepath.FilePath(self.mktemp())
 
651
        self.path.copyTo(fp)
 
652
        self.assertFalse(fp.child("link1").islink())
 
653
        self.assertEquals([x.basename() for x in fp.child("sub1").children()],
 
654
                          [x.basename() for x in fp.child("link1").children()])
 
655
 
 
656
 
 
657
    def test_copyToWithoutSymlink(self):
 
658
        """
 
659
        Verify that copying with followLinks=False copies symlinks as symlinks
 
660
        """
 
661
        self.symlink("sub1", self.path.child("link1").path)
 
662
        fp = filepath.FilePath(self.mktemp())
 
663
        self.path.copyTo(fp, followLinks=False)
 
664
        self.assertTrue(fp.child("link1").islink())
 
665
        self.assertEquals(os.readlink(self.path.child("link1").path),
 
666
                          os.readlink(fp.child("link1").path))
 
667
 
 
668
 
 
669
    def test_moveTo(self):
 
670
        """
 
671
        Verify that moving an entire directory results into another directory
 
672
        with the same content.
 
673
        """
 
674
        oldPaths = list(self.path.walk()) # Record initial state
 
675
        fp = filepath.FilePath(self.mktemp())
 
676
        self.path.moveTo(fp)
 
677
        fp.moveTo(self.path)
 
678
        newPaths = list(self.path.walk()) # Record double-move state
 
679
        newPaths.sort()
 
680
        oldPaths.sort()
 
681
        self.assertEquals(newPaths, oldPaths)
 
682
 
 
683
 
 
684
    def test_moveToError(self):
 
685
        """
 
686
        Verify error behavior of moveTo: it should raises one of OSError or
 
687
        IOError if you want to move a path into one of its child. It's simply
 
688
        the error raised by the underlying rename system call.
 
689
        """
 
690
        self.assertRaises((OSError, IOError), self.path.moveTo, self.path.child('file1'))
 
691
 
 
692
 
 
693
    def setUpFaultyRename(self):
 
694
        """
 
695
        Set up a C{os.rename} that will fail with L{errno.EXDEV} on first call.
 
696
        This is used to simulate a cross-device rename failure.
 
697
 
 
698
        @return: a list of pair (src, dest) of calls to C{os.rename}
 
699
        @rtype: C{list} of C{tuple}
 
700
        """
 
701
        invokedWith = []
 
702
        def faultyRename(src, dest):
 
703
            invokedWith.append((src, dest))
 
704
            if len(invokedWith) == 1:
 
705
                raise OSError(errno.EXDEV, 'Test-induced failure simulating '
 
706
                                           'cross-device rename failure')
 
707
            return originalRename(src, dest)
 
708
 
 
709
        originalRename = os.rename
 
710
        self.patch(os, "rename", faultyRename)
 
711
        return invokedWith
 
712
 
 
713
 
 
714
    def test_crossMountMoveTo(self):
 
715
        """
 
716
        C{moveTo} should be able to handle C{EXDEV} error raised by
 
717
        C{os.rename} when trying to move a file on a different mounted
 
718
        filesystem.
 
719
        """
 
720
        invokedWith = self.setUpFaultyRename()
 
721
        # Bit of a whitebox test - force os.rename, which moveTo tries
 
722
        # before falling back to a slower method, to fail, forcing moveTo to
 
723
        # use the slower behavior.
 
724
        self.test_moveTo()
 
725
        # A bit of a sanity check for this whitebox test - if our rename
 
726
        # was never invoked, the test has probably fallen into disrepair!
 
727
        self.assertTrue(invokedWith)
 
728
 
 
729
 
 
730
    def test_crossMountMoveToWithSymlink(self):
 
731
        """
 
732
        By default, when moving a symlink, it should follow the link and
 
733
        actually copy the content of the linked node.
 
734
        """
 
735
        invokedWith = self.setUpFaultyRename()
 
736
        f2 = self.path.child('file2')
 
737
        f3 = self.path.child('file3')
 
738
        self.symlink(self.path.child('file1').path, f2.path)
 
739
        f2.moveTo(f3)
 
740
        self.assertFalse(f3.islink())
 
741
        self.assertEquals(f3.getContent(), 'file 1')
 
742
        self.assertTrue(invokedWith)
 
743
 
 
744
 
 
745
    def test_crossMountMoveToWithoutSymlink(self):
 
746
        """
 
747
        Verify that moveTo called with followLinks=False actually create
 
748
        another symlink.
 
749
        """
 
750
        invokedWith = self.setUpFaultyRename()
 
751
        f2 = self.path.child('file2')
 
752
        f3 = self.path.child('file3')
 
753
        self.symlink(self.path.child('file1').path, f2.path)
 
754
        f2.moveTo(f3, followLinks=False)
 
755
        self.assertTrue(f3.islink())
 
756
        self.assertEquals(f3.getContent(), 'file 1')
 
757
        self.assertTrue(invokedWith)
 
758
 
 
759
 
 
760
    def testOpen(self):
 
761
        # Opening a file for reading when it does not already exist is an error
 
762
        nonexistent = self.path.child('nonexistent')
 
763
        e = self.assertRaises(IOError, nonexistent.open)
 
764
        self.assertEquals(e.errno, errno.ENOENT)
 
765
 
 
766
        # Opening a file for writing when it does not exist is okay
 
767
        writer = self.path.child('writer')
 
768
        f = writer.open('w')
 
769
        f.write('abc\ndef')
 
770
        f.close()
 
771
 
 
772
        # Make sure those bytes ended up there - and test opening a file for
 
773
        # reading when it does exist at the same time
 
774
        f = writer.open()
 
775
        self.assertEquals(f.read(), 'abc\ndef')
 
776
        f.close()
 
777
 
 
778
        # Re-opening that file in write mode should erase whatever was there.
 
779
        f = writer.open('w')
 
780
        f.close()
 
781
        f = writer.open()
 
782
        self.assertEquals(f.read(), '')
 
783
        f.close()
 
784
 
 
785
        # Put some bytes in a file so we can test that appending does not
 
786
        # destroy them.
 
787
        appender = self.path.child('appender')
 
788
        f = appender.open('w')
 
789
        f.write('abc')
 
790
        f.close()
 
791
 
 
792
        f = appender.open('a')
 
793
        f.write('def')
 
794
        f.close()
 
795
 
 
796
        f = appender.open('r')
 
797
        self.assertEquals(f.read(), 'abcdef')
 
798
        f.close()
 
799
 
 
800
        # read/write should let us do both without erasing those bytes
 
801
        f = appender.open('r+')
 
802
        self.assertEquals(f.read(), 'abcdef')
 
803
        # ANSI C *requires* an fseek or an fgetpos between an fread and an
 
804
        # fwrite or an fwrite and a fread.  We can't reliable get Python to
 
805
        # invoke fgetpos, so we seek to a 0 byte offset from the current
 
806
        # position instead.  Also, Python sucks for making this seek
 
807
        # relative to 1 instead of a symbolic constant representing the
 
808
        # current file position.
 
809
        f.seek(0, 1)
 
810
        # Put in some new bytes for us to test for later.
 
811
        f.write('ghi')
 
812
        f.close()
 
813
 
 
814
        # Make sure those new bytes really showed up
 
815
        f = appender.open('r')
 
816
        self.assertEquals(f.read(), 'abcdefghi')
 
817
        f.close()
 
818
 
 
819
        # write/read should let us do both, but erase anything that's there
 
820
        # already.
 
821
        f = appender.open('w+')
 
822
        self.assertEquals(f.read(), '')
 
823
        f.seek(0, 1) # Don't forget this!
 
824
        f.write('123')
 
825
        f.close()
 
826
 
 
827
        # super append mode should let us read and write and also position the
 
828
        # cursor at the end of the file, without erasing everything.
 
829
        f = appender.open('a+')
 
830
 
 
831
        # The order of these lines may seem surprising, but it is necessary.
 
832
        # The cursor is not at the end of the file until after the first write.
 
833
        f.write('456')
 
834
        f.seek(0, 1) # Asinine.
 
835
        self.assertEquals(f.read(), '')
 
836
 
 
837
        f.seek(0, 0)
 
838
        self.assertEquals(f.read(), '123456')
 
839
        f.close()
 
840
 
 
841
        # Opening a file exclusively must fail if that file exists already.
 
842
        nonexistent.requireCreate(True)
 
843
        nonexistent.open('w').close()
 
844
        existent = nonexistent
 
845
        del nonexistent
 
846
        self.assertRaises((OSError, IOError), existent.open)
 
847
 
 
848
 
 
849
    def test_existsCache(self):
 
850
        """
 
851
        Check that C{filepath.FilePath.exists} correctly restat the object if
 
852
        an operation has occurred in the mean time.
 
853
        """
 
854
        fp = filepath.FilePath(self.mktemp())
 
855
        self.assertEquals(fp.exists(), False)
 
856
 
 
857
        fp.makedirs()
 
858
        self.assertEquals(fp.exists(), True)
 
859
 
 
860
 
 
861
 
 
862
from twisted.python import urlpath
 
863
 
 
864
class URLPathTestCase(unittest.TestCase):
 
865
    def setUp(self):
 
866
        self.path = urlpath.URLPath.fromString("http://example.com/foo/bar?yes=no&no=yes#footer")
 
867
 
 
868
    def testStringConversion(self):
 
869
        self.assertEquals(str(self.path), "http://example.com/foo/bar?yes=no&no=yes#footer")
 
870
 
 
871
    def testChildString(self):
 
872
        self.assertEquals(str(self.path.child('hello')), "http://example.com/foo/bar/hello")
 
873
        self.assertEquals(str(self.path.child('hello').child('')), "http://example.com/foo/bar/hello/")
 
874
 
 
875
    def testSiblingString(self):
 
876
        self.assertEquals(str(self.path.sibling('baz')), 'http://example.com/foo/baz')
 
877
 
 
878
        # The sibling of http://example.com/foo/bar/
 
879
        #     is http://example.comf/foo/bar/baz
 
880
        # because really we are constructing a sibling of
 
881
        # http://example.com/foo/bar/index.html
 
882
        self.assertEquals(str(self.path.child('').sibling('baz')), 'http://example.com/foo/bar/baz')
 
883
 
 
884
    def testParentString(self):
 
885
        # parent should be equivalent to '..'
 
886
        # 'foo' is the current directory, '/' is the parent directory
 
887
        self.assertEquals(str(self.path.parent()), 'http://example.com/')
 
888
        self.assertEquals(str(self.path.child('').parent()), 'http://example.com/foo/')
 
889
        self.assertEquals(str(self.path.child('baz').parent()), 'http://example.com/foo/')
 
890
        self.assertEquals(str(self.path.parent().parent().parent().parent().parent()), 'http://example.com/')
 
891
 
 
892
    def testHereString(self):
 
893
        # here should be equivalent to '.'
 
894
        self.assertEquals(str(self.path.here()), 'http://example.com/foo/')
 
895
        self.assertEquals(str(self.path.child('').here()), 'http://example.com/foo/bar/')
 
896