2 def foo(): pass # keep the line number constant
5 from StringIO import StringIO
6 from twisted.trial import unittest
7 from twisted.internet import defer, reactor
8 from twisted.python.failure import Failure
9 from twisted.python import log
10 from pycryptopp.hash.sha256 import SHA256 as _hash
12 from allmydata.util import base32, idlib, humanreadable, mathutil, hashutil
13 from allmydata.util import assertutil, fileutil, deferredutil, abbreviate
14 from allmydata.util import limiter, time_format, pollmixin, cachedir
15 from allmydata.util import statistics, dictutil, pipeline
16 from allmydata.util import log as tahoe_log
17 from allmydata.util.spans import Spans, overlap, DataSpans
19 class Base32(unittest.TestCase):
20 def test_b2a_matches_Pythons(self):
22 y = "\x12\x34\x45\x67\x89\x0a\xbc\xde\xf0"
23 x = base64.b32encode(y)
24 while x and x[-1] == '=':
27 self.failUnlessEqual(base32.b2a(y), x)
29 self.failUnlessEqual(base32.b2a("\x12\x34"), "ci2a")
30 def test_b2a_or_none(self):
31 self.failUnlessEqual(base32.b2a_or_none(None), None)
32 self.failUnlessEqual(base32.b2a_or_none("\x12\x34"), "ci2a")
34 self.failUnlessEqual(base32.a2b("ci2a"), "\x12\x34")
35 self.failUnlessRaises(AssertionError, base32.a2b, "b0gus")
37 class IDLib(unittest.TestCase):
38 def test_nodeid_b2a(self):
39 self.failUnlessEqual(idlib.nodeid_b2a("\x00"*20), "a"*32)
41 class NoArgumentException(Exception):
45 class HumanReadable(unittest.TestCase):
48 self.failUnlessEqual(hr(foo), "<foo() at test_util.py:2>")
49 self.failUnlessEqual(hr(self.test_repr),
50 "<bound method HumanReadable.test_repr of <allmydata.test.test_util.HumanReadable testMethod=test_repr>>")
51 self.failUnlessEqual(hr(1L), "1")
52 self.failUnlessEqual(hr(10**40),
53 "100000000000000000...000000000000000000")
54 self.failUnlessEqual(hr(self), "<allmydata.test.test_util.HumanReadable testMethod=test_repr>")
55 self.failUnlessEqual(hr([1,2]), "[1, 2]")
56 self.failUnlessEqual(hr({1:2}), "{1:2}")
61 hr(e) == "<ValueError: ()>" # python-2.4
62 or hr(e) == "ValueError()") # python-2.5
64 raise ValueError("oops")
67 hr(e) == "<ValueError: 'oops'>" # python-2.4
68 or hr(e) == "ValueError('oops',)") # python-2.5
70 raise NoArgumentException
73 hr(e) == "<NoArgumentException>" # python-2.4
74 or hr(e) == "NoArgumentException()") # python-2.5
80 class Math(unittest.TestCase):
81 def test_div_ceil(self):
83 self.failUnlessEqual(f(0, 1), 0)
84 self.failUnlessEqual(f(0, 2), 0)
85 self.failUnlessEqual(f(0, 3), 0)
86 self.failUnlessEqual(f(1, 3), 1)
87 self.failUnlessEqual(f(2, 3), 1)
88 self.failUnlessEqual(f(3, 3), 1)
89 self.failUnlessEqual(f(4, 3), 2)
90 self.failUnlessEqual(f(5, 3), 2)
91 self.failUnlessEqual(f(6, 3), 2)
92 self.failUnlessEqual(f(7, 3), 3)
94 def test_next_multiple(self):
95 f = mathutil.next_multiple
96 self.failUnlessEqual(f(5, 1), 5)
97 self.failUnlessEqual(f(5, 2), 6)
98 self.failUnlessEqual(f(5, 3), 6)
99 self.failUnlessEqual(f(5, 4), 8)
100 self.failUnlessEqual(f(5, 5), 5)
101 self.failUnlessEqual(f(5, 6), 6)
102 self.failUnlessEqual(f(32, 1), 32)
103 self.failUnlessEqual(f(32, 2), 32)
104 self.failUnlessEqual(f(32, 3), 33)
105 self.failUnlessEqual(f(32, 4), 32)
106 self.failUnlessEqual(f(32, 5), 35)
107 self.failUnlessEqual(f(32, 6), 36)
108 self.failUnlessEqual(f(32, 7), 35)
109 self.failUnlessEqual(f(32, 8), 32)
110 self.failUnlessEqual(f(32, 9), 36)
111 self.failUnlessEqual(f(32, 10), 40)
112 self.failUnlessEqual(f(32, 11), 33)
113 self.failUnlessEqual(f(32, 12), 36)
114 self.failUnlessEqual(f(32, 13), 39)
115 self.failUnlessEqual(f(32, 14), 42)
116 self.failUnlessEqual(f(32, 15), 45)
117 self.failUnlessEqual(f(32, 16), 32)
118 self.failUnlessEqual(f(32, 17), 34)
119 self.failUnlessEqual(f(32, 18), 36)
120 self.failUnlessEqual(f(32, 589), 589)
122 def test_pad_size(self):
123 f = mathutil.pad_size
124 self.failUnlessEqual(f(0, 4), 0)
125 self.failUnlessEqual(f(1, 4), 3)
126 self.failUnlessEqual(f(2, 4), 2)
127 self.failUnlessEqual(f(3, 4), 1)
128 self.failUnlessEqual(f(4, 4), 0)
129 self.failUnlessEqual(f(5, 4), 3)
131 def test_is_power_of_k(self):
132 f = mathutil.is_power_of_k
133 for i in range(1, 100):
134 if i in (1, 2, 4, 8, 16, 32, 64):
135 self.failUnless(f(i, 2), "but %d *is* a power of 2" % i)
137 self.failIf(f(i, 2), "but %d is *not* a power of 2" % i)
138 for i in range(1, 100):
139 if i in (1, 3, 9, 27, 81):
140 self.failUnless(f(i, 3), "but %d *is* a power of 3" % i)
142 self.failIf(f(i, 3), "but %d is *not* a power of 3" % i)
144 def test_next_power_of_k(self):
145 f = mathutil.next_power_of_k
146 self.failUnlessEqual(f(0,2), 1)
147 self.failUnlessEqual(f(1,2), 1)
148 self.failUnlessEqual(f(2,2), 2)
149 self.failUnlessEqual(f(3,2), 4)
150 self.failUnlessEqual(f(4,2), 4)
151 for i in range(5, 8): self.failUnlessEqual(f(i,2), 8, "%d" % i)
152 for i in range(9, 16): self.failUnlessEqual(f(i,2), 16, "%d" % i)
153 for i in range(17, 32): self.failUnlessEqual(f(i,2), 32, "%d" % i)
154 for i in range(33, 64): self.failUnlessEqual(f(i,2), 64, "%d" % i)
155 for i in range(65, 100): self.failUnlessEqual(f(i,2), 128, "%d" % i)
157 self.failUnlessEqual(f(0,3), 1)
158 self.failUnlessEqual(f(1,3), 1)
159 self.failUnlessEqual(f(2,3), 3)
160 self.failUnlessEqual(f(3,3), 3)
161 for i in range(4, 9): self.failUnlessEqual(f(i,3), 9, "%d" % i)
162 for i in range(10, 27): self.failUnlessEqual(f(i,3), 27, "%d" % i)
163 for i in range(28, 81): self.failUnlessEqual(f(i,3), 81, "%d" % i)
164 for i in range(82, 200): self.failUnlessEqual(f(i,3), 243, "%d" % i)
168 self.failUnlessEqual(f([1,2,3]), 2)
169 self.failUnlessEqual(f([0,0,0,4]), 1)
170 self.failUnlessAlmostEqual(f([0.0, 1.0, 1.0]), .666666666666)
172 def test_round_sigfigs(self):
173 f = mathutil.round_sigfigs
174 self.failUnlessEqual(f(22.0/3, 4), 7.3330000000000002)
176 class Statistics(unittest.TestCase):
177 def should_assert(self, msg, func, *args, **kwargs):
179 func(*args, **kwargs)
181 except AssertionError:
184 def failUnlessListEqual(self, a, b, msg = None):
185 self.failUnlessEqual(len(a), len(b))
186 for i in range(len(a)):
187 self.failUnlessEqual(a[i], b[i], msg)
189 def failUnlessListAlmostEqual(self, a, b, places = 7, msg = None):
190 self.failUnlessEqual(len(a), len(b))
191 for i in range(len(a)):
192 self.failUnlessAlmostEqual(a[i], b[i], places, msg)
194 def test_binomial_coeff(self):
195 f = statistics.binomial_coeff
196 self.failUnlessEqual(f(20, 0), 1)
197 self.failUnlessEqual(f(20, 1), 20)
198 self.failUnlessEqual(f(20, 2), 190)
199 self.failUnlessEqual(f(20, 8), f(20, 12))
200 self.should_assert("Should assert if n < k", f, 2, 3)
202 def test_binomial_distribution_pmf(self):
203 f = statistics.binomial_distribution_pmf
206 pmf_stat = [0.81, 0.18, 0.01]
207 self.failUnlessListAlmostEqual(pmf_comp, pmf_stat)
209 # Summing across a PMF should give the total probability 1
210 self.failUnlessAlmostEqual(sum(pmf_comp), 1)
211 self.should_assert("Should assert if not 0<=p<=1", f, 1, -1)
212 self.should_assert("Should assert if n < 1", f, 0, .1)
215 statistics.print_pmf(pmf_comp, out=out)
216 lines = out.getvalue().splitlines()
217 self.failUnlessEqual(lines[0], "i=0: 0.81")
218 self.failUnlessEqual(lines[1], "i=1: 0.18")
219 self.failUnlessEqual(lines[2], "i=2: 0.01")
221 def test_survival_pmf(self):
222 f = statistics.survival_pmf
223 # Cross-check binomial-distribution method against convolution
225 p_list = [.9999] * 100 + [.99] * 50 + [.8] * 20
226 pmf1 = statistics.survival_pmf_via_conv(p_list)
227 pmf2 = statistics.survival_pmf_via_bd(p_list)
228 self.failUnlessListAlmostEqual(pmf1, pmf2)
229 self.failUnlessTrue(statistics.valid_pmf(pmf1))
230 self.should_assert("Should assert if p_i > 1", f, [1.1]);
231 self.should_assert("Should assert if p_i < 0", f, [-.1]);
233 def test_repair_count_pmf(self):
234 survival_pmf = statistics.binomial_distribution_pmf(5, .9)
235 repair_pmf = statistics.repair_count_pmf(survival_pmf, 3)
236 # repair_pmf[0] == sum(survival_pmf[0,1,2,5])
237 # repair_pmf[1] == survival_pmf[4]
238 # repair_pmf[2] = survival_pmf[3]
239 self.failUnlessListAlmostEqual(repair_pmf,
240 [0.00001 + 0.00045 + 0.0081 + 0.59049,
245 def test_repair_cost(self):
246 survival_pmf = statistics.binomial_distribution_pmf(5, .9)
247 bwcost = statistics.bandwidth_cost_function
248 cost = statistics.mean_repair_cost(bwcost, 1000,
249 survival_pmf, 3, ul_dl_ratio=1.0)
250 self.failUnlessAlmostEqual(cost, 558.90)
251 cost = statistics.mean_repair_cost(bwcost, 1000,
252 survival_pmf, 3, ul_dl_ratio=8.0)
253 self.failUnlessAlmostEqual(cost, 1664.55)
255 # I haven't manually checked the math beyond here -warner
256 cost = statistics.eternal_repair_cost(bwcost, 1000,
258 discount_rate=0, ul_dl_ratio=1.0)
259 self.failUnlessAlmostEqual(cost, 65292.056074766246)
260 cost = statistics.eternal_repair_cost(bwcost, 1000,
264 self.failUnlessAlmostEqual(cost, 9133.6097158191551)
266 def test_convolve(self):
267 f = statistics.convolve
271 v1v2result = [ 4, 13, 28, 27, 18 ]
272 # Convolution is commutative
275 self.failUnlessListEqual(r1, r2, "Convolution should be commutative")
276 self.failUnlessListEqual(r1, v1v2result, "Didn't match known result")
277 # Convolution is associative
278 r1 = f(f(v1, v2), v3)
279 r2 = f(v1, f(v2, v3))
280 self.failUnlessListEqual(r1, r2, "Convolution should be associative")
281 # Convolution is distributive
282 r1 = f(v3, [ a + b for a, b in zip(v1, v2) ])
285 r2 = [ a + b for a, b in zip(tmp1, tmp2) ]
286 self.failUnlessListEqual(r1, r2, "Convolution should be distributive")
287 # Convolution is scalar multiplication associative
289 r1 = [ a * 4 for a in tmp1 ]
290 tmp2 = [ a * 4 for a in v1 ]
292 self.failUnlessListEqual(r1, r2, "Convolution should be scalar multiplication associative")
294 def test_find_k(self):
295 f = statistics.find_k
296 g = statistics.pr_file_loss
297 plist = [.9] * 10 + [.8] * 10 # N=20
300 self.failUnlessEqual(k, 10)
301 self.failUnless(g(plist, k) < t)
303 def test_pr_file_loss(self):
304 f = statistics.pr_file_loss
306 self.failUnlessEqual(f(plist, 3), .0546875)
308 def test_pr_backup_file_loss(self):
309 f = statistics.pr_backup_file_loss
311 self.failUnlessEqual(f(plist, .5, 3), .02734375)
314 class Asserts(unittest.TestCase):
315 def should_assert(self, func, *args, **kwargs):
317 func(*args, **kwargs)
318 except AssertionError, e:
321 self.fail("assert failed with non-AssertionError: %s" % e)
322 self.fail("assert was not caught")
324 def should_not_assert(self, func, *args, **kwargs):
326 func(*args, **kwargs)
327 except AssertionError, e:
328 self.fail("assertion fired when it should not have: %s" % e)
330 self.fail("assertion (which shouldn't have failed) failed with non-AssertionError: %s" % e)
334 def test_assert(self):
335 f = assertutil._assert
336 self.should_assert(f)
337 self.should_assert(f, False)
338 self.should_not_assert(f, True)
340 m = self.should_assert(f, False, "message")
341 self.failUnlessEqual(m, "'message' <type 'str'>", m)
342 m = self.should_assert(f, False, "message1", othermsg=12)
343 self.failUnlessEqual("'message1' <type 'str'>, othermsg: 12 <type 'int'>", m)
344 m = self.should_assert(f, False, othermsg="message2")
345 self.failUnlessEqual("othermsg: 'message2' <type 'str'>", m)
347 def test_precondition(self):
348 f = assertutil.precondition
349 self.should_assert(f)
350 self.should_assert(f, False)
351 self.should_not_assert(f, True)
353 m = self.should_assert(f, False, "message")
354 self.failUnlessEqual("precondition: 'message' <type 'str'>", m)
355 m = self.should_assert(f, False, "message1", othermsg=12)
356 self.failUnlessEqual("precondition: 'message1' <type 'str'>, othermsg: 12 <type 'int'>", m)
357 m = self.should_assert(f, False, othermsg="message2")
358 self.failUnlessEqual("precondition: othermsg: 'message2' <type 'str'>", m)
360 def test_postcondition(self):
361 f = assertutil.postcondition
362 self.should_assert(f)
363 self.should_assert(f, False)
364 self.should_not_assert(f, True)
366 m = self.should_assert(f, False, "message")
367 self.failUnlessEqual("postcondition: 'message' <type 'str'>", m)
368 m = self.should_assert(f, False, "message1", othermsg=12)
369 self.failUnlessEqual("postcondition: 'message1' <type 'str'>, othermsg: 12 <type 'int'>", m)
370 m = self.should_assert(f, False, othermsg="message2")
371 self.failUnlessEqual("postcondition: othermsg: 'message2' <type 'str'>", m)
373 class FileUtil(unittest.TestCase):
374 def mkdir(self, basedir, path, mode=0777):
375 fn = os.path.join(basedir, path)
376 fileutil.make_dirs(fn, mode)
378 def touch(self, basedir, path, mode=None, data="touch\n"):
379 fn = os.path.join(basedir, path)
386 def test_rm_dir(self):
387 basedir = "util/FileUtil/test_rm_dir"
388 fileutil.make_dirs(basedir)
389 # create it again to test idempotency
390 fileutil.make_dirs(basedir)
391 d = os.path.join(basedir, "doomed")
393 self.touch(d, "a/b/1.txt")
394 self.touch(d, "a/b/2.txt", 0444)
395 self.touch(d, "a/b/3.txt", 0)
397 self.touch(d, "a/c/1.txt")
398 self.touch(d, "a/c/2.txt", 0444)
399 self.touch(d, "a/c/3.txt", 0)
400 os.chmod(os.path.join(d, "a/c"), 0444)
402 self.touch(d, "a/d/1.txt")
403 self.touch(d, "a/d/2.txt", 0444)
404 self.touch(d, "a/d/3.txt", 0)
405 os.chmod(os.path.join(d, "a/d"), 0)
408 self.failIf(os.path.exists(d))
409 # remove it again to test idempotency
412 def test_remove_if_possible(self):
413 basedir = "util/FileUtil/test_remove_if_possible"
414 fileutil.make_dirs(basedir)
415 self.touch(basedir, "here")
416 fn = os.path.join(basedir, "here")
417 fileutil.remove_if_possible(fn)
418 self.failIf(os.path.exists(fn))
419 fileutil.remove_if_possible(fn) # should be idempotent
420 fileutil.rm_dir(basedir)
421 fileutil.remove_if_possible(fn) # should survive errors
423 def test_write_atomically(self):
424 basedir = "util/FileUtil/test_write_atomically"
425 fileutil.make_dirs(basedir)
426 fn = os.path.join(basedir, "here")
427 fileutil.write_atomically(fn, "one")
428 self.failUnlessEqual(fileutil.read(fn), "one")
429 fileutil.write_atomically(fn, "two", mode="") # non-binary
430 self.failUnlessEqual(fileutil.read(fn), "two")
432 def test_open_or_create(self):
433 basedir = "util/FileUtil/test_open_or_create"
434 fileutil.make_dirs(basedir)
435 fn = os.path.join(basedir, "here")
436 f = fileutil.open_or_create(fn)
439 f = fileutil.open_or_create(fn)
446 self.failUnlessEqual(data, "stuff.more.")
448 def test_NamedTemporaryDirectory(self):
449 basedir = "util/FileUtil/test_NamedTemporaryDirectory"
450 fileutil.make_dirs(basedir)
451 td = fileutil.NamedTemporaryDirectory(dir=basedir)
453 self.failUnless(basedir in name)
454 self.failUnless(basedir in repr(td))
455 self.failUnless(os.path.isdir(name))
457 # it is conceivable that we need to force gc here, but I'm not sure
458 self.failIf(os.path.isdir(name))
460 def test_rename(self):
461 basedir = "util/FileUtil/test_rename"
462 fileutil.make_dirs(basedir)
463 self.touch(basedir, "here")
464 fn = os.path.join(basedir, "here")
465 fn2 = os.path.join(basedir, "there")
466 fileutil.rename(fn, fn2)
467 self.failIf(os.path.exists(fn))
468 self.failUnless(os.path.exists(fn2))
471 basedir = "util/FileUtil/test_du"
472 fileutil.make_dirs(basedir)
473 d = os.path.join(basedir, "space-consuming")
475 self.touch(d, "a/b/1.txt", data="a"*10)
476 self.touch(d, "a/b/2.txt", data="b"*11)
478 self.touch(d, "a/c/1.txt", data="c"*12)
479 self.touch(d, "a/c/2.txt", data="d"*13)
481 used = fileutil.du(basedir)
482 self.failUnlessEqual(10+11+12+13, used)
484 def test_abspath_expanduser_unicode(self):
485 self.failUnlessRaises(AssertionError, fileutil.abspath_expanduser_unicode, "bytestring")
487 saved_cwd = os.path.normpath(os.getcwdu())
488 abspath_cwd = fileutil.abspath_expanduser_unicode(u".")
489 self.failUnless(isinstance(saved_cwd, unicode), saved_cwd)
490 self.failUnless(isinstance(abspath_cwd, unicode), abspath_cwd)
491 self.failUnlessEqual(abspath_cwd, saved_cwd)
493 # adapted from <http://svn.python.org/view/python/branches/release26-maint/Lib/test/test_posixpath.py?view=markup&pathrev=78279#test_abspath>
495 self.failUnlessIn(u"foo", fileutil.abspath_expanduser_unicode(u"foo"))
496 self.failIfIn(u"~", fileutil.abspath_expanduser_unicode(u"~"))
500 cwds.append(u'\xe7w\xf0'.encode(sys.getfilesystemencoding()
502 except UnicodeEncodeError:
503 pass # the cwd can't be encoded -- test with ascii cwd only
509 for upath in (u'', u'fuu', u'f\xf9\xf9', u'/fuu', u'U:\\', u'~'):
510 uabspath = fileutil.abspath_expanduser_unicode(upath)
511 self.failUnless(isinstance(uabspath, unicode), uabspath)
515 def test_disk_stats(self):
516 avail = fileutil.get_available_space('.', 2**14)
518 raise unittest.SkipTest("This test will spuriously fail there is no disk space left.")
520 disk = fileutil.get_disk_stats('.', 2**13)
521 self.failUnless(disk['total'] > 0, disk['total'])
522 self.failUnless(disk['used'] > 0, disk['used'])
523 self.failUnless(disk['free_for_root'] > 0, disk['free_for_root'])
524 self.failUnless(disk['free_for_nonroot'] > 0, disk['free_for_nonroot'])
525 self.failUnless(disk['avail'] > 0, disk['avail'])
527 def test_disk_stats_avail_nonnegative(self):
528 # This test will spuriously fail if you have more than 2^128
529 # bytes of available space on your filesystem.
530 disk = fileutil.get_disk_stats('.', 2**128)
531 self.failUnlessEqual(disk['avail'], 0)
533 class PollMixinTests(unittest.TestCase):
535 self.pm = pollmixin.PollMixin()
537 def test_PollMixin_True(self):
538 d = self.pm.poll(check_f=lambda : True,
542 def test_PollMixin_False_then_True(self):
543 i = iter([False, True])
544 d = self.pm.poll(check_f=i.next,
548 def test_timeout(self):
549 d = self.pm.poll(check_f=lambda: False,
553 self.fail("poll should have failed, not returned %s" % (res,))
555 f.trap(pollmixin.TimeoutError)
556 return None # success
557 d.addCallbacks(_suc, _err)
560 class DeferredUtilTests(unittest.TestCase):
561 def test_gather_results(self):
562 d1 = defer.Deferred()
563 d2 = defer.Deferred()
564 res = deferredutil.gatherResults([d1, d2])
565 d1.errback(ValueError("BAD"))
567 self.fail("Should have errbacked, not resulted in %s" % (res,))
569 thef.trap(ValueError)
570 res.addCallbacks(_callb, _errb)
573 def test_success(self):
574 d1, d2 = defer.Deferred(), defer.Deferred()
577 dlss = deferredutil.DeferredListShouldSucceed([d1,d2])
578 dlss.addCallbacks(good.append, bad.append)
581 self.failUnlessEqual(good, [[1,2]])
582 self.failUnlessEqual(bad, [])
584 def test_failure(self):
585 d1, d2 = defer.Deferred(), defer.Deferred()
588 dlss = deferredutil.DeferredListShouldSucceed([d1,d2])
589 dlss.addCallbacks(good.append, bad.append)
590 d1.addErrback(lambda _ignore: None)
591 d2.addErrback(lambda _ignore: None)
593 d2.errback(ValueError())
594 self.failUnlessEqual(good, [])
595 self.failUnlessEqual(len(bad), 1)
597 self.failUnless(isinstance(f, Failure))
598 self.failUnless(f.check(ValueError))
600 class HashUtilTests(unittest.TestCase):
602 def test_random_key(self):
603 k = hashutil.random_key()
604 self.failUnlessEqual(len(k), hashutil.KEYLEN)
606 def test_sha256d(self):
607 h1 = hashutil.tagged_hash("tag1", "value")
608 h2 = hashutil.tagged_hasher("tag1")
612 self.failUnlessEqual(h1, h2a)
613 self.failUnlessEqual(h2a, h2b)
615 def test_sha256d_truncated(self):
616 h1 = hashutil.tagged_hash("tag1", "value", 16)
617 h2 = hashutil.tagged_hasher("tag1", 16)
620 self.failUnlessEqual(len(h1), 16)
621 self.failUnlessEqual(len(h2), 16)
622 self.failUnlessEqual(h1, h2)
625 h1 = hashutil.convergence_hash(3, 10, 1000, "data", "secret")
626 h2 = hashutil.convergence_hasher(3, 10, 1000, "secret")
629 self.failUnlessEqual(h1, h2)
631 def test_hashers(self):
632 h1 = hashutil.block_hash("foo")
633 h2 = hashutil.block_hasher()
635 self.failUnlessEqual(h1, h2.digest())
637 h1 = hashutil.uri_extension_hash("foo")
638 h2 = hashutil.uri_extension_hasher()
640 self.failUnlessEqual(h1, h2.digest())
642 h1 = hashutil.plaintext_hash("foo")
643 h2 = hashutil.plaintext_hasher()
645 self.failUnlessEqual(h1, h2.digest())
647 h1 = hashutil.crypttext_hash("foo")
648 h2 = hashutil.crypttext_hasher()
650 self.failUnlessEqual(h1, h2.digest())
652 h1 = hashutil.crypttext_segment_hash("foo")
653 h2 = hashutil.crypttext_segment_hasher()
655 self.failUnlessEqual(h1, h2.digest())
657 h1 = hashutil.plaintext_segment_hash("foo")
658 h2 = hashutil.plaintext_segment_hasher()
660 self.failUnlessEqual(h1, h2.digest())
662 def test_constant_time_compare(self):
663 self.failUnless(hashutil.constant_time_compare("a", "a"))
664 self.failUnless(hashutil.constant_time_compare("ab", "ab"))
665 self.failIf(hashutil.constant_time_compare("a", "b"))
666 self.failIf(hashutil.constant_time_compare("a", "aa"))
668 def _testknown(self, hashf, expected_a, *args):
670 got_a = base32.b2a(got)
671 self.failUnlessEqual(got_a, expected_a)
673 def test_known_answers(self):
674 # assert backwards compatibility
675 self._testknown(hashutil.storage_index_hash, "qb5igbhcc5esa6lwqorsy7e6am", "")
676 self._testknown(hashutil.block_hash, "msjr5bh4evuh7fa3zw7uovixfbvlnstr5b65mrerwfnvjxig2jvq", "")
677 self._testknown(hashutil.uri_extension_hash, "wthsu45q7zewac2mnivoaa4ulh5xvbzdmsbuyztq2a5fzxdrnkka", "")
678 self._testknown(hashutil.plaintext_hash, "5lz5hwz3qj3af7n6e3arblw7xzutvnd3p3fjsngqjcb7utf3x3da", "")
679 self._testknown(hashutil.crypttext_hash, "itdj6e4njtkoiavlrmxkvpreosscssklunhwtvxn6ggho4rkqwga", "")
680 self._testknown(hashutil.crypttext_segment_hash, "aovy5aa7jej6ym5ikgwyoi4pxawnoj3wtaludjz7e2nb5xijb7aa", "")
681 self._testknown(hashutil.plaintext_segment_hash, "4fdgf6qruaisyukhqcmoth4t3li6bkolbxvjy4awwcpprdtva7za", "")
682 self._testknown(hashutil.convergence_hash, "3mo6ni7xweplycin6nowynw2we", 3, 10, 100, "", "converge")
683 self._testknown(hashutil.my_renewal_secret_hash, "ujhr5k5f7ypkp67jkpx6jl4p47pyta7hu5m527cpcgvkafsefm6q", "")
684 self._testknown(hashutil.my_cancel_secret_hash, "rjwzmafe2duixvqy6h47f5wfrokdziry6zhx4smew4cj6iocsfaa", "")
685 self._testknown(hashutil.file_renewal_secret_hash, "hzshk2kf33gzbd5n3a6eszkf6q6o6kixmnag25pniusyaulqjnia", "", "si")
686 self._testknown(hashutil.file_cancel_secret_hash, "bfciwvr6w7wcavsngxzxsxxaszj72dej54n4tu2idzp6b74g255q", "", "si")
687 self._testknown(hashutil.bucket_renewal_secret_hash, "e7imrzgzaoashsncacvy3oysdd2m5yvtooo4gmj4mjlopsazmvuq", "", "\x00"*20)
688 self._testknown(hashutil.bucket_cancel_secret_hash, "dvdujeyxeirj6uux6g7xcf4lvesk632aulwkzjar7srildvtqwma", "", "\x00"*20)
689 self._testknown(hashutil.hmac, "c54ypfi6pevb3nvo6ba42jtglpkry2kbdopqsi7dgrm4r7tw5sra", "tag", "")
690 self._testknown(hashutil.mutable_rwcap_key_hash, "6rvn2iqrghii5n4jbbwwqqsnqu", "iv", "wk")
691 self._testknown(hashutil.ssk_writekey_hash, "ykpgmdbpgbb6yqz5oluw2q26ye", "")
692 self._testknown(hashutil.ssk_write_enabler_master_hash, "izbfbfkoait4dummruol3gy2bnixrrrslgye6ycmkuyujnenzpia", "")
693 self._testknown(hashutil.ssk_write_enabler_hash, "fuu2dvx7g6gqu5x22vfhtyed7p4pd47y5hgxbqzgrlyvxoev62tq", "wk", "\x00"*20)
694 self._testknown(hashutil.ssk_pubkey_fingerprint_hash, "3opzw4hhm2sgncjx224qmt5ipqgagn7h5zivnfzqycvgqgmgz35q", "")
695 self._testknown(hashutil.ssk_readkey_hash, "vugid4as6qbqgeq2xczvvcedai", "")
696 self._testknown(hashutil.ssk_readkey_data_hash, "73wsaldnvdzqaf7v4pzbr2ae5a", "iv", "rk")
697 self._testknown(hashutil.ssk_storage_index_hash, "j7icz6kigb6hxrej3tv4z7ayym", "")
700 class Abbreviate(unittest.TestCase):
702 a = abbreviate.abbreviate_time
703 self.failUnlessEqual(a(None), "unknown")
704 self.failUnlessEqual(a(0), "0 seconds")
705 self.failUnlessEqual(a(1), "1 second")
706 self.failUnlessEqual(a(2), "2 seconds")
707 self.failUnlessEqual(a(119), "119 seconds")
709 self.failUnlessEqual(a(2*MIN), "2 minutes")
710 self.failUnlessEqual(a(60*MIN), "60 minutes")
711 self.failUnlessEqual(a(179*MIN), "179 minutes")
713 self.failUnlessEqual(a(180*MIN), "3 hours")
714 self.failUnlessEqual(a(4*HOUR), "4 hours")
717 self.failUnlessEqual(a(2*DAY), "2 days")
718 self.failUnlessEqual(a(2*MONTH), "2 months")
720 self.failUnlessEqual(a(5*YEAR), "5 years")
722 def test_space(self):
723 tests_si = [(None, "unknown"),
730 (20*1000, "20.00 kB"),
731 (1024*1024, "1.05 MB"),
732 (1000*1000, "1.00 MB"),
733 (1000*1000*1000, "1.00 GB"),
734 (1000*1000*1000*1000, "1.00 TB"),
735 (1000*1000*1000*1000*1000, "1.00 PB"),
736 (1234567890123456, "1.23 PB"),
738 for (x, expected) in tests_si:
739 got = abbreviate.abbreviate_space(x, SI=True)
740 self.failUnlessEqual(got, expected)
742 tests_base1024 = [(None, "unknown"),
749 (20*1024, "20.00 kiB"),
750 (1000*1000, "976.56 kiB"),
751 (1024*1024, "1.00 MiB"),
752 (1024*1024*1024, "1.00 GiB"),
753 (1024*1024*1024*1024, "1.00 TiB"),
754 (1000*1000*1000*1000*1000, "909.49 TiB"),
755 (1024*1024*1024*1024*1024, "1.00 PiB"),
756 (1234567890123456, "1.10 PiB"),
758 for (x, expected) in tests_base1024:
759 got = abbreviate.abbreviate_space(x, SI=False)
760 self.failUnlessEqual(got, expected)
762 self.failUnlessEqual(abbreviate.abbreviate_space_both(1234567),
763 "(1.23 MB, 1.18 MiB)")
765 def test_parse_space(self):
766 p = abbreviate.parse_abbreviated_size
767 self.failUnlessEqual(p(""), None)
768 self.failUnlessEqual(p(None), None)
769 self.failUnlessEqual(p("123"), 123)
770 self.failUnlessEqual(p("123B"), 123)
771 self.failUnlessEqual(p("2K"), 2000)
772 self.failUnlessEqual(p("2kb"), 2000)
773 self.failUnlessEqual(p("2KiB"), 2048)
774 self.failUnlessEqual(p("10MB"), 10*1000*1000)
775 self.failUnlessEqual(p("10MiB"), 10*1024*1024)
776 self.failUnlessEqual(p("5G"), 5*1000*1000*1000)
777 self.failUnlessEqual(p("4GiB"), 4*1024*1024*1024)
778 e = self.failUnlessRaises(ValueError, p, "12 cubits")
779 self.failUnless("12 cubits" in str(e))
781 class Limiter(unittest.TestCase):
782 timeout = 480 # This takes longer than 240 seconds on Francois's arm box.
784 def job(self, i, foo):
785 self.calls.append( (i, foo) )
786 self.simultaneous += 1
787 self.peak_simultaneous = max(self.simultaneous, self.peak_simultaneous)
790 self.simultaneous -= 1
791 d.callback("done %d" % i)
792 reactor.callLater(1.0, _done)
795 def bad_job(self, i, foo):
796 raise ValueError("bad_job %d" % i)
798 def test_limiter(self):
800 self.simultaneous = 0
801 self.peak_simultaneous = 0
802 l = limiter.ConcurrencyLimiter()
805 dl.append(l.add(self.job, i, foo=str(i)))
806 d = defer.DeferredList(dl, fireOnOneErrback=True)
808 self.failUnlessEqual(self.simultaneous, 0)
809 self.failUnless(self.peak_simultaneous <= 10)
810 self.failUnlessEqual(len(self.calls), 20)
812 self.failUnless( (i, str(i)) in self.calls)
816 def test_errors(self):
818 self.simultaneous = 0
819 self.peak_simultaneous = 0
820 l = limiter.ConcurrencyLimiter()
823 dl.append(l.add(self.job, i, foo=str(i)))
824 d2 = l.add(self.bad_job, 21, "21")
825 d = defer.DeferredList(dl, fireOnOneErrback=True)
828 for (success, result) in res:
829 self.failUnlessEqual(success, True)
830 results.append(result)
832 expected_results = ["done %d" % i for i in range(20)]
833 expected_results.sort()
834 self.failUnlessEqual(results, expected_results)
835 self.failUnless(self.peak_simultaneous <= 10)
836 self.failUnlessEqual(len(self.calls), 20)
838 self.failUnless( (i, str(i)) in self.calls)
840 self.fail("should have failed, not got %s" % (res,))
843 self.failUnless("bad_job 21" in str(f))
844 d2.addCallbacks(_good, _err)
846 d.addCallback(_most_done)
848 self.failUnlessEqual(self.simultaneous, 0)
849 self.failUnless(self.peak_simultaneous <= 10)
850 self.failUnlessEqual(len(self.calls), 20)
852 self.failUnless( (i, str(i)) in self.calls)
853 d.addCallback(_all_done)
856 class TimeFormat(unittest.TestCase):
857 def test_epoch(self):
858 return self._help_test_epoch()
860 def test_epoch_in_London(self):
861 # Europe/London is a particularly troublesome timezone. Nowadays, its
862 # offset from GMT is 0. But in 1970, its offset from GMT was 1.
863 # (Apparently in 1970 Britain had redefined standard time to be GMT+1
864 # and stayed in standard time all year round, whereas today
865 # Europe/London standard time is GMT and Europe/London Daylight
866 # Savings Time is GMT+1.) The current implementation of
867 # time_format.iso_utc_time_to_localseconds() breaks if the timezone is
868 # Europe/London. (As soon as this unit test is done then I'll change
869 # that implementation to something that works even in this case...)
870 origtz = os.environ.get('TZ')
871 os.environ['TZ'] = "Europe/London"
872 if hasattr(time, 'tzset'):
875 return self._help_test_epoch()
880 os.environ['TZ'] = origtz
881 if hasattr(time, 'tzset'):
884 def _help_test_epoch(self):
885 origtzname = time.tzname
886 s = time_format.iso_utc_time_to_seconds("1970-01-01T00:00:01")
887 self.failUnlessEqual(s, 1.0)
888 s = time_format.iso_utc_time_to_seconds("1970-01-01_00:00:01")
889 self.failUnlessEqual(s, 1.0)
890 s = time_format.iso_utc_time_to_seconds("1970-01-01 00:00:01")
891 self.failUnlessEqual(s, 1.0)
893 self.failUnlessEqual(time_format.iso_utc(1.0), "1970-01-01_00:00:01")
894 self.failUnlessEqual(time_format.iso_utc(1.0, sep=" "),
895 "1970-01-01 00:00:01")
898 isostr = time_format.iso_utc(now)
899 timestamp = time_format.iso_utc_time_to_seconds(isostr)
900 self.failUnlessEqual(int(timestamp), int(now))
904 self.failUnlessEqual(time_format.iso_utc(t=my_time),
905 "1970-01-01_00:00:01")
906 e = self.failUnlessRaises(ValueError,
907 time_format.iso_utc_time_to_seconds,
908 "invalid timestring")
909 self.failUnless("not a complete ISO8601 timestamp" in str(e))
910 s = time_format.iso_utc_time_to_seconds("1970-01-01_00:00:01.500")
911 self.failUnlessEqual(s, 1.5)
913 # Look for daylight-savings-related errors.
914 thatmomentinmarch = time_format.iso_utc_time_to_seconds("2009-03-20 21:49:02.226536")
915 self.failUnlessEqual(thatmomentinmarch, 1237585742.226536)
916 self.failUnlessEqual(origtzname, time.tzname)
918 def test_iso_utc(self):
919 when = 1266760143.7841301
920 out = time_format.iso_utc_date(when)
921 self.failUnlessEqual(out, "2010-02-21")
922 out = time_format.iso_utc_date(t=lambda: when)
923 self.failUnlessEqual(out, "2010-02-21")
924 out = time_format.iso_utc(when)
925 self.failUnlessEqual(out, "2010-02-21_13:49:03.784130")
926 out = time_format.iso_utc(when, sep="-")
927 self.failUnlessEqual(out, "2010-02-21-13:49:03.784130")
929 def test_parse_duration(self):
930 p = time_format.parse_duration
932 self.failUnlessEqual(p("1 day"), DAY)
933 self.failUnlessEqual(p("2 days"), 2*DAY)
934 self.failUnlessEqual(p("3 months"), 3*31*DAY)
935 self.failUnlessEqual(p("4 mo"), 4*31*DAY)
936 self.failUnlessEqual(p("5 years"), 5*365*DAY)
937 e = self.failUnlessRaises(ValueError, p, "123")
938 self.failUnlessIn("no unit (like day, month, or year) in '123'",
941 def test_parse_date(self):
942 self.failUnlessEqual(time_format.parse_date("2010-02-21"), 1266710400)
944 class CacheDir(unittest.TestCase):
945 def test_basic(self):
946 basedir = "test_util/CacheDir/test_basic"
948 def _failIfExists(name):
949 absfn = os.path.join(basedir, name)
950 self.failIf(os.path.exists(absfn),
951 "%s exists but it shouldn't" % absfn)
953 def _failUnlessExists(name):
954 absfn = os.path.join(basedir, name)
955 self.failUnless(os.path.exists(absfn),
956 "%s doesn't exist but it should" % absfn)
958 cdm = cachedir.CacheDirectoryManager(basedir)
959 a = cdm.get_file("a")
960 b = cdm.get_file("b")
961 c = cdm.get_file("c")
962 f = open(a.get_filename(), "wb"); f.write("hi"); f.close(); del f
963 f = open(b.get_filename(), "wb"); f.write("hi"); f.close(); del f
964 f = open(c.get_filename(), "wb"); f.write("hi"); f.close(); del f
966 _failUnlessExists("a")
967 _failUnlessExists("b")
968 _failUnlessExists("c")
972 _failUnlessExists("a")
973 _failUnlessExists("b")
974 _failUnlessExists("c")
977 # this file won't be deleted yet, because it isn't old enough
979 _failUnlessExists("a")
980 _failUnlessExists("b")
981 _failUnlessExists("c")
983 # we change the definition of "old" to make everything old
988 _failUnlessExists("b")
989 _failUnlessExists("c")
997 _failUnlessExists("b")
998 _failUnlessExists("c")
1000 b2 = cdm.get_file("b")
1004 _failUnlessExists("b")
1005 _failUnlessExists("c")
1010 def __init__(self, x):
1015 return "<%s %s>" % (self.__class__.__name__, self.x,)
1018 def __le__(self, other):
1019 return self.x <= other
1020 def __lt__(self, other):
1021 return self.x < other
1022 def __ge__(self, other):
1023 return self.x >= other
1024 def __gt__(self, other):
1025 return self.x > other
1026 def __ne__(self, other):
1027 return self.x != other
1028 def __eq__(self, other):
1029 return self.x == other
1031 class DictUtil(unittest.TestCase):
1032 def _help_test_empty_dict(self, klass):
1036 self.failUnless(d1 == d2, "d1: %r, d2: %r" % (d1, d2,))
1037 self.failUnless(len(d1) == 0)
1038 self.failUnless(len(d2) == 0)
1040 def _help_test_nonempty_dict(self, klass):
1041 d1 = klass({'a': 1, 'b': "eggs", 3: "spam",})
1042 d2 = klass({'a': 1, 'b': "eggs", 3: "spam",})
1044 self.failUnless(d1 == d2)
1045 self.failUnless(len(d1) == 3, "%s, %s" % (len(d1), d1,))
1046 self.failUnless(len(d2) == 3)
1048 def _help_test_eq_but_notis(self, klass):
1049 d = klass({'a': 3, 'b': EqButNotIs(3), 'c': 3})
1054 d['b'] = EqButNotIs(3)
1059 d['b'] = EqButNotIs(3)
1065 d['a'] = EqButNotIs(3)
1070 fake3 = EqButNotIs(3)
1071 fake7 = EqButNotIs(7)
1075 self.failUnless(filter(lambda x: x is 8, d.itervalues()))
1076 self.failUnless(filter(lambda x: x is fake7, d.itervalues()))
1077 # The real 7 should have been ejected by the d[3] = 8.
1078 self.failUnless(not filter(lambda x: x is 7, d.itervalues()))
1079 self.failUnless(filter(lambda x: x is fake3, d.iterkeys()))
1080 self.failUnless(filter(lambda x: x is 3, d.iterkeys()))
1085 fake3 = EqButNotIs(3)
1086 fake7 = EqButNotIs(7)
1089 self.failUnless(filter(lambda x: x is 8, d.itervalues()))
1090 self.failUnless(filter(lambda x: x is fake7, d.itervalues()))
1091 # The real 7 should have been ejected by the d[3] = 8.
1092 self.failUnless(not filter(lambda x: x is 7, d.itervalues()))
1093 self.failUnless(filter(lambda x: x is fake3, d.iterkeys()))
1094 self.failUnless(filter(lambda x: x is 3, d.iterkeys()))
1098 self._help_test_eq_but_notis(dictutil.UtilDict)
1099 self._help_test_eq_but_notis(dictutil.NumDict)
1100 self._help_test_eq_but_notis(dictutil.ValueOrderedDict)
1101 self._help_test_nonempty_dict(dictutil.UtilDict)
1102 self._help_test_nonempty_dict(dictutil.NumDict)
1103 self._help_test_nonempty_dict(dictutil.ValueOrderedDict)
1104 self._help_test_eq_but_notis(dictutil.UtilDict)
1105 self._help_test_eq_but_notis(dictutil.NumDict)
1106 self._help_test_eq_but_notis(dictutil.ValueOrderedDict)
1108 def test_dict_of_sets(self):
1109 ds = dictutil.DictOfSets()
1114 self.failUnlessEqual(ds[1], set(["a"]))
1115 self.failUnlessEqual(ds[2], set(["b", "c"]))
1116 ds.discard(3, "d") # should not raise an exception
1118 self.failUnlessEqual(ds[2], set(["c"]))
1120 self.failIf(2 in ds)
1122 ds.union(1, ["a", "e"])
1124 self.failUnlessEqual(ds[1], set(["a","e"]))
1125 self.failUnlessEqual(ds[3], set(["f"]))
1126 ds2 = dictutil.DictOfSets()
1131 self.failUnlessEqual(ds[1], set(["a","e"]))
1132 self.failUnlessEqual(ds[3], set(["f", "g"]))
1133 self.failUnlessEqual(ds[4], set(["h"]))
1135 def test_move(self):
1136 d1 = {1: "a", 2: "b"}
1137 d2 = {2: "c", 3: "d"}
1138 dictutil.move(1, d1, d2)
1139 self.failUnlessEqual(d1, {2: "b"})
1140 self.failUnlessEqual(d2, {1: "a", 2: "c", 3: "d"})
1142 d1 = {1: "a", 2: "b"}
1143 d2 = {2: "c", 3: "d"}
1144 dictutil.move(2, d1, d2)
1145 self.failUnlessEqual(d1, {1: "a"})
1146 self.failUnlessEqual(d2, {2: "b", 3: "d"})
1148 d1 = {1: "a", 2: "b"}
1149 d2 = {2: "c", 3: "d"}
1150 self.failUnlessRaises(KeyError, dictutil.move, 5, d1, d2, strict=True)
1152 def test_subtract(self):
1153 d1 = {1: "a", 2: "b"}
1154 d2 = {2: "c", 3: "d"}
1155 d3 = dictutil.subtract(d1, d2)
1156 self.failUnlessEqual(d3, {1: "a"})
1158 d1 = {1: "a", 2: "b"}
1160 d3 = dictutil.subtract(d1, d2)
1161 self.failUnlessEqual(d3, {1: "a"})
1163 def test_utildict(self):
1164 d = dictutil.UtilDict({1: "a", 2: "b"})
1167 self.failUnlessEqual(d, {2: "b"})
1170 self.failUnlessRaises(TypeError, eq, d, "not a dict")
1172 d = dictutil.UtilDict({1: "b", 2: "a"})
1173 self.failUnlessEqual(d.items_sorted_by_value(),
1174 [(2, "a"), (1, "b")])
1175 self.failUnlessEqual(d.items_sorted_by_key(),
1176 [(1, "b"), (2, "a")])
1177 self.failUnlessEqual(repr(d), "{1: 'b', 2: 'a'}")
1178 self.failUnless(1 in d)
1180 d2 = dictutil.UtilDict({3: "c", 4: "d"})
1181 self.failUnless(d != d2)
1182 self.failUnless(d2 > d)
1183 self.failUnless(d2 >= d)
1184 self.failUnless(d <= d2)
1185 self.failUnless(d < d2)
1186 self.failUnlessEqual(d[1], "b")
1187 self.failUnlessEqual(sorted(list([k for k in d])), [1,2])
1190 self.failUnlessEqual(d, d3)
1191 self.failUnless(isinstance(d3, dictutil.UtilDict))
1193 d4 = d.fromkeys([3,4], "e")
1194 self.failUnlessEqual(d4, {3: "e", 4: "e"})
1196 self.failUnlessEqual(d.get(1), "b")
1197 self.failUnlessEqual(d.get(3), None)
1198 self.failUnlessEqual(d.get(3, "default"), "default")
1199 self.failUnlessEqual(sorted(list(d.items())),
1200 [(1, "b"), (2, "a")])
1201 self.failUnlessEqual(sorted(list(d.iteritems())),
1202 [(1, "b"), (2, "a")])
1203 self.failUnlessEqual(sorted(d.keys()), [1, 2])
1204 self.failUnlessEqual(sorted(d.values()), ["a", "b"])
1205 x = d.setdefault(1, "new")
1206 self.failUnlessEqual(x, "b")
1207 self.failUnlessEqual(d[1], "b")
1208 x = d.setdefault(3, "new")
1209 self.failUnlessEqual(x, "new")
1210 self.failUnlessEqual(d[3], "new")
1214 self.failUnless(x in [(1, "b"), (2, "a")])
1216 self.failUnless(x in [(1, "b"), (2, "a")])
1217 self.failUnlessRaises(KeyError, d.popitem)
1219 def test_numdict(self):
1220 d = dictutil.NumDict({"a": 1, "b": 2})
1222 d.add_num("a", 10, 5)
1223 d.add_num("c", 20, 5)
1225 self.failUnlessEqual(d, {"a": 11, "b": 2, "c": 25, "d": 30})
1227 d.subtract_num("a", 10)
1228 d.subtract_num("e", 10)
1229 d.subtract_num("f", 10, 15)
1230 self.failUnlessEqual(d, {"a": 1, "b": 2, "c": 25, "d": 30,
1233 self.failUnlessEqual(d.sum(), sum([1, 2, 25, 30, -10, 5]))
1235 d = dictutil.NumDict()
1239 self.failUnlessEqual(d, {"a": 2, "b": 6})
1243 self.failUnlessEqual(d, {"a": 1, "b": 6, "c": -1, "d": 4})
1244 self.failUnlessEqual(d.items_sorted_by_key(),
1245 [("a", 1), ("b", 6), ("c", -1), ("d", 4)])
1246 self.failUnlessEqual(d.items_sorted_by_value(),
1247 [("c", -1), ("a", 1), ("d", 4), ("b", 6)])
1248 self.failUnlessEqual(d.item_with_largest_value(), ("b", 6))
1250 d = dictutil.NumDict({"a": 1, "b": 2})
1251 self.failUnlessEqual(repr(d), "{'a': 1, 'b': 2}")
1252 self.failUnless("a" in d)
1254 d2 = dictutil.NumDict({"c": 3, "d": 4})
1255 self.failUnless(d != d2)
1256 self.failUnless(d2 > d)
1257 self.failUnless(d2 >= d)
1258 self.failUnless(d <= d2)
1259 self.failUnless(d < d2)
1260 self.failUnlessEqual(d["a"], 1)
1261 self.failUnlessEqual(sorted(list([k for k in d])), ["a","b"])
1264 self.failUnlessRaises(TypeError, eq, d, "not a dict")
1267 self.failUnlessEqual(d, d3)
1268 self.failUnless(isinstance(d3, dictutil.NumDict))
1270 d4 = d.fromkeys(["a","b"], 5)
1271 self.failUnlessEqual(d4, {"a": 5, "b": 5})
1273 self.failUnlessEqual(d.get("a"), 1)
1274 self.failUnlessEqual(d.get("c"), 0)
1275 self.failUnlessEqual(d.get("c", 5), 5)
1276 self.failUnlessEqual(sorted(list(d.items())),
1277 [("a", 1), ("b", 2)])
1278 self.failUnlessEqual(sorted(list(d.iteritems())),
1279 [("a", 1), ("b", 2)])
1280 self.failUnlessEqual(sorted(d.keys()), ["a", "b"])
1281 self.failUnlessEqual(sorted(d.values()), [1, 2])
1282 self.failUnless(d.has_key("a"))
1283 self.failIf(d.has_key("c"))
1285 x = d.setdefault("c", 3)
1286 self.failUnlessEqual(x, 3)
1287 self.failUnlessEqual(d["c"], 3)
1288 x = d.setdefault("c", 5)
1289 self.failUnlessEqual(x, 3)
1290 self.failUnlessEqual(d["c"], 3)
1294 self.failUnless(x in [("a", 1), ("b", 2)])
1296 self.failUnless(x in [("a", 1), ("b", 2)])
1297 self.failUnlessRaises(KeyError, d.popitem)
1300 d.update({"c": 4, "d": 5})
1301 self.failUnlessEqual(d, {"c": 4, "d": 5})
1303 def test_del_if_present(self):
1304 d = {1: "a", 2: "b"}
1305 dictutil.del_if_present(d, 1)
1306 dictutil.del_if_present(d, 3)
1307 self.failUnlessEqual(d, {2: "b"})
1309 def test_valueordereddict(self):
1310 d = dictutil.ValueOrderedDict()
1315 self.failUnlessEqual(d, {"a": 3, "b": 2, "c": 1})
1316 self.failUnlessEqual(d.items(), [("c", 1), ("b", 2), ("a", 3)])
1317 self.failUnlessEqual(d.values(), [1, 2, 3])
1318 self.failUnlessEqual(d.keys(), ["c", "b", "a"])
1319 self.failUnlessEqual(repr(d), "<ValueOrderedDict {c: 1, b: 2, a: 3}>")
1322 self.failIf(d == {"a": 4})
1323 self.failUnless(d != {"a": 4})
1325 x = d.setdefault("d", 0)
1326 self.failUnlessEqual(x, 0)
1327 self.failUnlessEqual(d["d"], 0)
1328 x = d.setdefault("d", -1)
1329 self.failUnlessEqual(x, 0)
1330 self.failUnlessEqual(d["d"], 0)
1332 x = d.remove("e", "default", False)
1333 self.failUnlessEqual(x, "default")
1334 self.failUnlessRaises(KeyError, d.remove, "e", "default", True)
1335 x = d.remove("d", 5)
1336 self.failUnlessEqual(x, 0)
1338 x = d.__getitem__("c")
1339 self.failUnlessEqual(x, 1)
1340 x = d.__getitem__("e", "default", False)
1341 self.failUnlessEqual(x, "default")
1342 self.failUnlessRaises(KeyError, d.__getitem__, "e", "default", True)
1344 self.failUnlessEqual(d.popitem(), ("c", 1))
1345 self.failUnlessEqual(d.popitem(), ("b", 2))
1346 self.failUnlessEqual(d.popitem(), ("a", 3))
1347 self.failUnlessRaises(KeyError, d.popitem)
1349 d = dictutil.ValueOrderedDict({"a": 3, "b": 2, "c": 1})
1350 x = d.pop("d", "default", False)
1351 self.failUnlessEqual(x, "default")
1352 self.failUnlessRaises(KeyError, d.pop, "d", "default", True)
1354 self.failUnlessEqual(x, 2)
1355 self.failUnlessEqual(d.items(), [("c", 1), ("a", 3)])
1357 d = dictutil.ValueOrderedDict({"a": 3, "b": 2, "c": 1})
1358 x = d.pop_from_list(1) # pop the second item, b/2
1359 self.failUnlessEqual(x, "b")
1360 self.failUnlessEqual(d.items(), [("c", 1), ("a", 3)])
1362 def test_auxdict(self):
1363 d = dictutil.AuxValueDict()
1364 # we put the serialized form in the auxdata
1365 d.set_with_aux("key", ("filecap", "metadata"), "serialized")
1367 self.failUnlessEqual(d.keys(), ["key"])
1368 self.failUnlessEqual(d["key"], ("filecap", "metadata"))
1369 self.failUnlessEqual(d.get_aux("key"), "serialized")
1370 def _get_missing(key):
1372 self.failUnlessRaises(KeyError, _get_missing, "nonkey")
1373 self.failUnlessEqual(d.get("nonkey"), None)
1374 self.failUnlessEqual(d.get("nonkey", "nonvalue"), "nonvalue")
1375 self.failUnlessEqual(d.get_aux("nonkey"), None)
1376 self.failUnlessEqual(d.get_aux("nonkey", "nonvalue"), "nonvalue")
1378 d["key"] = ("filecap2", "metadata2")
1379 self.failUnlessEqual(d["key"], ("filecap2", "metadata2"))
1380 self.failUnlessEqual(d.get_aux("key"), None)
1382 d.set_with_aux("key2", "value2", "aux2")
1383 self.failUnlessEqual(sorted(d.keys()), ["key", "key2"])
1385 self.failUnlessEqual(d.keys(), ["key"])
1386 self.failIf("key2" in d)
1387 self.failUnlessRaises(KeyError, _get_missing, "key2")
1388 self.failUnlessEqual(d.get("key2"), None)
1389 self.failUnlessEqual(d.get_aux("key2"), None)
1390 d["key2"] = "newvalue2"
1391 self.failUnlessEqual(d.get("key2"), "newvalue2")
1392 self.failUnlessEqual(d.get_aux("key2"), None)
1394 d = dictutil.AuxValueDict({1:2,3:4})
1395 self.failUnlessEqual(sorted(d.keys()), [1,3])
1396 self.failUnlessEqual(d[1], 2)
1397 self.failUnlessEqual(d.get_aux(1), None)
1399 d = dictutil.AuxValueDict([ (1,2), (3,4) ])
1400 self.failUnlessEqual(sorted(d.keys()), [1,3])
1401 self.failUnlessEqual(d[1], 2)
1402 self.failUnlessEqual(d.get_aux(1), None)
1404 d = dictutil.AuxValueDict(one=1, two=2)
1405 self.failUnlessEqual(sorted(d.keys()), ["one","two"])
1406 self.failUnlessEqual(d["one"], 1)
1407 self.failUnlessEqual(d.get_aux("one"), None)
1409 class Pipeline(unittest.TestCase):
1410 def pause(self, *args, **kwargs):
1411 d = defer.Deferred()
1412 self.calls.append( (d, args, kwargs) )
1415 def failUnlessCallsAre(self, expected):
1418 self.failUnlessEqual(len(self.calls), len(expected), self.calls)
1419 for i,c in enumerate(self.calls):
1420 self.failUnlessEqual(c[1:], expected[i], str(i))
1422 def test_basic(self):
1425 p = pipeline.Pipeline(100)
1427 d = p.flush() # fires immediately
1428 d.addCallbacks(finished.append, log.err)
1429 self.failUnlessEqual(len(finished), 1)
1432 d = p.add(10, self.pause, "one")
1433 # the call should start right away, and our return Deferred should
1435 d.addCallbacks(finished.append, log.err)
1436 self.failUnlessEqual(len(finished), 1)
1437 self.failUnlessEqual(finished[0], None)
1438 self.failUnlessCallsAre([ ( ("one",) , {} ) ])
1439 self.failUnlessEqual(p.gauge, 10)
1444 d = p.add(20, self.pause, "two", kw=2)
1445 # pipeline: [one, two]
1447 # the call and the Deferred should fire right away
1448 d.addCallbacks(finished.append, log.err)
1449 self.failUnlessEqual(len(finished), 1)
1450 self.failUnlessEqual(finished[0], None)
1451 self.failUnlessCallsAre([ ( ("one",) , {} ),
1452 ( ("two",) , {"kw": 2} ),
1454 self.failUnlessEqual(p.gauge, 30)
1456 self.calls[0][0].callback("one-result")
1458 self.failUnlessEqual(p.gauge, 20)
1461 d = p.add(90, self.pause, "three", "posarg1")
1462 # pipeline: [two, three]
1465 fd.addCallbacks(flushed.append, log.err)
1466 self.failUnlessEqual(flushed, [])
1468 # the call will be made right away, but the return Deferred will not,
1469 # because the pipeline is now full.
1470 d.addCallbacks(finished.append, log.err)
1471 self.failUnlessEqual(len(finished), 0)
1472 self.failUnlessCallsAre([ ( ("one",) , {} ),
1473 ( ("two",) , {"kw": 2} ),
1474 ( ("three", "posarg1"), {} ),
1476 self.failUnlessEqual(p.gauge, 110)
1478 self.failUnlessRaises(pipeline.SingleFileError, p.add, 10, self.pause)
1480 # retiring either call will unblock the pipeline, causing the #3
1482 self.calls[2][0].callback("three-result")
1485 self.failUnlessEqual(len(finished), 1)
1486 self.failUnlessEqual(finished[0], None)
1487 self.failUnlessEqual(flushed, [])
1489 # retiring call#2 will finally allow the flush() Deferred to fire
1490 self.calls[1][0].callback("two-result")
1491 self.failUnlessEqual(len(flushed), 1)
1493 def test_errors(self):
1495 p = pipeline.Pipeline(100)
1497 d1 = p.add(200, self.pause, "one")
1501 d1.addBoth(finished.append)
1502 self.failUnlessEqual(finished, [])
1505 d2.addBoth(flushed.append)
1506 self.failUnlessEqual(flushed, [])
1508 self.calls[0][0].errback(ValueError("oops"))
1510 self.failUnlessEqual(len(finished), 1)
1512 self.failUnless(isinstance(f, Failure))
1513 self.failUnless(f.check(pipeline.PipelineError))
1514 self.failUnlessIn("PipelineError", str(f.value))
1515 self.failUnlessIn("ValueError", str(f.value))
1517 self.failUnless("ValueError" in r, r)
1519 self.failUnless(f2.check(ValueError))
1521 self.failUnlessEqual(len(flushed), 1)
1523 self.failUnless(isinstance(f, Failure))
1524 self.failUnless(f.check(pipeline.PipelineError))
1526 self.failUnless(f2.check(ValueError))
1528 # now that the pipeline is in the failed state, any new calls will
1531 d3 = p.add(20, self.pause, "two")
1534 d3.addBoth(finished.append)
1535 self.failUnlessEqual(len(finished), 1)
1537 self.failUnless(isinstance(f, Failure))
1538 self.failUnless(f.check(pipeline.PipelineError))
1540 self.failUnless("ValueError" in r, r)
1542 self.failUnless(f2.check(ValueError))
1546 d4.addBoth(flushed.append)
1547 self.failUnlessEqual(len(flushed), 1)
1549 self.failUnless(isinstance(f, Failure))
1550 self.failUnless(f.check(pipeline.PipelineError))
1552 self.failUnless(f2.check(ValueError))
1554 def test_errors2(self):
1556 p = pipeline.Pipeline(100)
1558 d1 = p.add(10, self.pause, "one")
1559 d2 = p.add(20, self.pause, "two")
1560 d3 = p.add(30, self.pause, "three")
1563 # one call fails, then the second one succeeds: make sure
1564 # ExpandableDeferredList tolerates the second one
1567 d4.addBoth(flushed.append)
1568 self.failUnlessEqual(flushed, [])
1570 self.calls[0][0].errback(ValueError("oops"))
1571 self.failUnlessEqual(len(flushed), 1)
1573 self.failUnless(isinstance(f, Failure))
1574 self.failUnless(f.check(pipeline.PipelineError))
1576 self.failUnless(f2.check(ValueError))
1578 self.calls[1][0].callback("two-result")
1579 self.calls[2][0].errback(ValueError("three-error"))
1583 class SampleError(Exception):
1586 class Log(unittest.TestCase):
1588 if not hasattr(self, "flushLoggedErrors"):
1589 # without flushLoggedErrors, we can't get rid of the
1590 # twisted.log.err that tahoe_log records, so we can't keep this
1591 # test from [ERROR]ing
1592 raise unittest.SkipTest("needs flushLoggedErrors from Twisted-2.5.0")
1594 raise SampleError("simple sample")
1597 tahoe_log.err(format="intentional sample error",
1598 failure=f, level=tahoe_log.OPERATIONAL, umid="wO9UoQ")
1599 self.flushLoggedErrors(SampleError)
1603 # this is a simple+inefficient form of util.spans.Spans . We compare the
1604 # behavior of this reference model against the real (efficient) form.
1606 def __init__(self, _span_or_start=None, length=None):
1608 if length is not None:
1609 for i in range(_span_or_start, _span_or_start+length):
1611 elif _span_or_start:
1612 for (start,length) in _span_or_start:
1613 self.add(start, length)
1615 def add(self, start, length):
1616 for i in range(start, start+length):
1620 def remove(self, start, length):
1621 for i in range(start, start+length):
1622 self._have.discard(i)
1626 return sorted(self._have)
1629 items = sorted(self._have)
1633 if prevstart is None:
1634 prevstart = prevend = i
1639 yield (prevstart, prevend-prevstart+1)
1640 prevstart = prevend = i
1641 if prevstart is not None:
1642 yield (prevstart, prevend-prevstart+1)
1644 def __nonzero__(self): # this gets us bool()
1648 return len(self._have)
1650 def __add__(self, other):
1651 s = self.__class__(self)
1652 for (start, length) in other:
1653 s.add(start, length)
1656 def __sub__(self, other):
1657 s = self.__class__(self)
1658 for (start, length) in other:
1659 s.remove(start, length)
1662 def __iadd__(self, other):
1663 for (start, length) in other:
1664 self.add(start, length)
1667 def __isub__(self, other):
1668 for (start, length) in other:
1669 self.remove(start, length)
1672 def __and__(self, other):
1673 s = self.__class__()
1674 for i in other.each():
1679 def __contains__(self, (start,length)):
1680 for i in range(start, start+length):
1681 if i not in self._have:
1685 class ByteSpans(unittest.TestCase):
1686 def test_basic(self):
1688 self.failUnlessEqual(list(s), [])
1690 self.failIf((0,1) in s)
1691 self.failUnlessEqual(s.len(), 0)
1693 s1 = Spans(3, 4) # 3,4,5,6
1696 s1 = Spans(3L, 4L) # 3,4,5,6
1702 s2.add(10,2) # 10,11
1704 self.failUnless((10,1) in s2)
1705 self.failIf((10,1) in s1)
1706 self.failUnlessEqual(list(s2.each()), [3,4,5,6,10,11])
1707 self.failUnlessEqual(s2.len(), 6)
1709 s2.add(15,2).add(20,2)
1710 self.failUnlessEqual(list(s2.each()), [3,4,5,6,10,11,15,16,20,21])
1711 self.failUnlessEqual(s2.len(), 10)
1713 s2.remove(4,3).remove(15,1)
1714 self.failUnlessEqual(list(s2.each()), [3,10,11,16,20,21])
1715 self.failUnlessEqual(s2.len(), 6)
1717 s1 = SimpleSpans(3, 4) # 3 4 5 6
1718 s2 = SimpleSpans(5, 4) # 5 6 7 8
1720 self.failUnlessEqual(list(i.each()), [5, 6])
1722 def _check1(self, s):
1723 self.failUnlessEqual(list(s), [(3,4)])
1725 self.failUnlessEqual(s.len(), 4)
1726 self.failIf((0,1) in s)
1727 self.failUnless((3,4) in s)
1728 self.failUnless((3,1) in s)
1729 self.failUnless((5,2) in s)
1730 self.failUnless((6,1) in s)
1731 self.failIf((6,2) in s)
1732 self.failIf((7,1) in s)
1733 self.failUnlessEqual(list(s.each()), [3,4,5,6])
1735 def test_large(self):
1736 s = Spans(4, 2**65) # don't do this with a SimpleSpans
1737 self.failUnlessEqual(list(s), [(4, 2**65)])
1739 self.failUnlessEqual(s.len(), 2**65)
1740 self.failIf((0,1) in s)
1741 self.failUnless((4,2) in s)
1742 self.failUnless((2**65,2) in s)
1744 def test_math(self):
1745 s1 = Spans(0, 10) # 0,1,2,3,4,5,6,7,8,9
1746 s2 = Spans(5, 3) # 5,6,7
1747 s3 = Spans(8, 4) # 8,9,10,11
1750 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,8,9])
1752 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7])
1754 self.failUnlessEqual(list(s.each()), [5,6,7])
1756 self.failUnlessEqual(list(s.each()), [5,6,7])
1758 self.failUnlessEqual(list(s.each()), [5,6,7])
1760 self.failUnlessEqual(list(s.each()), [8,9])
1762 self.failUnlessEqual(list(s.each()), [8,9])
1764 self.failUnlessEqual(list(s.each()), [])
1766 self.failUnlessEqual(list(s.each()), [])
1768 self.failUnlessEqual(list(s.each()), [])
1770 self.failUnlessEqual(list(s.each()), [])
1773 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7,8,9])
1775 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7,8,9,10,11])
1777 self.failUnlessEqual(list(s.each()), [5,6,7,8,9,10,11])
1781 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,8,9])
1784 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7])
1787 self.failUnlessEqual(list(s.each()), [5,6,7])
1791 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7,8,9])
1794 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7,8,9,10,11])
1797 self.failUnlessEqual(list(s.each()), [5,6,7,8,9,10,11])
1799 def test_random(self):
1800 # attempt to increase coverage of corner cases by comparing behavior
1801 # of a simple-but-slow model implementation against the
1802 # complex-but-fast actual implementation, in a large number of random
1806 s1 = S1(); s2 = S2()
1808 def _create(subseed):
1809 ns1 = S1(); ns2 = S2()
1811 what = _hash(subseed+str(i)).hexdigest()
1812 start = int(what[2:4], 16)
1813 length = max(1,int(what[5:6], 16))
1814 ns1.add(start, length); ns2.add(start, length)
1818 for i in range(1000):
1819 what = _hash(seed+str(i)).hexdigest()
1822 start = int(what[2:4], 16)
1823 length = max(1,int(what[5:6], 16))
1826 if subop in "01234":
1827 s1 = S1(); s2 = S2()
1828 elif subop in "5678":
1829 s1 = S1(start, length); s2 = S2(start, length)
1831 s1 = S1(s1); s2 = S2(s2)
1832 #print "s2 = %s" % s2.dump()
1834 #print "s2.add(%d,%d)" % (start, length)
1835 s1.add(start, length); s2.add(start, length)
1837 #print "s2.remove(%d,%d)" % (start, length)
1838 s1.remove(start, length); s2.remove(start, length)
1840 ns1, ns2 = _create(what[7:11])
1841 #print "s2 + %s" % ns2.dump()
1842 s1 = s1 + ns1; s2 = s2 + ns2
1844 ns1, ns2 = _create(what[7:11])
1845 #print "%s - %s" % (s2.dump(), ns2.dump())
1846 s1 = s1 - ns1; s2 = s2 - ns2
1848 ns1, ns2 = _create(what[7:11])
1849 #print "s2 += %s" % ns2.dump()
1850 s1 += ns1; s2 += ns2
1852 ns1, ns2 = _create(what[7:11])
1853 #print "%s -= %s" % (s2.dump(), ns2.dump())
1854 s1 -= ns1; s2 -= ns2
1856 ns1, ns2 = _create(what[7:11])
1857 #print "%s &= %s" % (s2.dump(), ns2.dump())
1858 s1 = s1 & ns1; s2 = s2 & ns2
1859 #print "s2 now %s" % s2.dump()
1860 self.failUnlessEqual(list(s1.each()), list(s2.each()))
1861 self.failUnlessEqual(s1.len(), s2.len())
1862 self.failUnlessEqual(bool(s1), bool(s2))
1863 self.failUnlessEqual(list(s1), list(s2))
1865 what = _hash(what[12:14]+str(j)).hexdigest()
1866 start = int(what[2:4], 16)
1867 length = max(1, int(what[5:6], 16))
1868 span = (start, length)
1869 self.failUnlessEqual(bool(span in s1), bool(span in s2))
1875 # s.add(start,length) : returns s
1876 # s.remove(start,length)
1877 # s.each() -> list of byte offsets, mostly for testing
1878 # list(s) -> list of (start,length) tuples, one per span
1879 # (start,length) in s -> True if (start..start+length-1) are all members
1880 # NOT equivalent to x in list(s)
1881 # s.len() -> number of bytes, for testing, bool(), and accounting/limiting
1882 # bool(s) (__nonzeron__)
1883 # s = s1+s2, s1-s2, +=s1, -=s1
1885 def test_overlap(self):
1890 self._test_overlap(a,b,c,d)
1892 def _test_overlap(self, a, b, c, d):
1893 s1 = set(range(a,a+b))
1894 s2 = set(range(c,c+d))
1896 #self._show_overlap(s1, "1")
1897 #self._show_overlap(s2, "2")
1898 o = overlap(a,b,c,d)
1899 expected = s1.intersection(s2)
1901 self.failUnlessEqual(o, None)
1904 so = set(range(start,start+length))
1905 #self._show(so, "o")
1906 self.failUnlessEqual(so, expected)
1908 def _show_overlap(self, s, c):
1912 for i in range(max(s)):
1919 def extend(s, start, length, fill):
1920 if len(s) >= start+length:
1922 assert len(fill) == 1
1923 return s + fill*(start+length-len(s))
1925 def replace(s, start, data):
1926 assert len(s) >= start+len(data)
1927 return s[:start] + data + s[start+len(data):]
1929 class SimpleDataSpans:
1930 def __init__(self, other=None):
1931 self.missing = "" # "1" where missing, "0" where found
1934 for (start, data) in other.get_chunks():
1935 self.add(start, data)
1937 def __nonzero__(self): # this gets us bool()
1940 return len(self.missing.replace("1", ""))
1942 return [i for (i,c) in enumerate(self.missing) if c == "0"]
1943 def _have(self, start, length):
1944 m = self.missing[start:start+length]
1945 if not m or len(m)<length or int(m):
1948 def get_chunks(self):
1949 for i in self._dump():
1950 yield (i, self.data[i])
1951 def get_spans(self):
1952 return SimpleSpans([(start,len(data))
1953 for (start,data) in self.get_chunks()])
1954 def get(self, start, length):
1955 if self._have(start, length):
1956 return self.data[start:start+length]
1958 def pop(self, start, length):
1959 data = self.get(start, length)
1961 self.remove(start, length)
1963 def remove(self, start, length):
1964 self.missing = replace(extend(self.missing, start, length, "1"),
1966 def add(self, start, data):
1967 self.missing = replace(extend(self.missing, start, len(data), "1"),
1968 start, "0"*len(data))
1969 self.data = replace(extend(self.data, start, len(data), " "),
1973 class StringSpans(unittest.TestCase):
1974 def do_basic(self, klass):
1976 self.failUnlessEqual(ds.len(), 0)
1977 self.failUnlessEqual(list(ds._dump()), [])
1978 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 0)
1980 self.failUnlessEqual(ds.get(0, 4), None)
1981 self.failUnlessEqual(ds.pop(0, 4), None)
1985 self.failUnlessEqual(ds.len(), 4)
1986 self.failUnlessEqual(list(ds._dump()), [2,3,4,5])
1987 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 4)
1989 self.failUnless((2,2) in s)
1990 self.failUnlessEqual(ds.get(0, 4), None)
1991 self.failUnlessEqual(ds.pop(0, 4), None)
1992 self.failUnlessEqual(ds.get(4, 4), None)
1995 self.failUnlessEqual(ds2.len(), 4)
1996 self.failUnlessEqual(list(ds2._dump()), [2,3,4,5])
1997 self.failUnlessEqual(sum([len(d) for (s,d) in ds2.get_chunks()]), 4)
1998 self.failUnlessEqual(ds2.get(0, 4), None)
1999 self.failUnlessEqual(ds2.pop(0, 4), None)
2000 self.failUnlessEqual(ds2.pop(2, 3), "fou")
2001 self.failUnlessEqual(sum([len(d) for (s,d) in ds2.get_chunks()]), 1)
2002 self.failUnlessEqual(ds2.get(2, 3), None)
2003 self.failUnlessEqual(ds2.get(5, 1), "r")
2004 self.failUnlessEqual(ds.get(2, 3), "fou")
2005 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 4)
2008 self.failUnlessEqual(ds.len(), 6)
2009 self.failUnlessEqual(list(ds._dump()), [0,1,2,3,4,5])
2010 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 6)
2011 self.failUnlessEqual(ds.get(0, 4), "23fo")
2012 self.failUnlessEqual(ds.pop(0, 4), "23fo")
2013 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 2)
2014 self.failUnlessEqual(ds.get(0, 4), None)
2015 self.failUnlessEqual(ds.pop(0, 4), None)
2020 self.failUnlessEqual(ds.get(2, 4), "fear")
2025 self.failUnlessEqual(ds.get(2L, 4L), "fear")
2028 def do_scan(self, klass):
2029 # do a test with gaps and spans of size 1 and 2
2030 # left=(1,11) * right=(1,11) * gapsize=(1,2)
2031 # 111, 112, 121, 122, 211, 212, 221, 222
2040 # 11 1 1 11 11 11 1 1 111
2041 # 0123456789012345678901234567
2042 # abcdefghijklmnopqrstuvwxyz-=
2043 pieces = [(1, "bc"),
2053 p_elements = set([1,2,4,7,9,10,12,13,16,17,20,22,25,26,27])
2054 S = "abcdefghijklmnopqrstuvwxyz-="
2055 # TODO: when adding data, add capital letters, to make sure we aren't
2056 # just leaving the old data in place
2060 for start, data in pieces:
2065 d = "".join([((i not in p) and " " or S[i]) for i in range(l)])
2069 for start in range(0, l):
2070 for end in range(start+1, l):
2071 # add [start-end) to the baseline
2072 which = "%d-%d" % (start, end-1)
2073 p_added = set(range(start, end))
2077 print dump(b), which
2078 add = klass(); add.add(start, S[start:end])
2080 b.add(start, S[start:end])
2083 # check that the new span is there
2084 d = b.get(start, end-start)
2085 self.failUnlessEqual(d, S[start:end], which)
2086 # check that all the original pieces are still there
2087 for t_start, t_data in pieces:
2089 self.failUnlessEqual(b.get(t_start, t_len),
2090 S[t_start:t_start+t_len],
2091 "%s %d+%d" % (which, t_start, t_len))
2092 # check that a lot of subspans are mostly correct
2093 for t_start in range(l):
2094 for t_len in range(1,4):
2095 d = b.get(t_start, t_len)
2097 which2 = "%s+(%d-%d)" % (which, t_start,
2099 self.failUnlessEqual(d, S[t_start:t_start+t_len],
2101 # check that removing a subspan gives the right value
2103 b2.remove(t_start, t_len)
2104 removed = set(range(t_start, t_start+t_len))
2106 exp = (((i in p_elements) or (i in p_added))
2107 and (i not in removed))
2108 which2 = "%s-(%d-%d)" % (which, t_start,
2110 self.failUnlessEqual(bool(b2.get(i, 1)), exp,
2113 def test_test(self):
2114 self.do_basic(SimpleDataSpans)
2115 self.do_scan(SimpleDataSpans)
2117 def test_basic(self):
2118 self.do_basic(DataSpans)
2119 self.do_scan(DataSpans)
2121 def test_random(self):
2122 # attempt to increase coverage of corner cases by comparing behavior
2123 # of a simple-but-slow model implementation against the
2124 # complex-but-fast actual implementation, in a large number of random
2126 S1 = SimpleDataSpans
2128 s1 = S1(); s2 = S2()
2130 def _randstr(length, seed):
2133 while created < length:
2134 piece = _hash(seed + str(created)).hexdigest()
2135 pieces.append(piece)
2136 created += len(piece)
2137 return "".join(pieces)[:length]
2138 def _create(subseed):
2139 ns1 = S1(); ns2 = S2()
2141 what = _hash(subseed+str(i)).hexdigest()
2142 start = int(what[2:4], 16)
2143 length = max(1,int(what[5:6], 16))
2144 ns1.add(start, _randstr(length, what[7:9]));
2145 ns2.add(start, _randstr(length, what[7:9]))
2149 for i in range(1000):
2150 what = _hash(seed+str(i)).hexdigest()
2153 start = int(what[2:4], 16)
2154 length = max(1,int(what[5:6], 16))
2157 if subop in "0123456":
2158 s1 = S1(); s2 = S2()
2160 s1, s2 = _create(what[7:11])
2161 #print "s2 = %s" % list(s2._dump())
2162 elif op in "123456":
2163 #print "s2.add(%d,%d)" % (start, length)
2164 s1.add(start, _randstr(length, what[7:9]));
2165 s2.add(start, _randstr(length, what[7:9]))
2166 elif op in "789abc":
2167 #print "s2.remove(%d,%d)" % (start, length)
2168 s1.remove(start, length); s2.remove(start, length)
2170 #print "s2.pop(%d,%d)" % (start, length)
2171 d1 = s1.pop(start, length); d2 = s2.pop(start, length)
2172 self.failUnlessEqual(d1, d2)
2173 #print "s1 now %s" % list(s1._dump())
2174 #print "s2 now %s" % list(s2._dump())
2175 self.failUnlessEqual(s1.len(), s2.len())
2176 self.failUnlessEqual(list(s1._dump()), list(s2._dump()))
2177 for j in range(100):
2178 what = _hash(what[12:14]+str(j)).hexdigest()
2179 start = int(what[2:4], 16)
2180 length = max(1, int(what[5:6], 16))
2181 d1 = s1.get(start, length); d2 = s2.get(start, length)
2182 self.failUnlessEqual(d1, d2, "%d+%d" % (start, length))