2 def foo(): pass # keep the line number constant
5 from twisted.trial import unittest
6 from twisted.internet import defer, reactor
7 from twisted.python import failure
9 from allmydata.util import base32, idlib, humanreadable, mathutil, hashutil
10 from allmydata.util import assertutil, fileutil, deferredutil, abbreviate
11 from allmydata.util import limiter, time_format, pollmixin, cachedir
13 class Base32(unittest.TestCase):
14 def test_b2a_matches_Pythons(self):
16 y = "\x12\x34\x45\x67\x89\x0a\xbc\xde\xf0"
17 x = base64.b32encode(y)
18 while x and x[-1] == '=':
21 self.failUnlessEqual(base32.b2a(y), x)
23 self.failUnlessEqual(base32.b2a("\x12\x34"), "ci2a")
24 def test_b2a_or_none(self):
25 self.failUnlessEqual(base32.b2a_or_none(None), None)
26 self.failUnlessEqual(base32.b2a_or_none("\x12\x34"), "ci2a")
28 self.failUnlessEqual(base32.a2b("ci2a"), "\x12\x34")
29 self.failUnlessRaises(AssertionError, base32.a2b, "b0gus")
31 class IDLib(unittest.TestCase):
32 def test_nodeid_b2a(self):
33 self.failUnlessEqual(idlib.nodeid_b2a("\x00"*20), "a"*32)
35 class NoArgumentException(Exception):
39 class HumanReadable(unittest.TestCase):
42 self.failUnlessEqual(hr(foo), "<foo() at test_util.py:2>")
43 self.failUnlessEqual(hr(self.test_repr),
44 "<bound method HumanReadable.test_repr of <allmydata.test.test_util.HumanReadable testMethod=test_repr>>")
45 self.failUnlessEqual(hr(1L), "1")
46 self.failUnlessEqual(hr(10**40),
47 "100000000000000000...000000000000000000")
48 self.failUnlessEqual(hr(self), "<allmydata.test.test_util.HumanReadable testMethod=test_repr>")
49 self.failUnlessEqual(hr([1,2]), "[1, 2]")
50 self.failUnlessEqual(hr({1:2}), "{1:2}")
55 hr(e) == "<RuntimeError: ()>" # python-2.4
56 or hr(e) == "RuntimeError()") # python-2.5
58 raise RuntimeError("oops")
61 hr(e) == "<RuntimeError: 'oops'>" # python-2.4
62 or hr(e) == "RuntimeError('oops',)") # python-2.5
64 raise NoArgumentException
67 hr(e) == "<NoArgumentException>" # python-2.4
68 or hr(e) == "NoArgumentException()") # python-2.5
74 class Math(unittest.TestCase):
75 def test_div_ceil(self):
77 self.failUnlessEqual(f(0, 1), 0)
78 self.failUnlessEqual(f(0, 2), 0)
79 self.failUnlessEqual(f(0, 3), 0)
80 self.failUnlessEqual(f(1, 3), 1)
81 self.failUnlessEqual(f(2, 3), 1)
82 self.failUnlessEqual(f(3, 3), 1)
83 self.failUnlessEqual(f(4, 3), 2)
84 self.failUnlessEqual(f(5, 3), 2)
85 self.failUnlessEqual(f(6, 3), 2)
86 self.failUnlessEqual(f(7, 3), 3)
88 def test_next_multiple(self):
89 f = mathutil.next_multiple
90 self.failUnlessEqual(f(5, 1), 5)
91 self.failUnlessEqual(f(5, 2), 6)
92 self.failUnlessEqual(f(5, 3), 6)
93 self.failUnlessEqual(f(5, 4), 8)
94 self.failUnlessEqual(f(5, 5), 5)
95 self.failUnlessEqual(f(5, 6), 6)
96 self.failUnlessEqual(f(32, 1), 32)
97 self.failUnlessEqual(f(32, 2), 32)
98 self.failUnlessEqual(f(32, 3), 33)
99 self.failUnlessEqual(f(32, 4), 32)
100 self.failUnlessEqual(f(32, 5), 35)
101 self.failUnlessEqual(f(32, 6), 36)
102 self.failUnlessEqual(f(32, 7), 35)
103 self.failUnlessEqual(f(32, 8), 32)
104 self.failUnlessEqual(f(32, 9), 36)
105 self.failUnlessEqual(f(32, 10), 40)
106 self.failUnlessEqual(f(32, 11), 33)
107 self.failUnlessEqual(f(32, 12), 36)
108 self.failUnlessEqual(f(32, 13), 39)
109 self.failUnlessEqual(f(32, 14), 42)
110 self.failUnlessEqual(f(32, 15), 45)
111 self.failUnlessEqual(f(32, 16), 32)
112 self.failUnlessEqual(f(32, 17), 34)
113 self.failUnlessEqual(f(32, 18), 36)
114 self.failUnlessEqual(f(32, 589), 589)
116 def test_pad_size(self):
117 f = mathutil.pad_size
118 self.failUnlessEqual(f(0, 4), 0)
119 self.failUnlessEqual(f(1, 4), 3)
120 self.failUnlessEqual(f(2, 4), 2)
121 self.failUnlessEqual(f(3, 4), 1)
122 self.failUnlessEqual(f(4, 4), 0)
123 self.failUnlessEqual(f(5, 4), 3)
125 def test_is_power_of_k(self):
126 f = mathutil.is_power_of_k
127 for i in range(1, 100):
128 if i in (1, 2, 4, 8, 16, 32, 64):
129 self.failUnless(f(i, 2), "but %d *is* a power of 2" % i)
131 self.failIf(f(i, 2), "but %d is *not* a power of 2" % i)
132 for i in range(1, 100):
133 if i in (1, 3, 9, 27, 81):
134 self.failUnless(f(i, 3), "but %d *is* a power of 3" % i)
136 self.failIf(f(i, 3), "but %d is *not* a power of 3" % i)
138 def test_next_power_of_k(self):
139 f = mathutil.next_power_of_k
140 self.failUnlessEqual(f(0,2), 1)
141 self.failUnlessEqual(f(1,2), 1)
142 self.failUnlessEqual(f(2,2), 2)
143 self.failUnlessEqual(f(3,2), 4)
144 self.failUnlessEqual(f(4,2), 4)
145 for i in range(5, 8): self.failUnlessEqual(f(i,2), 8, "%d" % i)
146 for i in range(9, 16): self.failUnlessEqual(f(i,2), 16, "%d" % i)
147 for i in range(17, 32): self.failUnlessEqual(f(i,2), 32, "%d" % i)
148 for i in range(33, 64): self.failUnlessEqual(f(i,2), 64, "%d" % i)
149 for i in range(65, 100): self.failUnlessEqual(f(i,2), 128, "%d" % i)
151 self.failUnlessEqual(f(0,3), 1)
152 self.failUnlessEqual(f(1,3), 1)
153 self.failUnlessEqual(f(2,3), 3)
154 self.failUnlessEqual(f(3,3), 3)
155 for i in range(4, 9): self.failUnlessEqual(f(i,3), 9, "%d" % i)
156 for i in range(10, 27): self.failUnlessEqual(f(i,3), 27, "%d" % i)
157 for i in range(28, 81): self.failUnlessEqual(f(i,3), 81, "%d" % i)
158 for i in range(82, 200): self.failUnlessEqual(f(i,3), 243, "%d" % i)
162 self.failUnlessEqual(f([1,2,3]), 2)
163 self.failUnlessEqual(f([0,0,0,4]), 1)
164 self.failUnlessAlmostEqual(f([0.0, 1.0, 1.0]), .666666666666)
167 class Asserts(unittest.TestCase):
168 def should_assert(self, func, *args, **kwargs):
170 func(*args, **kwargs)
171 except AssertionError, e:
174 self.fail("assert failed with non-AssertionError: %s" % e)
175 self.fail("assert was not caught")
177 def should_not_assert(self, func, *args, **kwargs):
179 regexp = kwargs["re"]
182 func(*args, **kwargs)
183 except AssertionError, e:
184 self.fail("assertion fired when it should not have: %s" % e)
186 self.fail("assertion (which shouldn't have failed) failed with non-AssertionError: %s" % e)
190 def test_assert(self):
191 f = assertutil._assert
192 self.should_assert(f)
193 self.should_assert(f, False)
194 self.should_not_assert(f, True)
196 m = self.should_assert(f, False, "message")
197 self.failUnlessEqual(m, "'message' <type 'str'>", m)
198 m = self.should_assert(f, False, "message1", othermsg=12)
199 self.failUnlessEqual("'message1' <type 'str'>, othermsg: 12 <type 'int'>", m)
200 m = self.should_assert(f, False, othermsg="message2")
201 self.failUnlessEqual("othermsg: 'message2' <type 'str'>", m)
203 def test_precondition(self):
204 f = assertutil.precondition
205 self.should_assert(f)
206 self.should_assert(f, False)
207 self.should_not_assert(f, True)
209 m = self.should_assert(f, False, "message")
210 self.failUnlessEqual("precondition: 'message' <type 'str'>", m)
211 m = self.should_assert(f, False, "message1", othermsg=12)
212 self.failUnlessEqual("precondition: 'message1' <type 'str'>, othermsg: 12 <type 'int'>", m)
213 m = self.should_assert(f, False, othermsg="message2")
214 self.failUnlessEqual("precondition: othermsg: 'message2' <type 'str'>", m)
216 def test_postcondition(self):
217 f = assertutil.postcondition
218 self.should_assert(f)
219 self.should_assert(f, False)
220 self.should_not_assert(f, True)
222 m = self.should_assert(f, False, "message")
223 self.failUnlessEqual("postcondition: 'message' <type 'str'>", m)
224 m = self.should_assert(f, False, "message1", othermsg=12)
225 self.failUnlessEqual("postcondition: 'message1' <type 'str'>, othermsg: 12 <type 'int'>", m)
226 m = self.should_assert(f, False, othermsg="message2")
227 self.failUnlessEqual("postcondition: othermsg: 'message2' <type 'str'>", m)
229 class FileUtil(unittest.TestCase):
230 def mkdir(self, basedir, path, mode=0777):
231 fn = os.path.join(basedir, path)
232 fileutil.make_dirs(fn, mode)
234 def touch(self, basedir, path, mode=None, data="touch\n"):
235 fn = os.path.join(basedir, path)
242 def test_rm_dir(self):
243 basedir = "util/FileUtil/test_rm_dir"
244 fileutil.make_dirs(basedir)
245 # create it again to test idempotency
246 fileutil.make_dirs(basedir)
247 d = os.path.join(basedir, "doomed")
249 self.touch(d, "a/b/1.txt")
250 self.touch(d, "a/b/2.txt", 0444)
251 self.touch(d, "a/b/3.txt", 0)
253 self.touch(d, "a/c/1.txt")
254 self.touch(d, "a/c/2.txt", 0444)
255 self.touch(d, "a/c/3.txt", 0)
256 os.chmod(os.path.join(d, "a/c"), 0444)
258 self.touch(d, "a/d/1.txt")
259 self.touch(d, "a/d/2.txt", 0444)
260 self.touch(d, "a/d/3.txt", 0)
261 os.chmod(os.path.join(d, "a/d"), 0)
264 self.failIf(os.path.exists(d))
265 # remove it again to test idempotency
268 def test_remove_if_possible(self):
269 basedir = "util/FileUtil/test_remove_if_possible"
270 fileutil.make_dirs(basedir)
271 self.touch(basedir, "here")
272 fn = os.path.join(basedir, "here")
273 fileutil.remove_if_possible(fn)
274 self.failIf(os.path.exists(fn))
275 fileutil.remove_if_possible(fn) # should be idempotent
276 fileutil.rm_dir(basedir)
277 fileutil.remove_if_possible(fn) # should survive errors
279 def test_open_or_create(self):
280 basedir = "util/FileUtil/test_open_or_create"
281 fileutil.make_dirs(basedir)
282 fn = os.path.join(basedir, "here")
283 f = fileutil.open_or_create(fn)
286 f = fileutil.open_or_create(fn)
293 self.failUnlessEqual(data, "stuff.more.")
295 def test_NamedTemporaryDirectory(self):
296 basedir = "util/FileUtil/test_NamedTemporaryDirectory"
297 fileutil.make_dirs(basedir)
298 td = fileutil.NamedTemporaryDirectory(dir=basedir)
300 self.failUnless(basedir in name)
301 self.failUnless(basedir in repr(td))
302 self.failUnless(os.path.isdir(name))
304 # it is conceivable that we need to force gc here, but I'm not sure
305 self.failIf(os.path.isdir(name))
307 def test_rename(self):
308 basedir = "util/FileUtil/test_rename"
309 fileutil.make_dirs(basedir)
310 self.touch(basedir, "here")
311 fn = os.path.join(basedir, "here")
312 fn2 = os.path.join(basedir, "there")
313 fileutil.rename(fn, fn2)
314 self.failIf(os.path.exists(fn))
315 self.failUnless(os.path.exists(fn2))
318 basedir = "util/FileUtil/test_du"
319 fileutil.make_dirs(basedir)
320 d = os.path.join(basedir, "space-consuming")
322 self.touch(d, "a/b/1.txt", data="a"*10)
323 self.touch(d, "a/b/2.txt", data="b"*11)
325 self.touch(d, "a/c/1.txt", data="c"*12)
326 self.touch(d, "a/c/2.txt", data="d"*13)
328 used = fileutil.du(basedir)
329 self.failUnlessEqual(10+11+12+13, used)
331 class PollMixinTests(unittest.TestCase):
333 self.pm = pollmixin.PollMixin()
335 def test_PollMixin_True(self):
336 d = self.pm.poll(check_f=lambda : True,
340 def test_PollMixin_False_then_True(self):
341 i = iter([False, True])
342 d = self.pm.poll(check_f=i.next,
346 def test_timeout(self):
347 d = self.pm.poll(check_f=lambda: False,
351 self.fail("poll should have failed, not returned %s" % (res,))
353 f.trap(pollmixin.TimeoutError)
354 return None # success
355 d.addCallbacks(_suc, _err)
358 class DeferredUtilTests(unittest.TestCase):
359 def test_success(self):
360 d1, d2 = defer.Deferred(), defer.Deferred()
363 dlss = deferredutil.DeferredListShouldSucceed([d1,d2])
364 dlss.addCallbacks(good.append, bad.append)
367 self.failUnlessEqual(good, [[1,2]])
368 self.failUnlessEqual(bad, [])
370 def test_failure(self):
371 d1, d2 = defer.Deferred(), defer.Deferred()
374 dlss = deferredutil.DeferredListShouldSucceed([d1,d2])
375 dlss.addCallbacks(good.append, bad.append)
376 d1.addErrback(lambda _ignore: None)
377 d2.addErrback(lambda _ignore: None)
379 d2.errback(RuntimeError())
380 self.failUnlessEqual(good, [])
381 self.failUnlessEqual(len(bad), 1)
383 self.failUnless(isinstance(f, failure.Failure))
384 self.failUnless(f.check(RuntimeError))
386 class HashUtilTests(unittest.TestCase):
388 def test_random_key(self):
389 k = hashutil.random_key()
390 self.failUnlessEqual(len(k), hashutil.KEYLEN)
392 def test_sha256d(self):
393 h1 = hashutil.tagged_hash("tag1", "value")
394 h2 = hashutil.tagged_hasher("tag1")
398 self.failUnlessEqual(h1, h2a)
399 self.failUnlessEqual(h2a, h2b)
401 def test_sha256d_truncated(self):
402 h1 = hashutil.tagged_hash("tag1", "value", 16)
403 h2 = hashutil.tagged_hasher("tag1", 16)
406 self.failUnlessEqual(len(h1), 16)
407 self.failUnlessEqual(len(h2), 16)
408 self.failUnlessEqual(h1, h2)
411 h1 = hashutil.convergence_hash(3, 10, 1000, "data", "secret")
412 h2 = hashutil.convergence_hasher(3, 10, 1000, "secret")
415 self.failUnlessEqual(h1, h2)
417 def test_hashers(self):
418 h1 = hashutil.block_hash("foo")
419 h2 = hashutil.block_hasher()
421 self.failUnlessEqual(h1, h2.digest())
423 h1 = hashutil.uri_extension_hash("foo")
424 h2 = hashutil.uri_extension_hasher()
426 self.failUnlessEqual(h1, h2.digest())
428 h1 = hashutil.plaintext_hash("foo")
429 h2 = hashutil.plaintext_hasher()
431 self.failUnlessEqual(h1, h2.digest())
433 h1 = hashutil.crypttext_hash("foo")
434 h2 = hashutil.crypttext_hasher()
436 self.failUnlessEqual(h1, h2.digest())
438 h1 = hashutil.crypttext_segment_hash("foo")
439 h2 = hashutil.crypttext_segment_hasher()
441 self.failUnlessEqual(h1, h2.digest())
443 h1 = hashutil.plaintext_segment_hash("foo")
444 h2 = hashutil.plaintext_segment_hasher()
446 self.failUnlessEqual(h1, h2.digest())
448 class Abbreviate(unittest.TestCase):
450 a = abbreviate.abbreviate_time
451 self.failUnlessEqual(a(None), "unknown")
452 self.failUnlessEqual(a(0), "0 seconds")
453 self.failUnlessEqual(a(1), "1 second")
454 self.failUnlessEqual(a(2), "2 seconds")
455 self.failUnlessEqual(a(119), "119 seconds")
457 self.failUnlessEqual(a(2*MIN), "2 minutes")
458 self.failUnlessEqual(a(60*MIN), "60 minutes")
459 self.failUnlessEqual(a(179*MIN), "179 minutes")
461 self.failUnlessEqual(a(180*MIN), "3 hours")
462 self.failUnlessEqual(a(4*HOUR), "4 hours")
465 self.failUnlessEqual(a(2*DAY), "2 days")
466 self.failUnlessEqual(a(2*MONTH), "2 months")
468 self.failUnlessEqual(a(5*YEAR), "5 years")
470 def test_space(self):
471 tests_si = [(None, "unknown"),
478 (20*1000, "20.00 kB"),
479 (1024*1024, "1.05 MB"),
480 (1000*1000, "1.00 MB"),
481 (1000*1000*1000, "1.00 GB"),
482 (1000*1000*1000*1000, "1.00 TB"),
483 (1000*1000*1000*1000*1000, "1.00 PB"),
484 (1234567890123456, "1.23 PB"),
486 for (x, expected) in tests_si:
487 got = abbreviate.abbreviate_space(x, SI=True)
488 self.failUnlessEqual(got, expected)
490 tests_base1024 = [(None, "unknown"),
497 (20*1024, "20.00 kiB"),
498 (1000*1000, "976.56 kiB"),
499 (1024*1024, "1.00 MiB"),
500 (1024*1024*1024, "1.00 GiB"),
501 (1024*1024*1024*1024, "1.00 TiB"),
502 (1000*1000*1000*1000*1000, "909.49 TiB"),
503 (1024*1024*1024*1024*1024, "1.00 PiB"),
504 (1234567890123456, "1.10 PiB"),
506 for (x, expected) in tests_base1024:
507 got = abbreviate.abbreviate_space(x, SI=False)
508 self.failUnlessEqual(got, expected)
510 self.failUnlessEqual(abbreviate.abbreviate_space_both(1234567),
511 "(1.23 MB, 1.18 MiB)")
513 def test_parse_space(self):
514 p = abbreviate.parse_abbreviated_size
515 self.failUnlessEqual(p(""), None)
516 self.failUnlessEqual(p(None), None)
517 self.failUnlessEqual(p("123"), 123)
518 self.failUnlessEqual(p("123B"), 123)
519 self.failUnlessEqual(p("2K"), 2000)
520 self.failUnlessEqual(p("2kb"), 2000)
521 self.failUnlessEqual(p("2KiB"), 2048)
522 self.failUnlessEqual(p("10MB"), 10*1000*1000)
523 self.failUnlessEqual(p("10MiB"), 10*1024*1024)
524 self.failUnlessEqual(p("5G"), 5*1000*1000*1000)
525 self.failUnlessEqual(p("4GiB"), 4*1024*1024*1024)
526 e = self.failUnlessRaises(ValueError, p, "12 cubits")
527 self.failUnless("12 cubits" in str(e))
529 class Limiter(unittest.TestCase):
530 def job(self, i, foo):
531 self.calls.append( (i, foo) )
532 self.simultaneous += 1
533 self.peak_simultaneous = max(self.simultaneous, self.peak_simultaneous)
536 self.simultaneous -= 1
537 d.callback("done %d" % i)
538 reactor.callLater(1.0, _done)
541 def bad_job(self, i, foo):
542 raise RuntimeError("bad_job %d" % i)
544 def test_limiter(self):
546 self.simultaneous = 0
547 self.peak_simultaneous = 0
548 l = limiter.ConcurrencyLimiter()
551 dl.append(l.add(self.job, i, foo=str(i)))
552 d = defer.DeferredList(dl, fireOnOneErrback=True)
554 self.failUnlessEqual(self.simultaneous, 0)
555 self.failUnless(self.peak_simultaneous <= 10)
556 self.failUnlessEqual(len(self.calls), 20)
558 self.failUnless( (i, str(i)) in self.calls)
562 def test_errors(self):
564 self.simultaneous = 0
565 self.peak_simultaneous = 0
566 l = limiter.ConcurrencyLimiter()
569 dl.append(l.add(self.job, i, foo=str(i)))
570 d2 = l.add(self.bad_job, 21, "21")
571 d = defer.DeferredList(dl, fireOnOneErrback=True)
574 for (success, result) in res:
575 self.failUnlessEqual(success, True)
576 results.append(result)
578 expected_results = ["done %d" % i for i in range(20)]
579 expected_results.sort()
580 self.failUnlessEqual(results, expected_results)
581 self.failUnless(self.peak_simultaneous <= 10)
582 self.failUnlessEqual(len(self.calls), 20)
584 self.failUnless( (i, str(i)) in self.calls)
586 self.fail("should have failed, not got %s" % (res,))
589 self.failUnless("bad_job 21" in str(f))
590 d2.addCallbacks(_good, _err)
592 d.addCallback(_most_done)
594 self.failUnlessEqual(self.simultaneous, 0)
595 self.failUnless(self.peak_simultaneous <= 10)
596 self.failUnlessEqual(len(self.calls), 20)
598 self.failUnless( (i, str(i)) in self.calls)
599 d.addCallback(_all_done)
602 class TimeFormat(unittest.TestCase):
603 def test_epoch(self):
604 s = time_format.iso_utc_time_to_localseconds("1970-01-01T00:00:01")
605 self.failUnlessEqual(s, 1.0)
606 s = time_format.iso_utc_time_to_localseconds("1970-01-01_00:00:01")
607 self.failUnlessEqual(s, 1.0)
608 s = time_format.iso_utc_time_to_localseconds("1970-01-01 00:00:01")
609 self.failUnlessEqual(s, 1.0)
611 self.failUnlessEqual(time_format.iso_utc(1.0), "1970-01-01_00:00:01")
612 self.failUnlessEqual(time_format.iso_utc(1.0, sep=" "),
613 "1970-01-01 00:00:01")
617 self.failUnlessEqual(time_format.iso_utc(t=my_time),
618 "1970-01-01_00:00:01")
619 e = self.failUnlessRaises(ValueError,
620 time_format.iso_utc_time_to_localseconds,
621 "invalid timestring")
622 self.failUnless("not a complete ISO8601 timestamp" in str(e))
623 s = time_format.iso_utc_time_to_localseconds("1970-01-01_00:00:01.500")
624 self.failUnlessEqual(s, 1.5)
626 class CacheDir(unittest.TestCase):
627 def test_basic(self):
628 basedir = "test_util/CacheDir/test_basic"
630 def _failIfExists(name):
631 absfn = os.path.join(basedir, name)
632 self.failIf(os.path.exists(absfn),
633 "%s exists but it shouldn't" % absfn)
635 def _failUnlessExists(name):
636 absfn = os.path.join(basedir, name)
637 self.failUnless(os.path.exists(absfn),
638 "%s doesn't exist but it should" % absfn)
640 cdm = cachedir.CacheDirectoryManager(basedir)
641 a = cdm.get_file("a")
642 b = cdm.get_file("b")
643 c = cdm.get_file("c")
644 f = open(a.get_filename(), "wb"); f.write("hi"); f.close(); del f
645 f = open(b.get_filename(), "wb"); f.write("hi"); f.close(); del f
646 f = open(c.get_filename(), "wb"); f.write("hi"); f.close(); del f
648 _failUnlessExists("a")
649 _failUnlessExists("b")
650 _failUnlessExists("c")
654 _failUnlessExists("a")
655 _failUnlessExists("b")
656 _failUnlessExists("c")
659 # this file won't be deleted yet, because it isn't old enough
661 _failUnlessExists("a")
662 _failUnlessExists("b")
663 _failUnlessExists("c")
665 # we change the definition of "old" to make everything old
670 _failUnlessExists("b")
671 _failUnlessExists("c")
679 _failUnlessExists("b")
680 _failUnlessExists("c")
682 b2 = cdm.get_file("b")
686 _failUnlessExists("b")
687 _failUnlessExists("c")