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)
440 f.seek(0, os.SEEK_END)
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 # we tolerate used==0 for a Travis-CI bug, see #2290
523 self.failUnless(disk['used'] >= 0, disk['used'])
524 self.failUnless(disk['free_for_root'] > 0, disk['free_for_root'])
525 self.failUnless(disk['free_for_nonroot'] > 0, disk['free_for_nonroot'])
526 self.failUnless(disk['avail'] > 0, disk['avail'])
528 def test_disk_stats_avail_nonnegative(self):
529 # This test will spuriously fail if you have more than 2^128
530 # bytes of available space on your filesystem.
531 disk = fileutil.get_disk_stats('.', 2**128)
532 self.failUnlessEqual(disk['avail'], 0)
534 class PollMixinTests(unittest.TestCase):
536 self.pm = pollmixin.PollMixin()
538 def test_PollMixin_True(self):
539 d = self.pm.poll(check_f=lambda : True,
543 def test_PollMixin_False_then_True(self):
544 i = iter([False, True])
545 d = self.pm.poll(check_f=i.next,
549 def test_timeout(self):
550 d = self.pm.poll(check_f=lambda: False,
554 self.fail("poll should have failed, not returned %s" % (res,))
556 f.trap(pollmixin.TimeoutError)
557 return None # success
558 d.addCallbacks(_suc, _err)
561 class DeferredUtilTests(unittest.TestCase):
562 def test_gather_results(self):
563 d1 = defer.Deferred()
564 d2 = defer.Deferred()
565 res = deferredutil.gatherResults([d1, d2])
566 d1.errback(ValueError("BAD"))
568 self.fail("Should have errbacked, not resulted in %s" % (res,))
570 thef.trap(ValueError)
571 res.addCallbacks(_callb, _errb)
574 def test_success(self):
575 d1, d2 = defer.Deferred(), defer.Deferred()
578 dlss = deferredutil.DeferredListShouldSucceed([d1,d2])
579 dlss.addCallbacks(good.append, bad.append)
582 self.failUnlessEqual(good, [[1,2]])
583 self.failUnlessEqual(bad, [])
585 def test_failure(self):
586 d1, d2 = defer.Deferred(), defer.Deferred()
589 dlss = deferredutil.DeferredListShouldSucceed([d1,d2])
590 dlss.addCallbacks(good.append, bad.append)
591 d1.addErrback(lambda _ignore: None)
592 d2.addErrback(lambda _ignore: None)
594 d2.errback(ValueError())
595 self.failUnlessEqual(good, [])
596 self.failUnlessEqual(len(bad), 1)
598 self.failUnless(isinstance(f, Failure))
599 self.failUnless(f.check(ValueError))
601 class HashUtilTests(unittest.TestCase):
603 def test_random_key(self):
604 k = hashutil.random_key()
605 self.failUnlessEqual(len(k), hashutil.KEYLEN)
607 def test_sha256d(self):
608 h1 = hashutil.tagged_hash("tag1", "value")
609 h2 = hashutil.tagged_hasher("tag1")
613 self.failUnlessEqual(h1, h2a)
614 self.failUnlessEqual(h2a, h2b)
616 def test_sha256d_truncated(self):
617 h1 = hashutil.tagged_hash("tag1", "value", 16)
618 h2 = hashutil.tagged_hasher("tag1", 16)
621 self.failUnlessEqual(len(h1), 16)
622 self.failUnlessEqual(len(h2), 16)
623 self.failUnlessEqual(h1, h2)
626 h1 = hashutil.convergence_hash(3, 10, 1000, "data", "secret")
627 h2 = hashutil.convergence_hasher(3, 10, 1000, "secret")
630 self.failUnlessEqual(h1, h2)
632 def test_hashers(self):
633 h1 = hashutil.block_hash("foo")
634 h2 = hashutil.block_hasher()
636 self.failUnlessEqual(h1, h2.digest())
638 h1 = hashutil.uri_extension_hash("foo")
639 h2 = hashutil.uri_extension_hasher()
641 self.failUnlessEqual(h1, h2.digest())
643 h1 = hashutil.plaintext_hash("foo")
644 h2 = hashutil.plaintext_hasher()
646 self.failUnlessEqual(h1, h2.digest())
648 h1 = hashutil.crypttext_hash("foo")
649 h2 = hashutil.crypttext_hasher()
651 self.failUnlessEqual(h1, h2.digest())
653 h1 = hashutil.crypttext_segment_hash("foo")
654 h2 = hashutil.crypttext_segment_hasher()
656 self.failUnlessEqual(h1, h2.digest())
658 h1 = hashutil.plaintext_segment_hash("foo")
659 h2 = hashutil.plaintext_segment_hasher()
661 self.failUnlessEqual(h1, h2.digest())
663 def test_timing_safe_compare(self):
664 self.failUnless(hashutil.timing_safe_compare("a", "a"))
665 self.failUnless(hashutil.timing_safe_compare("ab", "ab"))
666 self.failIf(hashutil.timing_safe_compare("a", "b"))
667 self.failIf(hashutil.timing_safe_compare("a", "aa"))
669 def _testknown(self, hashf, expected_a, *args):
671 got_a = base32.b2a(got)
672 self.failUnlessEqual(got_a, expected_a)
674 def test_known_answers(self):
675 # assert backwards compatibility
676 self._testknown(hashutil.storage_index_hash, "qb5igbhcc5esa6lwqorsy7e6am", "")
677 self._testknown(hashutil.block_hash, "msjr5bh4evuh7fa3zw7uovixfbvlnstr5b65mrerwfnvjxig2jvq", "")
678 self._testknown(hashutil.uri_extension_hash, "wthsu45q7zewac2mnivoaa4ulh5xvbzdmsbuyztq2a5fzxdrnkka", "")
679 self._testknown(hashutil.plaintext_hash, "5lz5hwz3qj3af7n6e3arblw7xzutvnd3p3fjsngqjcb7utf3x3da", "")
680 self._testknown(hashutil.crypttext_hash, "itdj6e4njtkoiavlrmxkvpreosscssklunhwtvxn6ggho4rkqwga", "")
681 self._testknown(hashutil.crypttext_segment_hash, "aovy5aa7jej6ym5ikgwyoi4pxawnoj3wtaludjz7e2nb5xijb7aa", "")
682 self._testknown(hashutil.plaintext_segment_hash, "4fdgf6qruaisyukhqcmoth4t3li6bkolbxvjy4awwcpprdtva7za", "")
683 self._testknown(hashutil.convergence_hash, "3mo6ni7xweplycin6nowynw2we", 3, 10, 100, "", "converge")
684 self._testknown(hashutil.my_renewal_secret_hash, "ujhr5k5f7ypkp67jkpx6jl4p47pyta7hu5m527cpcgvkafsefm6q", "")
685 self._testknown(hashutil.my_cancel_secret_hash, "rjwzmafe2duixvqy6h47f5wfrokdziry6zhx4smew4cj6iocsfaa", "")
686 self._testknown(hashutil.file_renewal_secret_hash, "hzshk2kf33gzbd5n3a6eszkf6q6o6kixmnag25pniusyaulqjnia", "", "si")
687 self._testknown(hashutil.file_cancel_secret_hash, "bfciwvr6w7wcavsngxzxsxxaszj72dej54n4tu2idzp6b74g255q", "", "si")
688 self._testknown(hashutil.bucket_renewal_secret_hash, "e7imrzgzaoashsncacvy3oysdd2m5yvtooo4gmj4mjlopsazmvuq", "", "\x00"*20)
689 self._testknown(hashutil.bucket_cancel_secret_hash, "dvdujeyxeirj6uux6g7xcf4lvesk632aulwkzjar7srildvtqwma", "", "\x00"*20)
690 self._testknown(hashutil.hmac, "c54ypfi6pevb3nvo6ba42jtglpkry2kbdopqsi7dgrm4r7tw5sra", "tag", "")
691 self._testknown(hashutil.mutable_rwcap_key_hash, "6rvn2iqrghii5n4jbbwwqqsnqu", "iv", "wk")
692 self._testknown(hashutil.ssk_writekey_hash, "ykpgmdbpgbb6yqz5oluw2q26ye", "")
693 self._testknown(hashutil.ssk_write_enabler_master_hash, "izbfbfkoait4dummruol3gy2bnixrrrslgye6ycmkuyujnenzpia", "")
694 self._testknown(hashutil.ssk_write_enabler_hash, "fuu2dvx7g6gqu5x22vfhtyed7p4pd47y5hgxbqzgrlyvxoev62tq", "wk", "\x00"*20)
695 self._testknown(hashutil.ssk_pubkey_fingerprint_hash, "3opzw4hhm2sgncjx224qmt5ipqgagn7h5zivnfzqycvgqgmgz35q", "")
696 self._testknown(hashutil.ssk_readkey_hash, "vugid4as6qbqgeq2xczvvcedai", "")
697 self._testknown(hashutil.ssk_readkey_data_hash, "73wsaldnvdzqaf7v4pzbr2ae5a", "iv", "rk")
698 self._testknown(hashutil.ssk_storage_index_hash, "j7icz6kigb6hxrej3tv4z7ayym", "")
701 class Abbreviate(unittest.TestCase):
703 a = abbreviate.abbreviate_time
704 self.failUnlessEqual(a(None), "unknown")
705 self.failUnlessEqual(a(0), "0 seconds")
706 self.failUnlessEqual(a(1), "1 second")
707 self.failUnlessEqual(a(2), "2 seconds")
708 self.failUnlessEqual(a(119), "119 seconds")
710 self.failUnlessEqual(a(2*MIN), "2 minutes")
711 self.failUnlessEqual(a(60*MIN), "60 minutes")
712 self.failUnlessEqual(a(179*MIN), "179 minutes")
714 self.failUnlessEqual(a(180*MIN), "3 hours")
715 self.failUnlessEqual(a(4*HOUR), "4 hours")
718 self.failUnlessEqual(a(2*DAY), "2 days")
719 self.failUnlessEqual(a(2*MONTH), "2 months")
721 self.failUnlessEqual(a(5*YEAR), "5 years")
723 def test_space(self):
724 tests_si = [(None, "unknown"),
731 (20*1000, "20.00 kB"),
732 (1024*1024, "1.05 MB"),
733 (1000*1000, "1.00 MB"),
734 (1000*1000*1000, "1.00 GB"),
735 (1000*1000*1000*1000, "1.00 TB"),
736 (1000*1000*1000*1000*1000, "1.00 PB"),
737 (1000*1000*1000*1000*1000*1000, "1.00 EB"),
738 (1234567890123456789, "1.23 EB"),
740 for (x, expected) in tests_si:
741 got = abbreviate.abbreviate_space(x, SI=True)
742 self.failUnlessEqual(got, expected)
744 tests_base1024 = [(None, "unknown"),
751 (20*1024, "20.00 kiB"),
752 (1000*1000, "976.56 kiB"),
753 (1024*1024, "1.00 MiB"),
754 (1024*1024*1024, "1.00 GiB"),
755 (1024*1024*1024*1024, "1.00 TiB"),
756 (1000*1000*1000*1000*1000, "909.49 TiB"),
757 (1024*1024*1024*1024*1024, "1.00 PiB"),
758 (1024*1024*1024*1024*1024*1024, "1.00 EiB"),
759 (1234567890123456789, "1.07 EiB"),
761 for (x, expected) in tests_base1024:
762 got = abbreviate.abbreviate_space(x, SI=False)
763 self.failUnlessEqual(got, expected)
765 self.failUnlessEqual(abbreviate.abbreviate_space_both(1234567),
766 "(1.23 MB, 1.18 MiB)")
768 def test_parse_space(self):
769 p = abbreviate.parse_abbreviated_size
770 self.failUnlessEqual(p(""), None)
771 self.failUnlessEqual(p(None), None)
772 self.failUnlessEqual(p("123"), 123)
773 self.failUnlessEqual(p("123B"), 123)
774 self.failUnlessEqual(p("2K"), 2000)
775 self.failUnlessEqual(p("2kb"), 2000)
776 self.failUnlessEqual(p("2KiB"), 2048)
777 self.failUnlessEqual(p("10MB"), 10*1000*1000)
778 self.failUnlessEqual(p("10MiB"), 10*1024*1024)
779 self.failUnlessEqual(p("5G"), 5*1000*1000*1000)
780 self.failUnlessEqual(p("4GiB"), 4*1024*1024*1024)
781 self.failUnlessEqual(p("3TB"), 3*1000*1000*1000*1000)
782 self.failUnlessEqual(p("3TiB"), 3*1024*1024*1024*1024)
783 self.failUnlessEqual(p("6PB"), 6*1000*1000*1000*1000*1000)
784 self.failUnlessEqual(p("6PiB"), 6*1024*1024*1024*1024*1024)
785 self.failUnlessEqual(p("9EB"), 9*1000*1000*1000*1000*1000*1000)
786 self.failUnlessEqual(p("9EiB"), 9*1024*1024*1024*1024*1024*1024)
788 e = self.failUnlessRaises(ValueError, p, "12 cubits")
789 self.failUnlessIn("12 cubits", str(e))
790 e = self.failUnlessRaises(ValueError, p, "1 BB")
791 self.failUnlessIn("1 BB", str(e))
792 e = self.failUnlessRaises(ValueError, p, "fhtagn")
793 self.failUnlessIn("fhtagn", str(e))
795 class Limiter(unittest.TestCase):
796 timeout = 480 # This takes longer than 240 seconds on Francois's arm box.
798 def job(self, i, foo):
799 self.calls.append( (i, foo) )
800 self.simultaneous += 1
801 self.peak_simultaneous = max(self.simultaneous, self.peak_simultaneous)
804 self.simultaneous -= 1
805 d.callback("done %d" % i)
806 reactor.callLater(1.0, _done)
809 def bad_job(self, i, foo):
810 raise ValueError("bad_job %d" % i)
812 def test_limiter(self):
814 self.simultaneous = 0
815 self.peak_simultaneous = 0
816 l = limiter.ConcurrencyLimiter()
819 dl.append(l.add(self.job, i, foo=str(i)))
820 d = defer.DeferredList(dl, fireOnOneErrback=True)
822 self.failUnlessEqual(self.simultaneous, 0)
823 self.failUnless(self.peak_simultaneous <= 10)
824 self.failUnlessEqual(len(self.calls), 20)
826 self.failUnless( (i, str(i)) in self.calls)
830 def test_errors(self):
832 self.simultaneous = 0
833 self.peak_simultaneous = 0
834 l = limiter.ConcurrencyLimiter()
837 dl.append(l.add(self.job, i, foo=str(i)))
838 d2 = l.add(self.bad_job, 21, "21")
839 d = defer.DeferredList(dl, fireOnOneErrback=True)
842 for (success, result) in res:
843 self.failUnlessEqual(success, True)
844 results.append(result)
846 expected_results = ["done %d" % i for i in range(20)]
847 expected_results.sort()
848 self.failUnlessEqual(results, expected_results)
849 self.failUnless(self.peak_simultaneous <= 10)
850 self.failUnlessEqual(len(self.calls), 20)
852 self.failUnless( (i, str(i)) in self.calls)
854 self.fail("should have failed, not got %s" % (res,))
857 self.failUnless("bad_job 21" in str(f))
858 d2.addCallbacks(_good, _err)
860 d.addCallback(_most_done)
862 self.failUnlessEqual(self.simultaneous, 0)
863 self.failUnless(self.peak_simultaneous <= 10)
864 self.failUnlessEqual(len(self.calls), 20)
866 self.failUnless( (i, str(i)) in self.calls)
867 d.addCallback(_all_done)
870 class TimeFormat(unittest.TestCase):
871 def test_epoch(self):
872 return self._help_test_epoch()
874 def test_epoch_in_London(self):
875 # Europe/London is a particularly troublesome timezone. Nowadays, its
876 # offset from GMT is 0. But in 1970, its offset from GMT was 1.
877 # (Apparently in 1970 Britain had redefined standard time to be GMT+1
878 # and stayed in standard time all year round, whereas today
879 # Europe/London standard time is GMT and Europe/London Daylight
880 # Savings Time is GMT+1.) The current implementation of
881 # time_format.iso_utc_time_to_localseconds() breaks if the timezone is
882 # Europe/London. (As soon as this unit test is done then I'll change
883 # that implementation to something that works even in this case...)
884 origtz = os.environ.get('TZ')
885 os.environ['TZ'] = "Europe/London"
886 if hasattr(time, 'tzset'):
889 return self._help_test_epoch()
894 os.environ['TZ'] = origtz
895 if hasattr(time, 'tzset'):
898 def _help_test_epoch(self):
899 origtzname = time.tzname
900 s = time_format.iso_utc_time_to_seconds("1970-01-01T00:00:01")
901 self.failUnlessEqual(s, 1.0)
902 s = time_format.iso_utc_time_to_seconds("1970-01-01_00:00:01")
903 self.failUnlessEqual(s, 1.0)
904 s = time_format.iso_utc_time_to_seconds("1970-01-01 00:00:01")
905 self.failUnlessEqual(s, 1.0)
907 self.failUnlessEqual(time_format.iso_utc(1.0), "1970-01-01_00:00:01")
908 self.failUnlessEqual(time_format.iso_utc(1.0, sep=" "),
909 "1970-01-01 00:00:01")
912 isostr = time_format.iso_utc(now)
913 timestamp = time_format.iso_utc_time_to_seconds(isostr)
914 self.failUnlessEqual(int(timestamp), int(now))
918 self.failUnlessEqual(time_format.iso_utc(t=my_time),
919 "1970-01-01_00:00:01")
920 e = self.failUnlessRaises(ValueError,
921 time_format.iso_utc_time_to_seconds,
922 "invalid timestring")
923 self.failUnless("not a complete ISO8601 timestamp" in str(e))
924 s = time_format.iso_utc_time_to_seconds("1970-01-01_00:00:01.500")
925 self.failUnlessEqual(s, 1.5)
927 # Look for daylight-savings-related errors.
928 thatmomentinmarch = time_format.iso_utc_time_to_seconds("2009-03-20 21:49:02.226536")
929 self.failUnlessEqual(thatmomentinmarch, 1237585742.226536)
930 self.failUnlessEqual(origtzname, time.tzname)
932 def test_iso_utc(self):
933 when = 1266760143.7841301
934 out = time_format.iso_utc_date(when)
935 self.failUnlessEqual(out, "2010-02-21")
936 out = time_format.iso_utc_date(t=lambda: when)
937 self.failUnlessEqual(out, "2010-02-21")
938 out = time_format.iso_utc(when)
939 self.failUnlessEqual(out, "2010-02-21_13:49:03.784130")
940 out = time_format.iso_utc(when, sep="-")
941 self.failUnlessEqual(out, "2010-02-21-13:49:03.784130")
943 def test_parse_duration(self):
944 p = time_format.parse_duration
946 self.failUnlessEqual(p("1 day"), DAY)
947 self.failUnlessEqual(p("2 days"), 2*DAY)
948 self.failUnlessEqual(p("3 months"), 3*31*DAY)
949 self.failUnlessEqual(p("4 mo"), 4*31*DAY)
950 self.failUnlessEqual(p("5 years"), 5*365*DAY)
951 e = self.failUnlessRaises(ValueError, p, "123")
952 self.failUnlessIn("no unit (like day, month, or year) in '123'",
955 def test_parse_date(self):
956 self.failUnlessEqual(time_format.parse_date("2010-02-21"), 1266710400)
958 class CacheDir(unittest.TestCase):
959 def test_basic(self):
960 basedir = "test_util/CacheDir/test_basic"
962 def _failIfExists(name):
963 absfn = os.path.join(basedir, name)
964 self.failIf(os.path.exists(absfn),
965 "%s exists but it shouldn't" % absfn)
967 def _failUnlessExists(name):
968 absfn = os.path.join(basedir, name)
969 self.failUnless(os.path.exists(absfn),
970 "%s doesn't exist but it should" % absfn)
972 cdm = cachedir.CacheDirectoryManager(basedir)
973 a = cdm.get_file("a")
974 b = cdm.get_file("b")
975 c = cdm.get_file("c")
976 f = open(a.get_filename(), "wb"); f.write("hi"); f.close(); del f
977 f = open(b.get_filename(), "wb"); f.write("hi"); f.close(); del f
978 f = open(c.get_filename(), "wb"); f.write("hi"); f.close(); del f
980 _failUnlessExists("a")
981 _failUnlessExists("b")
982 _failUnlessExists("c")
986 _failUnlessExists("a")
987 _failUnlessExists("b")
988 _failUnlessExists("c")
991 # this file won't be deleted yet, because it isn't old enough
993 _failUnlessExists("a")
994 _failUnlessExists("b")
995 _failUnlessExists("c")
997 # we change the definition of "old" to make everything old
1002 _failUnlessExists("b")
1003 _failUnlessExists("c")
1011 _failUnlessExists("b")
1012 _failUnlessExists("c")
1014 b2 = cdm.get_file("b")
1018 _failUnlessExists("b")
1019 _failUnlessExists("c")
1024 def __init__(self, x):
1029 return "<%s %s>" % (self.__class__.__name__, self.x,)
1032 def __le__(self, other):
1033 return self.x <= other
1034 def __lt__(self, other):
1035 return self.x < other
1036 def __ge__(self, other):
1037 return self.x >= other
1038 def __gt__(self, other):
1039 return self.x > other
1040 def __ne__(self, other):
1041 return self.x != other
1042 def __eq__(self, other):
1043 return self.x == other
1045 class DictUtil(unittest.TestCase):
1046 def _help_test_empty_dict(self, klass):
1050 self.failUnless(d1 == d2, "d1: %r, d2: %r" % (d1, d2,))
1051 self.failUnless(len(d1) == 0)
1052 self.failUnless(len(d2) == 0)
1054 def _help_test_nonempty_dict(self, klass):
1055 d1 = klass({'a': 1, 'b': "eggs", 3: "spam",})
1056 d2 = klass({'a': 1, 'b': "eggs", 3: "spam",})
1058 self.failUnless(d1 == d2)
1059 self.failUnless(len(d1) == 3, "%s, %s" % (len(d1), d1,))
1060 self.failUnless(len(d2) == 3)
1062 def _help_test_eq_but_notis(self, klass):
1063 d = klass({'a': 3, 'b': EqButNotIs(3), 'c': 3})
1068 d['b'] = EqButNotIs(3)
1073 d['b'] = EqButNotIs(3)
1079 d['a'] = EqButNotIs(3)
1084 fake3 = EqButNotIs(3)
1085 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()))
1099 fake3 = EqButNotIs(3)
1100 fake7 = EqButNotIs(7)
1103 self.failUnless(filter(lambda x: x is 8, d.itervalues()))
1104 self.failUnless(filter(lambda x: x is fake7, d.itervalues()))
1105 # The real 7 should have been ejected by the d[3] = 8.
1106 self.failUnless(not filter(lambda x: x is 7, d.itervalues()))
1107 self.failUnless(filter(lambda x: x is fake3, d.iterkeys()))
1108 self.failUnless(filter(lambda x: x is 3, d.iterkeys()))
1112 self._help_test_eq_but_notis(dictutil.UtilDict)
1113 self._help_test_eq_but_notis(dictutil.NumDict)
1114 self._help_test_eq_but_notis(dictutil.ValueOrderedDict)
1115 self._help_test_nonempty_dict(dictutil.UtilDict)
1116 self._help_test_nonempty_dict(dictutil.NumDict)
1117 self._help_test_nonempty_dict(dictutil.ValueOrderedDict)
1118 self._help_test_eq_but_notis(dictutil.UtilDict)
1119 self._help_test_eq_but_notis(dictutil.NumDict)
1120 self._help_test_eq_but_notis(dictutil.ValueOrderedDict)
1122 def test_dict_of_sets(self):
1123 ds = dictutil.DictOfSets()
1128 self.failUnlessEqual(ds[1], set(["a"]))
1129 self.failUnlessEqual(ds[2], set(["b", "c"]))
1130 ds.discard(3, "d") # should not raise an exception
1132 self.failUnlessEqual(ds[2], set(["c"]))
1134 self.failIf(2 in ds)
1137 ds2 = dictutil.DictOfSets()
1142 self.failUnlessEqual(ds[1], set(["a"]))
1143 self.failUnlessEqual(ds[3], set(["f", "g"]))
1144 self.failUnlessEqual(ds[4], set(["h"]))
1146 def test_move(self):
1147 d1 = {1: "a", 2: "b"}
1148 d2 = {2: "c", 3: "d"}
1149 dictutil.move(1, d1, d2)
1150 self.failUnlessEqual(d1, {2: "b"})
1151 self.failUnlessEqual(d2, {1: "a", 2: "c", 3: "d"})
1153 d1 = {1: "a", 2: "b"}
1154 d2 = {2: "c", 3: "d"}
1155 dictutil.move(2, d1, d2)
1156 self.failUnlessEqual(d1, {1: "a"})
1157 self.failUnlessEqual(d2, {2: "b", 3: "d"})
1159 d1 = {1: "a", 2: "b"}
1160 d2 = {2: "c", 3: "d"}
1161 self.failUnlessRaises(KeyError, dictutil.move, 5, d1, d2, strict=True)
1163 def test_subtract(self):
1164 d1 = {1: "a", 2: "b"}
1165 d2 = {2: "c", 3: "d"}
1166 d3 = dictutil.subtract(d1, d2)
1167 self.failUnlessEqual(d3, {1: "a"})
1169 d1 = {1: "a", 2: "b"}
1171 d3 = dictutil.subtract(d1, d2)
1172 self.failUnlessEqual(d3, {1: "a"})
1174 def test_utildict(self):
1175 d = dictutil.UtilDict({1: "a", 2: "b"})
1178 self.failUnlessEqual(d, {2: "b"})
1181 self.failUnlessRaises(TypeError, eq, d, "not a dict")
1183 d = dictutil.UtilDict({1: "b", 2: "a"})
1184 self.failUnlessEqual(d.items_sorted_by_value(),
1185 [(2, "a"), (1, "b")])
1186 self.failUnlessEqual(d.items_sorted_by_key(),
1187 [(1, "b"), (2, "a")])
1188 self.failUnlessEqual(repr(d), "{1: 'b', 2: 'a'}")
1189 self.failUnless(1 in d)
1191 d2 = dictutil.UtilDict({3: "c", 4: "d"})
1192 self.failUnless(d != d2)
1193 self.failUnless(d2 > d)
1194 self.failUnless(d2 >= d)
1195 self.failUnless(d <= d2)
1196 self.failUnless(d < d2)
1197 self.failUnlessEqual(d[1], "b")
1198 self.failUnlessEqual(sorted(list([k for k in d])), [1,2])
1201 self.failUnlessEqual(d, d3)
1202 self.failUnless(isinstance(d3, dictutil.UtilDict))
1204 d4 = d.fromkeys([3,4], "e")
1205 self.failUnlessEqual(d4, {3: "e", 4: "e"})
1207 self.failUnlessEqual(d.get(1), "b")
1208 self.failUnlessEqual(d.get(3), None)
1209 self.failUnlessEqual(d.get(3, "default"), "default")
1210 self.failUnlessEqual(sorted(list(d.items())),
1211 [(1, "b"), (2, "a")])
1212 self.failUnlessEqual(sorted(list(d.iteritems())),
1213 [(1, "b"), (2, "a")])
1214 self.failUnlessEqual(sorted(d.keys()), [1, 2])
1215 self.failUnlessEqual(sorted(d.values()), ["a", "b"])
1216 x = d.setdefault(1, "new")
1217 self.failUnlessEqual(x, "b")
1218 self.failUnlessEqual(d[1], "b")
1219 x = d.setdefault(3, "new")
1220 self.failUnlessEqual(x, "new")
1221 self.failUnlessEqual(d[3], "new")
1225 self.failUnless(x in [(1, "b"), (2, "a")])
1227 self.failUnless(x in [(1, "b"), (2, "a")])
1228 self.failUnlessRaises(KeyError, d.popitem)
1230 def test_numdict(self):
1231 d = dictutil.NumDict({"a": 1, "b": 2})
1233 d.add_num("a", 10, 5)
1234 d.add_num("c", 20, 5)
1236 self.failUnlessEqual(d, {"a": 11, "b": 2, "c": 25, "d": 30})
1238 d.subtract_num("a", 10)
1239 d.subtract_num("e", 10)
1240 d.subtract_num("f", 10, 15)
1241 self.failUnlessEqual(d, {"a": 1, "b": 2, "c": 25, "d": 30,
1244 self.failUnlessEqual(d.sum(), sum([1, 2, 25, 30, -10, 5]))
1246 d = dictutil.NumDict()
1250 self.failUnlessEqual(d, {"a": 2, "b": 6})
1254 self.failUnlessEqual(d, {"a": 1, "b": 6, "c": -1, "d": 4})
1255 self.failUnlessEqual(d.items_sorted_by_key(),
1256 [("a", 1), ("b", 6), ("c", -1), ("d", 4)])
1257 self.failUnlessEqual(d.items_sorted_by_value(),
1258 [("c", -1), ("a", 1), ("d", 4), ("b", 6)])
1259 self.failUnlessEqual(d.item_with_largest_value(), ("b", 6))
1261 d = dictutil.NumDict({"a": 1, "b": 2})
1262 self.failUnlessEqual(repr(d), "{'a': 1, 'b': 2}")
1263 self.failUnless("a" in d)
1265 d2 = dictutil.NumDict({"c": 3, "d": 4})
1266 self.failUnless(d != d2)
1267 self.failUnless(d2 > d)
1268 self.failUnless(d2 >= d)
1269 self.failUnless(d <= d2)
1270 self.failUnless(d < d2)
1271 self.failUnlessEqual(d["a"], 1)
1272 self.failUnlessEqual(sorted(list([k for k in d])), ["a","b"])
1275 self.failUnlessRaises(TypeError, eq, d, "not a dict")
1278 self.failUnlessEqual(d, d3)
1279 self.failUnless(isinstance(d3, dictutil.NumDict))
1281 d4 = d.fromkeys(["a","b"], 5)
1282 self.failUnlessEqual(d4, {"a": 5, "b": 5})
1284 self.failUnlessEqual(d.get("a"), 1)
1285 self.failUnlessEqual(d.get("c"), 0)
1286 self.failUnlessEqual(d.get("c", 5), 5)
1287 self.failUnlessEqual(sorted(list(d.items())),
1288 [("a", 1), ("b", 2)])
1289 self.failUnlessEqual(sorted(list(d.iteritems())),
1290 [("a", 1), ("b", 2)])
1291 self.failUnlessEqual(sorted(d.keys()), ["a", "b"])
1292 self.failUnlessEqual(sorted(d.values()), [1, 2])
1293 self.failUnless(d.has_key("a"))
1294 self.failIf(d.has_key("c"))
1296 x = d.setdefault("c", 3)
1297 self.failUnlessEqual(x, 3)
1298 self.failUnlessEqual(d["c"], 3)
1299 x = d.setdefault("c", 5)
1300 self.failUnlessEqual(x, 3)
1301 self.failUnlessEqual(d["c"], 3)
1305 self.failUnless(x in [("a", 1), ("b", 2)])
1307 self.failUnless(x in [("a", 1), ("b", 2)])
1308 self.failUnlessRaises(KeyError, d.popitem)
1311 d.update({"c": 4, "d": 5})
1312 self.failUnlessEqual(d, {"c": 4, "d": 5})
1314 def test_del_if_present(self):
1315 d = {1: "a", 2: "b"}
1316 dictutil.del_if_present(d, 1)
1317 dictutil.del_if_present(d, 3)
1318 self.failUnlessEqual(d, {2: "b"})
1320 def test_valueordereddict(self):
1321 d = dictutil.ValueOrderedDict()
1326 self.failUnlessEqual(d, {"a": 3, "b": 2, "c": 1})
1327 self.failUnlessEqual(d.items(), [("c", 1), ("b", 2), ("a", 3)])
1328 self.failUnlessEqual(d.values(), [1, 2, 3])
1329 self.failUnlessEqual(d.keys(), ["c", "b", "a"])
1330 self.failUnlessEqual(repr(d), "<ValueOrderedDict {c: 1, b: 2, a: 3}>")
1333 self.failIf(d == {"a": 4})
1334 self.failUnless(d != {"a": 4})
1336 x = d.setdefault("d", 0)
1337 self.failUnlessEqual(x, 0)
1338 self.failUnlessEqual(d["d"], 0)
1339 x = d.setdefault("d", -1)
1340 self.failUnlessEqual(x, 0)
1341 self.failUnlessEqual(d["d"], 0)
1343 x = d.remove("e", "default", False)
1344 self.failUnlessEqual(x, "default")
1345 self.failUnlessRaises(KeyError, d.remove, "e", "default", True)
1346 x = d.remove("d", 5)
1347 self.failUnlessEqual(x, 0)
1349 x = d.__getitem__("c")
1350 self.failUnlessEqual(x, 1)
1351 x = d.__getitem__("e", "default", False)
1352 self.failUnlessEqual(x, "default")
1353 self.failUnlessRaises(KeyError, d.__getitem__, "e", "default", True)
1355 self.failUnlessEqual(d.popitem(), ("c", 1))
1356 self.failUnlessEqual(d.popitem(), ("b", 2))
1357 self.failUnlessEqual(d.popitem(), ("a", 3))
1358 self.failUnlessRaises(KeyError, d.popitem)
1360 d = dictutil.ValueOrderedDict({"a": 3, "b": 2, "c": 1})
1361 x = d.pop("d", "default", False)
1362 self.failUnlessEqual(x, "default")
1363 self.failUnlessRaises(KeyError, d.pop, "d", "default", True)
1365 self.failUnlessEqual(x, 2)
1366 self.failUnlessEqual(d.items(), [("c", 1), ("a", 3)])
1368 d = dictutil.ValueOrderedDict({"a": 3, "b": 2, "c": 1})
1369 x = d.pop_from_list(1) # pop the second item, b/2
1370 self.failUnlessEqual(x, "b")
1371 self.failUnlessEqual(d.items(), [("c", 1), ("a", 3)])
1373 def test_auxdict(self):
1374 d = dictutil.AuxValueDict()
1375 # we put the serialized form in the auxdata
1376 d.set_with_aux("key", ("filecap", "metadata"), "serialized")
1378 self.failUnlessEqual(d.keys(), ["key"])
1379 self.failUnlessEqual(d["key"], ("filecap", "metadata"))
1380 self.failUnlessEqual(d.get_aux("key"), "serialized")
1381 def _get_missing(key):
1383 self.failUnlessRaises(KeyError, _get_missing, "nonkey")
1384 self.failUnlessEqual(d.get("nonkey"), None)
1385 self.failUnlessEqual(d.get("nonkey", "nonvalue"), "nonvalue")
1386 self.failUnlessEqual(d.get_aux("nonkey"), None)
1387 self.failUnlessEqual(d.get_aux("nonkey", "nonvalue"), "nonvalue")
1389 d["key"] = ("filecap2", "metadata2")
1390 self.failUnlessEqual(d["key"], ("filecap2", "metadata2"))
1391 self.failUnlessEqual(d.get_aux("key"), None)
1393 d.set_with_aux("key2", "value2", "aux2")
1394 self.failUnlessEqual(sorted(d.keys()), ["key", "key2"])
1396 self.failUnlessEqual(d.keys(), ["key"])
1397 self.failIf("key2" in d)
1398 self.failUnlessRaises(KeyError, _get_missing, "key2")
1399 self.failUnlessEqual(d.get("key2"), None)
1400 self.failUnlessEqual(d.get_aux("key2"), None)
1401 d["key2"] = "newvalue2"
1402 self.failUnlessEqual(d.get("key2"), "newvalue2")
1403 self.failUnlessEqual(d.get_aux("key2"), None)
1405 d = dictutil.AuxValueDict({1:2,3:4})
1406 self.failUnlessEqual(sorted(d.keys()), [1,3])
1407 self.failUnlessEqual(d[1], 2)
1408 self.failUnlessEqual(d.get_aux(1), None)
1410 d = dictutil.AuxValueDict([ (1,2), (3,4) ])
1411 self.failUnlessEqual(sorted(d.keys()), [1,3])
1412 self.failUnlessEqual(d[1], 2)
1413 self.failUnlessEqual(d.get_aux(1), None)
1415 d = dictutil.AuxValueDict(one=1, two=2)
1416 self.failUnlessEqual(sorted(d.keys()), ["one","two"])
1417 self.failUnlessEqual(d["one"], 1)
1418 self.failUnlessEqual(d.get_aux("one"), None)
1420 class Pipeline(unittest.TestCase):
1421 def pause(self, *args, **kwargs):
1422 d = defer.Deferred()
1423 self.calls.append( (d, args, kwargs) )
1426 def failUnlessCallsAre(self, expected):
1429 self.failUnlessEqual(len(self.calls), len(expected), self.calls)
1430 for i,c in enumerate(self.calls):
1431 self.failUnlessEqual(c[1:], expected[i], str(i))
1433 def test_basic(self):
1436 p = pipeline.Pipeline(100)
1438 d = p.flush() # fires immediately
1439 d.addCallbacks(finished.append, log.err)
1440 self.failUnlessEqual(len(finished), 1)
1443 d = p.add(10, self.pause, "one")
1444 # the call should start right away, and our return Deferred should
1446 d.addCallbacks(finished.append, log.err)
1447 self.failUnlessEqual(len(finished), 1)
1448 self.failUnlessEqual(finished[0], None)
1449 self.failUnlessCallsAre([ ( ("one",) , {} ) ])
1450 self.failUnlessEqual(p.gauge, 10)
1455 d = p.add(20, self.pause, "two", kw=2)
1456 # pipeline: [one, two]
1458 # the call and the Deferred should fire right away
1459 d.addCallbacks(finished.append, log.err)
1460 self.failUnlessEqual(len(finished), 1)
1461 self.failUnlessEqual(finished[0], None)
1462 self.failUnlessCallsAre([ ( ("one",) , {} ),
1463 ( ("two",) , {"kw": 2} ),
1465 self.failUnlessEqual(p.gauge, 30)
1467 self.calls[0][0].callback("one-result")
1469 self.failUnlessEqual(p.gauge, 20)
1472 d = p.add(90, self.pause, "three", "posarg1")
1473 # pipeline: [two, three]
1476 fd.addCallbacks(flushed.append, log.err)
1477 self.failUnlessEqual(flushed, [])
1479 # the call will be made right away, but the return Deferred will not,
1480 # because the pipeline is now full.
1481 d.addCallbacks(finished.append, log.err)
1482 self.failUnlessEqual(len(finished), 0)
1483 self.failUnlessCallsAre([ ( ("one",) , {} ),
1484 ( ("two",) , {"kw": 2} ),
1485 ( ("three", "posarg1"), {} ),
1487 self.failUnlessEqual(p.gauge, 110)
1489 self.failUnlessRaises(pipeline.SingleFileError, p.add, 10, self.pause)
1491 # retiring either call will unblock the pipeline, causing the #3
1493 self.calls[2][0].callback("three-result")
1496 self.failUnlessEqual(len(finished), 1)
1497 self.failUnlessEqual(finished[0], None)
1498 self.failUnlessEqual(flushed, [])
1500 # retiring call#2 will finally allow the flush() Deferred to fire
1501 self.calls[1][0].callback("two-result")
1502 self.failUnlessEqual(len(flushed), 1)
1504 def test_errors(self):
1506 p = pipeline.Pipeline(100)
1508 d1 = p.add(200, self.pause, "one")
1512 d1.addBoth(finished.append)
1513 self.failUnlessEqual(finished, [])
1516 d2.addBoth(flushed.append)
1517 self.failUnlessEqual(flushed, [])
1519 self.calls[0][0].errback(ValueError("oops"))
1521 self.failUnlessEqual(len(finished), 1)
1523 self.failUnless(isinstance(f, Failure))
1524 self.failUnless(f.check(pipeline.PipelineError))
1525 self.failUnlessIn("PipelineError", str(f.value))
1526 self.failUnlessIn("ValueError", str(f.value))
1528 self.failUnless("ValueError" in r, r)
1530 self.failUnless(f2.check(ValueError))
1532 self.failUnlessEqual(len(flushed), 1)
1534 self.failUnless(isinstance(f, Failure))
1535 self.failUnless(f.check(pipeline.PipelineError))
1537 self.failUnless(f2.check(ValueError))
1539 # now that the pipeline is in the failed state, any new calls will
1542 d3 = p.add(20, self.pause, "two")
1545 d3.addBoth(finished.append)
1546 self.failUnlessEqual(len(finished), 1)
1548 self.failUnless(isinstance(f, Failure))
1549 self.failUnless(f.check(pipeline.PipelineError))
1551 self.failUnless("ValueError" in r, r)
1553 self.failUnless(f2.check(ValueError))
1557 d4.addBoth(flushed.append)
1558 self.failUnlessEqual(len(flushed), 1)
1560 self.failUnless(isinstance(f, Failure))
1561 self.failUnless(f.check(pipeline.PipelineError))
1563 self.failUnless(f2.check(ValueError))
1565 def test_errors2(self):
1567 p = pipeline.Pipeline(100)
1569 d1 = p.add(10, self.pause, "one")
1570 d2 = p.add(20, self.pause, "two")
1571 d3 = p.add(30, self.pause, "three")
1574 # one call fails, then the second one succeeds: make sure
1575 # ExpandableDeferredList tolerates the second one
1578 d4.addBoth(flushed.append)
1579 self.failUnlessEqual(flushed, [])
1581 self.calls[0][0].errback(ValueError("oops"))
1582 self.failUnlessEqual(len(flushed), 1)
1584 self.failUnless(isinstance(f, Failure))
1585 self.failUnless(f.check(pipeline.PipelineError))
1587 self.failUnless(f2.check(ValueError))
1589 self.calls[1][0].callback("two-result")
1590 self.calls[2][0].errback(ValueError("three-error"))
1594 class SampleError(Exception):
1597 class Log(unittest.TestCase):
1599 if not hasattr(self, "flushLoggedErrors"):
1600 # without flushLoggedErrors, we can't get rid of the
1601 # twisted.log.err that tahoe_log records, so we can't keep this
1602 # test from [ERROR]ing
1603 raise unittest.SkipTest("needs flushLoggedErrors from Twisted-2.5.0")
1605 raise SampleError("simple sample")
1608 tahoe_log.err(format="intentional sample error",
1609 failure=f, level=tahoe_log.OPERATIONAL, umid="wO9UoQ")
1610 self.flushLoggedErrors(SampleError)
1614 # this is a simple+inefficient form of util.spans.Spans . We compare the
1615 # behavior of this reference model against the real (efficient) form.
1617 def __init__(self, _span_or_start=None, length=None):
1619 if length is not None:
1620 for i in range(_span_or_start, _span_or_start+length):
1622 elif _span_or_start:
1623 for (start,length) in _span_or_start:
1624 self.add(start, length)
1626 def add(self, start, length):
1627 for i in range(start, start+length):
1631 def remove(self, start, length):
1632 for i in range(start, start+length):
1633 self._have.discard(i)
1637 return sorted(self._have)
1640 items = sorted(self._have)
1644 if prevstart is None:
1645 prevstart = prevend = i
1650 yield (prevstart, prevend-prevstart+1)
1651 prevstart = prevend = i
1652 if prevstart is not None:
1653 yield (prevstart, prevend-prevstart+1)
1655 def __nonzero__(self): # this gets us bool()
1659 return len(self._have)
1661 def __add__(self, other):
1662 s = self.__class__(self)
1663 for (start, length) in other:
1664 s.add(start, length)
1667 def __sub__(self, other):
1668 s = self.__class__(self)
1669 for (start, length) in other:
1670 s.remove(start, length)
1673 def __iadd__(self, other):
1674 for (start, length) in other:
1675 self.add(start, length)
1678 def __isub__(self, other):
1679 for (start, length) in other:
1680 self.remove(start, length)
1683 def __and__(self, other):
1684 s = self.__class__()
1685 for i in other.each():
1690 def __contains__(self, (start,length)):
1691 for i in range(start, start+length):
1692 if i not in self._have:
1696 class ByteSpans(unittest.TestCase):
1697 def test_basic(self):
1699 self.failUnlessEqual(list(s), [])
1701 self.failIf((0,1) in s)
1702 self.failUnlessEqual(s.len(), 0)
1704 s1 = Spans(3, 4) # 3,4,5,6
1707 s1 = Spans(3L, 4L) # 3,4,5,6
1713 s2.add(10,2) # 10,11
1715 self.failUnless((10,1) in s2)
1716 self.failIf((10,1) in s1)
1717 self.failUnlessEqual(list(s2.each()), [3,4,5,6,10,11])
1718 self.failUnlessEqual(s2.len(), 6)
1720 s2.add(15,2).add(20,2)
1721 self.failUnlessEqual(list(s2.each()), [3,4,5,6,10,11,15,16,20,21])
1722 self.failUnlessEqual(s2.len(), 10)
1724 s2.remove(4,3).remove(15,1)
1725 self.failUnlessEqual(list(s2.each()), [3,10,11,16,20,21])
1726 self.failUnlessEqual(s2.len(), 6)
1728 s1 = SimpleSpans(3, 4) # 3 4 5 6
1729 s2 = SimpleSpans(5, 4) # 5 6 7 8
1731 self.failUnlessEqual(list(i.each()), [5, 6])
1733 def _check1(self, s):
1734 self.failUnlessEqual(list(s), [(3,4)])
1736 self.failUnlessEqual(s.len(), 4)
1737 self.failIf((0,1) in s)
1738 self.failUnless((3,4) in s)
1739 self.failUnless((3,1) in s)
1740 self.failUnless((5,2) in s)
1741 self.failUnless((6,1) in s)
1742 self.failIf((6,2) in s)
1743 self.failIf((7,1) in s)
1744 self.failUnlessEqual(list(s.each()), [3,4,5,6])
1746 def test_large(self):
1747 s = Spans(4, 2**65) # don't do this with a SimpleSpans
1748 self.failUnlessEqual(list(s), [(4, 2**65)])
1750 self.failUnlessEqual(s.len(), 2**65)
1751 self.failIf((0,1) in s)
1752 self.failUnless((4,2) in s)
1753 self.failUnless((2**65,2) in s)
1755 def test_math(self):
1756 s1 = Spans(0, 10) # 0,1,2,3,4,5,6,7,8,9
1757 s2 = Spans(5, 3) # 5,6,7
1758 s3 = Spans(8, 4) # 8,9,10,11
1761 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,8,9])
1763 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7])
1765 self.failUnlessEqual(list(s.each()), [5,6,7])
1767 self.failUnlessEqual(list(s.each()), [5,6,7])
1769 self.failUnlessEqual(list(s.each()), [5,6,7])
1771 self.failUnlessEqual(list(s.each()), [8,9])
1773 self.failUnlessEqual(list(s.each()), [8,9])
1775 self.failUnlessEqual(list(s.each()), [])
1777 self.failUnlessEqual(list(s.each()), [])
1779 self.failUnlessEqual(list(s.each()), [])
1781 self.failUnlessEqual(list(s.each()), [])
1784 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7,8,9])
1786 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7,8,9,10,11])
1788 self.failUnlessEqual(list(s.each()), [5,6,7,8,9,10,11])
1792 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,8,9])
1795 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7])
1798 self.failUnlessEqual(list(s.each()), [5,6,7])
1802 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7,8,9])
1805 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7,8,9,10,11])
1808 self.failUnlessEqual(list(s.each()), [5,6,7,8,9,10,11])
1810 def test_random(self):
1811 # attempt to increase coverage of corner cases by comparing behavior
1812 # of a simple-but-slow model implementation against the
1813 # complex-but-fast actual implementation, in a large number of random
1817 s1 = S1(); s2 = S2()
1819 def _create(subseed):
1820 ns1 = S1(); ns2 = S2()
1822 what = _hash(subseed+str(i)).hexdigest()
1823 start = int(what[2:4], 16)
1824 length = max(1,int(what[5:6], 16))
1825 ns1.add(start, length); ns2.add(start, length)
1829 for i in range(1000):
1830 what = _hash(seed+str(i)).hexdigest()
1833 start = int(what[2:4], 16)
1834 length = max(1,int(what[5:6], 16))
1837 if subop in "01234":
1838 s1 = S1(); s2 = S2()
1839 elif subop in "5678":
1840 s1 = S1(start, length); s2 = S2(start, length)
1842 s1 = S1(s1); s2 = S2(s2)
1843 #print "s2 = %s" % s2.dump()
1845 #print "s2.add(%d,%d)" % (start, length)
1846 s1.add(start, length); s2.add(start, length)
1848 #print "s2.remove(%d,%d)" % (start, length)
1849 s1.remove(start, length); s2.remove(start, length)
1851 ns1, ns2 = _create(what[7:11])
1852 #print "s2 + %s" % ns2.dump()
1853 s1 = s1 + ns1; s2 = s2 + ns2
1855 ns1, ns2 = _create(what[7:11])
1856 #print "%s - %s" % (s2.dump(), ns2.dump())
1857 s1 = s1 - ns1; s2 = s2 - ns2
1859 ns1, ns2 = _create(what[7:11])
1860 #print "s2 += %s" % ns2.dump()
1861 s1 += ns1; s2 += ns2
1863 ns1, ns2 = _create(what[7:11])
1864 #print "%s -= %s" % (s2.dump(), ns2.dump())
1865 s1 -= ns1; s2 -= ns2
1867 ns1, ns2 = _create(what[7:11])
1868 #print "%s &= %s" % (s2.dump(), ns2.dump())
1869 s1 = s1 & ns1; s2 = s2 & ns2
1870 #print "s2 now %s" % s2.dump()
1871 self.failUnlessEqual(list(s1.each()), list(s2.each()))
1872 self.failUnlessEqual(s1.len(), s2.len())
1873 self.failUnlessEqual(bool(s1), bool(s2))
1874 self.failUnlessEqual(list(s1), list(s2))
1876 what = _hash(what[12:14]+str(j)).hexdigest()
1877 start = int(what[2:4], 16)
1878 length = max(1, int(what[5:6], 16))
1879 span = (start, length)
1880 self.failUnlessEqual(bool(span in s1), bool(span in s2))
1886 # s.add(start,length) : returns s
1887 # s.remove(start,length)
1888 # s.each() -> list of byte offsets, mostly for testing
1889 # list(s) -> list of (start,length) tuples, one per span
1890 # (start,length) in s -> True if (start..start+length-1) are all members
1891 # NOT equivalent to x in list(s)
1892 # s.len() -> number of bytes, for testing, bool(), and accounting/limiting
1893 # bool(s) (__nonzeron__)
1894 # s = s1+s2, s1-s2, +=s1, -=s1
1896 def test_overlap(self):
1901 self._test_overlap(a,b,c,d)
1903 def _test_overlap(self, a, b, c, d):
1904 s1 = set(range(a,a+b))
1905 s2 = set(range(c,c+d))
1907 #self._show_overlap(s1, "1")
1908 #self._show_overlap(s2, "2")
1909 o = overlap(a,b,c,d)
1910 expected = s1.intersection(s2)
1912 self.failUnlessEqual(o, None)
1915 so = set(range(start,start+length))
1916 #self._show(so, "o")
1917 self.failUnlessEqual(so, expected)
1919 def _show_overlap(self, s, c):
1923 for i in range(max(s)):
1930 def extend(s, start, length, fill):
1931 if len(s) >= start+length:
1933 assert len(fill) == 1
1934 return s + fill*(start+length-len(s))
1936 def replace(s, start, data):
1937 assert len(s) >= start+len(data)
1938 return s[:start] + data + s[start+len(data):]
1940 class SimpleDataSpans:
1941 def __init__(self, other=None):
1942 self.missing = "" # "1" where missing, "0" where found
1945 for (start, data) in other.get_chunks():
1946 self.add(start, data)
1948 def __nonzero__(self): # this gets us bool()
1951 return len(self.missing.replace("1", ""))
1953 return [i for (i,c) in enumerate(self.missing) if c == "0"]
1954 def _have(self, start, length):
1955 m = self.missing[start:start+length]
1956 if not m or len(m)<length or int(m):
1959 def get_chunks(self):
1960 for i in self._dump():
1961 yield (i, self.data[i])
1962 def get_spans(self):
1963 return SimpleSpans([(start,len(data))
1964 for (start,data) in self.get_chunks()])
1965 def get(self, start, length):
1966 if self._have(start, length):
1967 return self.data[start:start+length]
1969 def pop(self, start, length):
1970 data = self.get(start, length)
1972 self.remove(start, length)
1974 def remove(self, start, length):
1975 self.missing = replace(extend(self.missing, start, length, "1"),
1977 def add(self, start, data):
1978 self.missing = replace(extend(self.missing, start, len(data), "1"),
1979 start, "0"*len(data))
1980 self.data = replace(extend(self.data, start, len(data), " "),
1984 class StringSpans(unittest.TestCase):
1985 def do_basic(self, klass):
1987 self.failUnlessEqual(ds.len(), 0)
1988 self.failUnlessEqual(list(ds._dump()), [])
1989 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 0)
1991 self.failUnlessEqual(ds.get(0, 4), None)
1992 self.failUnlessEqual(ds.pop(0, 4), None)
1996 self.failUnlessEqual(ds.len(), 4)
1997 self.failUnlessEqual(list(ds._dump()), [2,3,4,5])
1998 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 4)
2000 self.failUnless((2,2) in s1)
2001 self.failUnlessEqual(ds.get(0, 4), None)
2002 self.failUnlessEqual(ds.pop(0, 4), None)
2003 self.failUnlessEqual(ds.get(4, 4), None)
2006 self.failUnlessEqual(ds2.len(), 4)
2007 self.failUnlessEqual(list(ds2._dump()), [2,3,4,5])
2008 self.failUnlessEqual(sum([len(d) for (s,d) in ds2.get_chunks()]), 4)
2009 self.failUnlessEqual(ds2.get(0, 4), None)
2010 self.failUnlessEqual(ds2.pop(0, 4), None)
2011 self.failUnlessEqual(ds2.pop(2, 3), "fou")
2012 self.failUnlessEqual(sum([len(d) for (s,d) in ds2.get_chunks()]), 1)
2013 self.failUnlessEqual(ds2.get(2, 3), None)
2014 self.failUnlessEqual(ds2.get(5, 1), "r")
2015 self.failUnlessEqual(ds.get(2, 3), "fou")
2016 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 4)
2019 self.failUnlessEqual(ds.len(), 6)
2020 self.failUnlessEqual(list(ds._dump()), [0,1,2,3,4,5])
2021 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 6)
2022 self.failUnlessEqual(ds.get(0, 4), "23fo")
2023 self.failUnlessEqual(ds.pop(0, 4), "23fo")
2024 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 2)
2025 self.failUnlessEqual(ds.get(0, 4), None)
2026 self.failUnlessEqual(ds.pop(0, 4), None)
2031 self.failUnlessEqual(ds.get(2, 4), "fear")
2036 self.failUnlessEqual(ds.get(2L, 4L), "fear")
2039 def do_scan(self, klass):
2040 # do a test with gaps and spans of size 1 and 2
2041 # left=(1,11) * right=(1,11) * gapsize=(1,2)
2042 # 111, 112, 121, 122, 211, 212, 221, 222
2051 # 11 1 1 11 11 11 1 1 111
2052 # 0123456789012345678901234567
2053 # abcdefghijklmnopqrstuvwxyz-=
2054 pieces = [(1, "bc"),
2064 p_elements = set([1,2,4,7,9,10,12,13,16,17,20,22,25,26,27])
2065 S = "abcdefghijklmnopqrstuvwxyz-="
2066 # TODO: when adding data, add capital letters, to make sure we aren't
2067 # just leaving the old data in place
2071 for start, data in pieces:
2076 d = "".join([((i not in p) and " " or S[i]) for i in range(l)])
2080 for start in range(0, l):
2081 for end in range(start+1, l):
2082 # add [start-end) to the baseline
2083 which = "%d-%d" % (start, end-1)
2084 p_added = set(range(start, end))
2088 print dump(b), which
2089 add = klass(); add.add(start, S[start:end])
2091 b.add(start, S[start:end])
2094 # check that the new span is there
2095 d = b.get(start, end-start)
2096 self.failUnlessEqual(d, S[start:end], which)
2097 # check that all the original pieces are still there
2098 for t_start, t_data in pieces:
2100 self.failUnlessEqual(b.get(t_start, t_len),
2101 S[t_start:t_start+t_len],
2102 "%s %d+%d" % (which, t_start, t_len))
2103 # check that a lot of subspans are mostly correct
2104 for t_start in range(l):
2105 for t_len in range(1,4):
2106 d = b.get(t_start, t_len)
2108 which2 = "%s+(%d-%d)" % (which, t_start,
2110 self.failUnlessEqual(d, S[t_start:t_start+t_len],
2112 # check that removing a subspan gives the right value
2114 b2.remove(t_start, t_len)
2115 removed = set(range(t_start, t_start+t_len))
2117 exp = (((i in p_elements) or (i in p_added))
2118 and (i not in removed))
2119 which2 = "%s-(%d-%d)" % (which, t_start,
2121 self.failUnlessEqual(bool(b2.get(i, 1)), exp,
2124 def test_test(self):
2125 self.do_basic(SimpleDataSpans)
2126 self.do_scan(SimpleDataSpans)
2128 def test_basic(self):
2129 self.do_basic(DataSpans)
2130 self.do_scan(DataSpans)
2132 def test_random(self):
2133 # attempt to increase coverage of corner cases by comparing behavior
2134 # of a simple-but-slow model implementation against the
2135 # complex-but-fast actual implementation, in a large number of random
2137 S1 = SimpleDataSpans
2139 s1 = S1(); s2 = S2()
2141 def _randstr(length, seed):
2144 while created < length:
2145 piece = _hash(seed + str(created)).hexdigest()
2146 pieces.append(piece)
2147 created += len(piece)
2148 return "".join(pieces)[:length]
2149 def _create(subseed):
2150 ns1 = S1(); ns2 = S2()
2152 what = _hash(subseed+str(i)).hexdigest()
2153 start = int(what[2:4], 16)
2154 length = max(1,int(what[5:6], 16))
2155 ns1.add(start, _randstr(length, what[7:9]));
2156 ns2.add(start, _randstr(length, what[7:9]))
2160 for i in range(1000):
2161 what = _hash(seed+str(i)).hexdigest()
2164 start = int(what[2:4], 16)
2165 length = max(1,int(what[5:6], 16))
2168 if subop in "0123456":
2169 s1 = S1(); s2 = S2()
2171 s1, s2 = _create(what[7:11])
2172 #print "s2 = %s" % list(s2._dump())
2173 elif op in "123456":
2174 #print "s2.add(%d,%d)" % (start, length)
2175 s1.add(start, _randstr(length, what[7:9]));
2176 s2.add(start, _randstr(length, what[7:9]))
2177 elif op in "789abc":
2178 #print "s2.remove(%d,%d)" % (start, length)
2179 s1.remove(start, length); s2.remove(start, length)
2181 #print "s2.pop(%d,%d)" % (start, length)
2182 d1 = s1.pop(start, length); d2 = s2.pop(start, length)
2183 self.failUnlessEqual(d1, d2)
2184 #print "s1 now %s" % list(s1._dump())
2185 #print "s2 now %s" % list(s2._dump())
2186 self.failUnlessEqual(s1.len(), s2.len())
2187 self.failUnlessEqual(list(s1._dump()), list(s2._dump()))
2188 for j in range(100):
2189 what = _hash(what[12:14]+str(j)).hexdigest()
2190 start = int(what[2:4], 16)
2191 length = max(1, int(what[5:6], 16))
2192 d1 = s1.get(start, length); d2 = s2.get(start, length)
2193 self.failUnlessEqual(d1, d2, "%d+%d" % (start, length))