OLD | NEW |
| (Empty) |
1 # Copyright (c) 2001-2008 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.win32 import WindowsError, ERROR_DIRECTORY | |
11 from twisted.python import filepath | |
12 from twisted.python.zippath import ZipArchive | |
13 from twisted.python.runtime import platform | |
14 | |
15 from twisted.trial import unittest | |
16 | |
17 | |
18 class AbstractFilePathTestCase(unittest.TestCase): | |
19 | |
20 f1content = "file 1" | |
21 f2content = "file 2" | |
22 | |
23 def _mkpath(self, *p): | |
24 x = os.path.abspath(os.path.join(self.cmn, *p)) | |
25 self.all.append(x) | |
26 return x | |
27 | |
28 def subdir(self, *dirname): | |
29 os.mkdir(self._mkpath(*dirname)) | |
30 | |
31 def subfile(self, *dirname): | |
32 return open(self._mkpath(*dirname), "wb") | |
33 | |
34 def setUp(self): | |
35 self.now = time.time() | |
36 cmn = self.cmn = os.path.abspath(self.mktemp()) | |
37 self.all = [cmn] | |
38 os.mkdir(cmn) | |
39 self.subdir("sub1") | |
40 f = self.subfile("file1") | |
41 f.write(self.f1content) | |
42 f = self.subfile("sub1", "file2") | |
43 f.write(self.f2content) | |
44 self.subdir('sub3') | |
45 f = self.subfile("sub3", "file3.ext1") | |
46 f = self.subfile("sub3", "file3.ext2") | |
47 f = self.subfile("sub3", "file3.ext3") | |
48 self.all.sort() | |
49 | |
50 self.path = filepath.FilePath(cmn) | |
51 | |
52 def test_segmentsFromPositive(self): | |
53 """ | |
54 Verify that the segments between two paths are correctly identified. | |
55 """ | |
56 self.assertEquals( | |
57 self.path.child("a").child("b").child("c").segmentsFrom(self.path), | |
58 ["a", "b", "c"]) | |
59 | |
60 def test_segmentsFromNegative(self): | |
61 """Verify that segmentsFrom notices when the ancestor isn't an ancestor. | |
62 """ | |
63 self.assertRaises( | |
64 ValueError, | |
65 self.path.child("a").child("b").child("c").segmentsFrom, | |
66 self.path.child("d").child("c").child("e")) | |
67 | |
68 def test_walk(self): | |
69 """Verify that walking the path gives the same result as the known file | |
70 hierarchy. | |
71 """ | |
72 x = [foo.path for foo in self.path.walk()] | |
73 x.sort() | |
74 self.assertEquals(x, self.all) | |
75 | |
76 def test_validSubdir(self): | |
77 """Verify that a valid subdirectory will show up as a directory, but not
as a | |
78 file, not as a symlink, and be listable. | |
79 """ | |
80 sub1 = self.path.child('sub1') | |
81 self.failUnless(sub1.exists(), | |
82 "This directory does exist.") | |
83 self.failUnless(sub1.isdir(), | |
84 "It's a directory.") | |
85 self.failUnless(not sub1.isfile(), | |
86 "It's a directory.") | |
87 self.failUnless(not sub1.islink(), | |
88 "It's a directory.") | |
89 self.failUnlessEqual(sub1.listdir(), | |
90 ['file2']) | |
91 | |
92 | |
93 def test_invalidSubdir(self): | |
94 """ | |
95 Verify that a subdirectory that doesn't exist is reported as such. | |
96 """ | |
97 sub2 = self.path.child('sub2') | |
98 self.failIf(sub2.exists(), | |
99 "This directory does not exist.") | |
100 | |
101 def test_validFiles(self): | |
102 """ | |
103 Make sure that we can read existent non-empty files. | |
104 """ | |
105 f1 = self.path.child('file1') | |
106 self.failUnlessEqual(f1.open().read(), self.f1content) | |
107 f2 = self.path.child('sub1').child('file2') | |
108 self.failUnlessEqual(f2.open().read(), self.f2content) | |
109 | |
110 | |
111 def test_dictionaryKeys(self): | |
112 """ | |
113 Verify that path instances are usable as dictionary keys. | |
114 """ | |
115 f1 = self.path.child('file1') | |
116 f1prime = self.path.child('file1') | |
117 f2 = self.path.child('file2') | |
118 dictoid = {} | |
119 dictoid[f1] = 3 | |
120 dictoid[f1prime] = 4 | |
121 self.assertEquals(dictoid[f1], 4) | |
122 self.assertEquals(dictoid.keys(), [f1]) | |
123 self.assertIdentical(dictoid.keys()[0], f1) | |
124 self.assertNotIdentical(dictoid.keys()[0], f1prime) # sanity check | |
125 dictoid[f2] = 5 | |
126 self.assertEquals(dictoid[f2], 5) | |
127 self.assertEquals(len(dictoid), 2) | |
128 | |
129 | |
130 def test_dictionaryKeyWithString(self): | |
131 """ | |
132 Verify that path instances are usable as dictionary keys which do not cl
ash | |
133 with their string counterparts. | |
134 """ | |
135 f1 = self.path.child('file1') | |
136 dictoid = {f1: 'hello'} | |
137 dictoid[f1.path] = 'goodbye' | |
138 self.assertEquals(len(dictoid), 2) | |
139 | |
140 | |
141 def test_childrenNonexistentError(self): | |
142 """ | |
143 Verify that children raises the appropriate exception for non-existent | |
144 directories. | |
145 """ | |
146 self.assertRaises(filepath.UnlistableError, | |
147 self.path.child('not real').children) | |
148 | |
149 def test_childrenNotDirectoryError(self): | |
150 """ | |
151 Verify that listdir raises the appropriate exception for attempting to l
ist | |
152 a file rather than a directory. | |
153 """ | |
154 self.assertRaises(filepath.UnlistableError, | |
155 self.path.child('file1').children) | |
156 | |
157 | |
158 def test_newTimesAreFloats(self): | |
159 """ | |
160 Verify that all times returned from the various new time functions are i
nts | |
161 (and hopefully therefore 'high precision'). | |
162 """ | |
163 for p in self.path, self.path.child('file1'): | |
164 self.failUnlessEqual(type(p.getAccessTime()), float) | |
165 self.failUnlessEqual(type(p.getModificationTime()), float) | |
166 self.failUnlessEqual(type(p.getStatusChangeTime()), float) | |
167 | |
168 | |
169 def test_oldTimesAreInts(self): | |
170 """ | |
171 Verify that all times returned from the various time functions are | |
172 integers, for compatibility. | |
173 """ | |
174 for p in self.path, self.path.child('file1'): | |
175 self.failUnlessEqual(type(p.getatime()), int) | |
176 self.failUnlessEqual(type(p.getmtime()), int) | |
177 self.failUnlessEqual(type(p.getctime()), int) | |
178 | |
179 | |
180 | |
181 class FakeWindowsPath(filepath.FilePath): | |
182 """ | |
183 A test version of FilePath which overrides listdir to raise L{WindowsError}. | |
184 """ | |
185 | |
186 def listdir(self): | |
187 """ | |
188 @raise WindowsError: always. | |
189 """ | |
190 raise WindowsError( | |
191 ERROR_DIRECTORY, | |
192 "A directory's validness was called into question") | |
193 | |
194 | |
195 class ListingCompatibilityTests(unittest.TestCase): | |
196 """ | |
197 These tests verify compatibility with legacy behavior of directory listing. | |
198 """ | |
199 | |
200 def test_windowsErrorExcept(self): | |
201 """ | |
202 Verify that when a WindowsError is raised from listdir, catching | |
203 WindowsError works. | |
204 """ | |
205 fwp = FakeWindowsPath(self.mktemp()) | |
206 self.assertRaises(filepath.UnlistableError, fwp.children) | |
207 self.assertRaises(WindowsError, fwp.children) | |
208 | |
209 | |
210 def test_alwaysCatchOSError(self): | |
211 """ | |
212 Verify that in the normal case where a directory does not exist, we will | |
213 get an OSError. | |
214 """ | |
215 fp = filepath.FilePath(self.mktemp()) | |
216 self.assertRaises(OSError, fp.children) | |
217 | |
218 | |
219 def test_keepOriginalAttributes(self): | |
220 """ | |
221 Verify that the Unlistable exception raised will preserve the attributes
of | |
222 the previously-raised exception. | |
223 """ | |
224 fp = filepath.FilePath(self.mktemp()) | |
225 ose = self.assertRaises(OSError, fp.children) | |
226 d1 = ose.__dict__.keys() | |
227 d1.remove('originalException') | |
228 d2 = ose.originalException.__dict__.keys() | |
229 d1.sort() | |
230 d2.sort() | |
231 self.assertEquals(d1, d2) | |
232 | |
233 | |
234 | |
235 def zipit(dirname, zfname): | |
236 """ | |
237 create a zipfile on zfname, containing the contents of dirname' | |
238 """ | |
239 zf = zipfile.ZipFile(zfname, "w") | |
240 basedir = os.path.basename(dirname) | |
241 for root, dirs, files, in os.walk(dirname): | |
242 for fname in files: | |
243 fspath = os.path.join(root, fname) | |
244 arcpath = os.path.join(root, fname)[len(dirname)+1:] | |
245 # print fspath, '=>', arcpath | |
246 zf.write(fspath, arcpath) | |
247 zf.close() | |
248 | |
249 class ZipFilePathTestCase(AbstractFilePathTestCase): | |
250 | |
251 def setUp(self): | |
252 AbstractFilePathTestCase.setUp(self) | |
253 zipit(self.cmn, self.cmn+'.zip') | |
254 self.path = ZipArchive(self.cmn+'.zip') | |
255 self.all = [x.replace(self.cmn, self.cmn+'.zip') for x in self.all] | |
256 | |
257 | |
258 class FilePathTestCase(AbstractFilePathTestCase): | |
259 | |
260 def test_chmod(self): | |
261 """ | |
262 Make sure that calling L{FilePath.chmod} modifies the permissions of | |
263 the passed file as expected (using C{os.stat} to check). We use some | |
264 basic modes that should work everywhere (even on Windows). | |
265 """ | |
266 for mode in (0555, 0777): | |
267 self.path.child("sub1").chmod(mode) | |
268 self.assertEquals( | |
269 stat.S_IMODE(os.stat(self.path.child("sub1").path).st_mode), | |
270 mode) | |
271 | |
272 | |
273 def test_getAndSet(self): | |
274 content = 'newcontent' | |
275 self.path.child('new').setContent(content) | |
276 newcontent = self.path.child('new').getContent() | |
277 self.failUnlessEqual(content, newcontent) | |
278 content = 'content' | |
279 self.path.child('new').setContent(content, '.tmp') | |
280 newcontent = self.path.child('new').getContent() | |
281 self.failUnlessEqual(content, newcontent) | |
282 | |
283 | |
284 def test_symbolicLink(self): | |
285 """ | |
286 Verify the behavior of the C{isLink} method against links and | |
287 non-links. Also check that the symbolic link shares the directory | |
288 property with its target. | |
289 """ | |
290 s4 = self.path.child("sub4") | |
291 s3 = self.path.child("sub3") | |
292 os.symlink(s3.path, s4.path) | |
293 self.assertTrue(s4.islink()) | |
294 self.assertFalse(s3.islink()) | |
295 self.assertTrue(s4.isdir()) | |
296 self.assertTrue(s3.isdir()) | |
297 | |
298 | |
299 def test_linkTo(self): | |
300 """ | |
301 Verify that symlink creates a valid symlink that is both a link and a | |
302 file if its target is a file, or a directory if its target is a | |
303 directory. | |
304 """ | |
305 targetLinks = [ | |
306 (self.path.child("sub2"), self.path.child("sub2.link")), | |
307 (self.path.child("sub2").child("file3.ext1"), | |
308 self.path.child("file3.ext1.link")) | |
309 ] | |
310 for target, link in targetLinks: | |
311 target.linkTo(link) | |
312 self.assertTrue(link.islink(), "This is a link") | |
313 self.assertEquals(target.isdir(), link.isdir()) | |
314 self.assertEquals(target.isfile(), link.isfile()) | |
315 | |
316 | |
317 def test_linkToErrors(self): | |
318 """ | |
319 Verify C{linkTo} fails in the following case: | |
320 - the target is in a directory that doesn't exist | |
321 - the target already exists | |
322 """ | |
323 self.assertRaises(OSError, self.path.child("file1").linkTo, | |
324 self.path.child('nosub').child('file1')) | |
325 self.assertRaises(OSError, self.path.child("file1").linkTo, | |
326 self.path.child('sub1').child('file2')) | |
327 | |
328 | |
329 if not getattr(os, "symlink", None): | |
330 skipMsg = "Your platform does not support symbolic links." | |
331 test_symbolicLink.skip = skipMsg | |
332 test_linkTo.skip = skipMsg | |
333 test_linkToErrors.skip = skipMsg | |
334 | |
335 | |
336 def testMultiExt(self): | |
337 f3 = self.path.child('sub3').child('file3') | |
338 exts = '.foo','.bar', 'ext1','ext2','ext3' | |
339 self.failIf(f3.siblingExtensionSearch(*exts)) | |
340 f3e = f3.siblingExtension(".foo") | |
341 f3e.touch() | |
342 self.failIf(not f3.siblingExtensionSearch(*exts).exists()) | |
343 self.failIf(not f3.siblingExtensionSearch('*').exists()) | |
344 f3e.remove() | |
345 self.failIf(f3.siblingExtensionSearch(*exts)) | |
346 | |
347 def testPreauthChild(self): | |
348 fp = filepath.FilePath('.') | |
349 fp.preauthChild('foo/bar') | |
350 self.assertRaises(filepath.InsecurePath, fp.child, '/foo') | |
351 | |
352 def testStatCache(self): | |
353 p = self.path.child('stattest') | |
354 p.touch() | |
355 self.failUnlessEqual(p.getsize(), 0) | |
356 self.failUnlessEqual(abs(p.getmtime() - time.time()) // 20, 0) | |
357 self.failUnlessEqual(abs(p.getctime() - time.time()) // 20, 0) | |
358 self.failUnlessEqual(abs(p.getatime() - time.time()) // 20, 0) | |
359 self.failUnlessEqual(p.exists(), True) | |
360 self.failUnlessEqual(p.exists(), True) | |
361 # OOB removal: FilePath.remove() will automatically restat | |
362 os.remove(p.path) | |
363 # test caching | |
364 self.failUnlessEqual(p.exists(), True) | |
365 p.restat(reraise=False) | |
366 self.failUnlessEqual(p.exists(), False) | |
367 self.failUnlessEqual(p.islink(), False) | |
368 self.failUnlessEqual(p.isdir(), False) | |
369 self.failUnlessEqual(p.isfile(), False) | |
370 | |
371 def testPersist(self): | |
372 newpath = pickle.loads(pickle.dumps(self.path)) | |
373 self.failUnlessEqual(self.path.__class__, newpath.__class__) | |
374 self.failUnlessEqual(self.path.path, newpath.path) | |
375 | |
376 def testInsecureUNIX(self): | |
377 self.assertRaises(filepath.InsecurePath, self.path.child, "..") | |
378 self.assertRaises(filepath.InsecurePath, self.path.child, "/etc") | |
379 self.assertRaises(filepath.InsecurePath, self.path.child, "../..") | |
380 | |
381 def testInsecureWin32(self): | |
382 self.assertRaises(filepath.InsecurePath, self.path.child, r"..\..") | |
383 self.assertRaises(filepath.InsecurePath, self.path.child, r"C:randomfile
") | |
384 | |
385 if platform.getType() != 'win32': | |
386 testInsecureWin32.skip = "Consider yourself lucky." | |
387 | |
388 def testInsecureWin32Whacky(self): | |
389 """Windows has 'special' filenames like NUL and CON and COM1 and LPR | |
390 and PRN and ... god knows what else. They can be located anywhere in | |
391 the filesystem. For obvious reasons, we do not wish to normally permit | |
392 access to these. | |
393 """ | |
394 self.assertRaises(filepath.InsecurePath, self.path.child, "CON") | |
395 self.assertRaises(filepath.InsecurePath, self.path.child, "C:CON") | |
396 self.assertRaises(filepath.InsecurePath, self.path.child, r"C:\CON") | |
397 | |
398 if platform.getType() != 'win32': | |
399 testInsecureWin32Whacky.skip = "Consider yourself lucky." | |
400 | |
401 def testComparison(self): | |
402 self.assertEquals(filepath.FilePath('a'), | |
403 filepath.FilePath('a')) | |
404 self.failUnless(filepath.FilePath('z') > | |
405 filepath.FilePath('a')) | |
406 self.failUnless(filepath.FilePath('z') >= | |
407 filepath.FilePath('a')) | |
408 self.failUnless(filepath.FilePath('a') >= | |
409 filepath.FilePath('a')) | |
410 self.failUnless(filepath.FilePath('a') <= | |
411 filepath.FilePath('a')) | |
412 self.failUnless(filepath.FilePath('a') < | |
413 filepath.FilePath('z')) | |
414 self.failUnless(filepath.FilePath('a') <= | |
415 filepath.FilePath('z')) | |
416 self.failUnless(filepath.FilePath('a') != | |
417 filepath.FilePath('z')) | |
418 self.failUnless(filepath.FilePath('z') != | |
419 filepath.FilePath('a')) | |
420 | |
421 self.failIf(filepath.FilePath('z') != | |
422 filepath.FilePath('z')) | |
423 | |
424 def testSibling(self): | |
425 p = self.path.child('sibling_start') | |
426 ts = p.sibling('sibling_test') | |
427 self.assertEquals(ts.dirname(), p.dirname()) | |
428 self.assertEquals(ts.basename(), 'sibling_test') | |
429 ts.createDirectory() | |
430 self.assertIn(ts, self.path.children()) | |
431 | |
432 def testTemporarySibling(self): | |
433 ts = self.path.temporarySibling() | |
434 self.assertEquals(ts.dirname(), self.path.dirname()) | |
435 self.assertNotIn(ts.basename(), self.path.listdir()) | |
436 ts.createDirectory() | |
437 self.assertIn(ts, self.path.parent().children()) | |
438 | |
439 def testRemove(self): | |
440 self.path.remove() | |
441 self.failIf(self.path.exists()) | |
442 | |
443 | |
444 def test_removeWithSymlink(self): | |
445 """ | |
446 For a path which is a symbolic link, L{FilePath.remove} just deletes | |
447 the link, not the target. | |
448 """ | |
449 link = self.path.child("sub1.link") | |
450 # setUp creates the sub1 child | |
451 os.symlink(self.path.child("sub1").path, link.path) | |
452 link.remove() | |
453 self.assertFalse(link.exists()) | |
454 self.assertTrue(self.path.child("sub1").exists()) | |
455 | |
456 if getattr(os, 'symlink', None) is None: | |
457 test_removeWithSymlink.skip = "Platform doesn't support symbolic links" | |
458 | |
459 | |
460 def testCopyTo(self): | |
461 self.assertRaises((OSError, IOError), self.path.copyTo, self.path.child(
'file1')) | |
462 oldPaths = list(self.path.walk()) # Record initial state | |
463 fp = filepath.FilePath(self.mktemp()) | |
464 self.path.copyTo(fp) | |
465 self.path.remove() | |
466 fp.copyTo(self.path) | |
467 newPaths = list(self.path.walk()) # Record double-copy state | |
468 newPaths.sort() | |
469 oldPaths.sort() | |
470 self.assertEquals(newPaths, oldPaths) | |
471 | |
472 def testMoveTo(self): | |
473 self.assertRaises((OSError, IOError), self.path.moveTo, self.path.child(
'file1')) | |
474 oldPaths = list(self.path.walk()) # Record initial state | |
475 fp = filepath.FilePath(self.mktemp()) | |
476 self.path.moveTo(fp) | |
477 fp.moveTo(self.path) | |
478 newPaths = list(self.path.walk()) # Record double-move state | |
479 newPaths.sort() | |
480 oldPaths.sort() | |
481 self.assertEquals(newPaths, oldPaths) | |
482 | |
483 | |
484 def test_crossMountMoveTo(self): | |
485 """ | |
486 C{moveTo} should be able to handle C{EXDEV} error raised by | |
487 C{os.rename} when trying to move a file on a different mounted | |
488 filesystem. | |
489 """ | |
490 # Bit of a whitebox test - force os.rename, which moveTo tries | |
491 # before falling back to a slower method, to fail, forcing moveTo to | |
492 # use the slower behavior. | |
493 invokedWith = [] | |
494 def faultyRename(src, dest): | |
495 invokedWith.append((src, dest)) | |
496 if len(invokedWith) == 2: | |
497 raise OSError(errno.EXDEV, 'Test-induced failure simulating cros
s-device rename failure') | |
498 return originalRename(src, dest) | |
499 | |
500 originalRename = os.rename | |
501 os.rename = faultyRename | |
502 try: | |
503 self.testMoveTo() | |
504 # A bit of a sanity check for this whitebox test - if our rename | |
505 # was never invoked, the test has probably fallen into | |
506 # disrepair! | |
507 self.failUnless(len(invokedWith) >= 2) | |
508 finally: | |
509 os.rename = originalRename | |
510 | |
511 | |
512 def testOpen(self): | |
513 # Opening a file for reading when it does not already exist is an error | |
514 nonexistent = self.path.child('nonexistent') | |
515 e = self.assertRaises(IOError, nonexistent.open) | |
516 self.assertEquals(e.errno, errno.ENOENT) | |
517 | |
518 # Opening a file for writing when it does not exist is okay | |
519 writer = self.path.child('writer') | |
520 f = writer.open('w') | |
521 f.write('abc\ndef') | |
522 f.close() | |
523 | |
524 # Make sure those bytes ended up there - and test opening a file for | |
525 # reading when it does exist at the same time | |
526 f = writer.open() | |
527 self.assertEquals(f.read(), 'abc\ndef') | |
528 f.close() | |
529 | |
530 # Re-opening that file in write mode should erase whatever was there. | |
531 f = writer.open('w') | |
532 f.close() | |
533 f = writer.open() | |
534 self.assertEquals(f.read(), '') | |
535 f.close() | |
536 | |
537 # Put some bytes in a file so we can test that appending does not | |
538 # destroy them. | |
539 appender = self.path.child('appender') | |
540 f = appender.open('w') | |
541 f.write('abc') | |
542 f.close() | |
543 | |
544 f = appender.open('a') | |
545 f.write('def') | |
546 f.close() | |
547 | |
548 f = appender.open('r') | |
549 self.assertEquals(f.read(), 'abcdef') | |
550 f.close() | |
551 | |
552 # read/write should let us do both without erasing those bytes | |
553 f = appender.open('r+') | |
554 self.assertEquals(f.read(), 'abcdef') | |
555 # ANSI C *requires* an fseek or an fgetpos between an fread and an | |
556 # fwrite or an fwrite and a fread. We can't reliable get Python to | |
557 # invoke fgetpos, so we seek to a 0 byte offset from the current | |
558 # position instead. Also, Python sucks for making this seek | |
559 # relative to 1 instead of a symbolic constant representing the | |
560 # current file position. | |
561 f.seek(0, 1) | |
562 # Put in some new bytes for us to test for later. | |
563 f.write('ghi') | |
564 f.close() | |
565 | |
566 # Make sure those new bytes really showed up | |
567 f = appender.open('r') | |
568 self.assertEquals(f.read(), 'abcdefghi') | |
569 f.close() | |
570 | |
571 # write/read should let us do both, but erase anything that's there | |
572 # already. | |
573 f = appender.open('w+') | |
574 self.assertEquals(f.read(), '') | |
575 f.seek(0, 1) # Don't forget this! | |
576 f.write('123') | |
577 f.close() | |
578 | |
579 # super append mode should let us read and write and also position the | |
580 # cursor at the end of the file, without erasing everything. | |
581 f = appender.open('a+') | |
582 | |
583 # The order of these lines may seem surprising, but it is necessary. | |
584 # The cursor is not at the end of the file until after the first write. | |
585 f.write('456') | |
586 f.seek(0, 1) # Asinine. | |
587 self.assertEquals(f.read(), '') | |
588 | |
589 f.seek(0, 0) | |
590 self.assertEquals(f.read(), '123456') | |
591 f.close() | |
592 | |
593 # Opening a file exclusively must fail if that file exists already. | |
594 nonexistent.requireCreate(True) | |
595 nonexistent.open('w').close() | |
596 existent = nonexistent | |
597 del nonexistent | |
598 self.assertRaises((OSError, IOError), existent.open) | |
599 | |
600 | |
601 def test_existsCache(self): | |
602 """ | |
603 Check that C{filepath.FilePath.exists} correctly restat the object if | |
604 an operation has occurred in the mean time. | |
605 """ | |
606 fp = filepath.FilePath(self.mktemp()) | |
607 self.assertEquals(fp.exists(), False) | |
608 | |
609 fp.makedirs() | |
610 self.assertEquals(fp.exists(), True) | |
611 | |
612 | |
613 | |
614 from twisted.python import urlpath | |
615 | |
616 class URLPathTestCase(unittest.TestCase): | |
617 def setUp(self): | |
618 self.path = urlpath.URLPath.fromString("http://example.com/foo/bar?yes=n
o&no=yes#footer") | |
619 | |
620 def testStringConversion(self): | |
621 self.assertEquals(str(self.path), "http://example.com/foo/bar?yes=no&no=
yes#footer") | |
622 | |
623 def testChildString(self): | |
624 self.assertEquals(str(self.path.child('hello')), "http://example.com/foo
/bar/hello") | |
625 self.assertEquals(str(self.path.child('hello').child('')), "http://examp
le.com/foo/bar/hello/") | |
626 | |
627 def testSiblingString(self): | |
628 self.assertEquals(str(self.path.sibling('baz')), 'http://example.com/foo
/baz') | |
629 | |
630 # The sibling of http://example.com/foo/bar/ | |
631 # is http://example.comf/foo/bar/baz | |
632 # because really we are constructing a sibling of | |
633 # http://example.com/foo/bar/index.html | |
634 self.assertEquals(str(self.path.child('').sibling('baz')), 'http://examp
le.com/foo/bar/baz') | |
635 | |
636 def testParentString(self): | |
637 # parent should be equivalent to '..' | |
638 # 'foo' is the current directory, '/' is the parent directory | |
639 self.assertEquals(str(self.path.parent()), 'http://example.com/') | |
640 self.assertEquals(str(self.path.child('').parent()), 'http://example.com
/foo/') | |
641 self.assertEquals(str(self.path.child('baz').parent()), 'http://example.
com/foo/') | |
642 self.assertEquals(str(self.path.parent().parent().parent().parent().pare
nt()), 'http://example.com/') | |
643 | |
644 def testHereString(self): | |
645 # here should be equivalent to '.' | |
646 self.assertEquals(str(self.path.here()), 'http://example.com/foo/') | |
647 self.assertEquals(str(self.path.child('').here()), 'http://example.com/f
oo/bar/') | |
648 | |
OLD | NEW |