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_NamedTemporaryDirectory(self):
433 basedir = "util/FileUtil/test_NamedTemporaryDirectory"
434 fileutil.make_dirs(basedir)
435 td = fileutil.NamedTemporaryDirectory(dir=basedir)
437 self.failUnless(basedir in name)
438 self.failUnless(basedir in repr(td))
439 self.failUnless(os.path.isdir(name))
441 # it is conceivable that we need to force gc here, but I'm not sure
442 self.failIf(os.path.isdir(name))
444 def test_rename(self):
445 basedir = "util/FileUtil/test_rename"
446 fileutil.make_dirs(basedir)
447 self.touch(basedir, "here")
448 fn = os.path.join(basedir, "here")
449 fn2 = os.path.join(basedir, "there")
450 fileutil.rename(fn, fn2)
451 self.failIf(os.path.exists(fn))
452 self.failUnless(os.path.exists(fn2))
455 basedir = "util/FileUtil/test_du"
456 fileutil.make_dirs(basedir)
457 d = os.path.join(basedir, "space-consuming")
459 self.touch(d, "a/b/1.txt", data="a"*10)
460 self.touch(d, "a/b/2.txt", data="b"*11)
462 self.touch(d, "a/c/1.txt", data="c"*12)
463 self.touch(d, "a/c/2.txt", data="d"*13)
465 used = fileutil.du(basedir)
466 self.failUnlessEqual(10+11+12+13, used)
468 def test_abspath_expanduser_unicode(self):
469 self.failUnlessRaises(AssertionError, fileutil.abspath_expanduser_unicode, "bytestring")
471 saved_cwd = os.path.normpath(os.getcwdu())
472 abspath_cwd = fileutil.abspath_expanduser_unicode(u".")
473 self.failUnless(isinstance(saved_cwd, unicode), saved_cwd)
474 self.failUnless(isinstance(abspath_cwd, unicode), abspath_cwd)
475 self.failUnlessEqual(abspath_cwd, saved_cwd)
477 # adapted from <http://svn.python.org/view/python/branches/release26-maint/Lib/test/test_posixpath.py?view=markup&pathrev=78279#test_abspath>
479 self.failUnlessIn(u"foo", fileutil.abspath_expanduser_unicode(u"foo"))
480 self.failIfIn(u"~", fileutil.abspath_expanduser_unicode(u"~"))
484 cwds.append(u'\xe7w\xf0'.encode(sys.getfilesystemencoding()
486 except UnicodeEncodeError:
487 pass # the cwd can't be encoded -- test with ascii cwd only
493 for upath in (u'', u'fuu', u'f\xf9\xf9', u'/fuu', u'U:\\', u'~'):
494 uabspath = fileutil.abspath_expanduser_unicode(upath)
495 self.failUnless(isinstance(uabspath, unicode), uabspath)
499 def test_disk_stats(self):
500 avail = fileutil.get_available_space('.', 2**14)
502 raise unittest.SkipTest("This test will spuriously fail there is no disk space left.")
504 disk = fileutil.get_disk_stats('.', 2**13)
505 self.failUnless(disk['total'] > 0, disk['total'])
506 # we tolerate used==0 for a Travis-CI bug, see #2290
507 self.failUnless(disk['used'] >= 0, disk['used'])
508 self.failUnless(disk['free_for_root'] > 0, disk['free_for_root'])
509 self.failUnless(disk['free_for_nonroot'] > 0, disk['free_for_nonroot'])
510 self.failUnless(disk['avail'] > 0, disk['avail'])
512 def test_disk_stats_avail_nonnegative(self):
513 # This test will spuriously fail if you have more than 2^128
514 # bytes of available space on your filesystem.
515 disk = fileutil.get_disk_stats('.', 2**128)
516 self.failUnlessEqual(disk['avail'], 0)
518 class PollMixinTests(unittest.TestCase):
520 self.pm = pollmixin.PollMixin()
522 def test_PollMixin_True(self):
523 d = self.pm.poll(check_f=lambda : True,
527 def test_PollMixin_False_then_True(self):
528 i = iter([False, True])
529 d = self.pm.poll(check_f=i.next,
533 def test_timeout(self):
534 d = self.pm.poll(check_f=lambda: False,
538 self.fail("poll should have failed, not returned %s" % (res,))
540 f.trap(pollmixin.TimeoutError)
541 return None # success
542 d.addCallbacks(_suc, _err)
545 class DeferredUtilTests(unittest.TestCase):
546 def test_gather_results(self):
547 d1 = defer.Deferred()
548 d2 = defer.Deferred()
549 res = deferredutil.gatherResults([d1, d2])
550 d1.errback(ValueError("BAD"))
552 self.fail("Should have errbacked, not resulted in %s" % (res,))
554 thef.trap(ValueError)
555 res.addCallbacks(_callb, _errb)
558 def test_success(self):
559 d1, d2 = defer.Deferred(), defer.Deferred()
562 dlss = deferredutil.DeferredListShouldSucceed([d1,d2])
563 dlss.addCallbacks(good.append, bad.append)
566 self.failUnlessEqual(good, [[1,2]])
567 self.failUnlessEqual(bad, [])
569 def test_failure(self):
570 d1, d2 = defer.Deferred(), defer.Deferred()
573 dlss = deferredutil.DeferredListShouldSucceed([d1,d2])
574 dlss.addCallbacks(good.append, bad.append)
575 d1.addErrback(lambda _ignore: None)
576 d2.addErrback(lambda _ignore: None)
578 d2.errback(ValueError())
579 self.failUnlessEqual(good, [])
580 self.failUnlessEqual(len(bad), 1)
582 self.failUnless(isinstance(f, Failure))
583 self.failUnless(f.check(ValueError))
585 class HashUtilTests(unittest.TestCase):
587 def test_random_key(self):
588 k = hashutil.random_key()
589 self.failUnlessEqual(len(k), hashutil.KEYLEN)
591 def test_sha256d(self):
592 h1 = hashutil.tagged_hash("tag1", "value")
593 h2 = hashutil.tagged_hasher("tag1")
597 self.failUnlessEqual(h1, h2a)
598 self.failUnlessEqual(h2a, h2b)
600 def test_sha256d_truncated(self):
601 h1 = hashutil.tagged_hash("tag1", "value", 16)
602 h2 = hashutil.tagged_hasher("tag1", 16)
605 self.failUnlessEqual(len(h1), 16)
606 self.failUnlessEqual(len(h2), 16)
607 self.failUnlessEqual(h1, h2)
610 h1 = hashutil.convergence_hash(3, 10, 1000, "data", "secret")
611 h2 = hashutil.convergence_hasher(3, 10, 1000, "secret")
614 self.failUnlessEqual(h1, h2)
616 def test_hashers(self):
617 h1 = hashutil.block_hash("foo")
618 h2 = hashutil.block_hasher()
620 self.failUnlessEqual(h1, h2.digest())
622 h1 = hashutil.uri_extension_hash("foo")
623 h2 = hashutil.uri_extension_hasher()
625 self.failUnlessEqual(h1, h2.digest())
627 h1 = hashutil.plaintext_hash("foo")
628 h2 = hashutil.plaintext_hasher()
630 self.failUnlessEqual(h1, h2.digest())
632 h1 = hashutil.crypttext_hash("foo")
633 h2 = hashutil.crypttext_hasher()
635 self.failUnlessEqual(h1, h2.digest())
637 h1 = hashutil.crypttext_segment_hash("foo")
638 h2 = hashutil.crypttext_segment_hasher()
640 self.failUnlessEqual(h1, h2.digest())
642 h1 = hashutil.plaintext_segment_hash("foo")
643 h2 = hashutil.plaintext_segment_hasher()
645 self.failUnlessEqual(h1, h2.digest())
647 def test_timing_safe_compare(self):
648 self.failUnless(hashutil.timing_safe_compare("a", "a"))
649 self.failUnless(hashutil.timing_safe_compare("ab", "ab"))
650 self.failIf(hashutil.timing_safe_compare("a", "b"))
651 self.failIf(hashutil.timing_safe_compare("a", "aa"))
653 def _testknown(self, hashf, expected_a, *args):
655 got_a = base32.b2a(got)
656 self.failUnlessEqual(got_a, expected_a)
658 def test_known_answers(self):
659 # assert backwards compatibility
660 self._testknown(hashutil.storage_index_hash, "qb5igbhcc5esa6lwqorsy7e6am", "")
661 self._testknown(hashutil.block_hash, "msjr5bh4evuh7fa3zw7uovixfbvlnstr5b65mrerwfnvjxig2jvq", "")
662 self._testknown(hashutil.uri_extension_hash, "wthsu45q7zewac2mnivoaa4ulh5xvbzdmsbuyztq2a5fzxdrnkka", "")
663 self._testknown(hashutil.plaintext_hash, "5lz5hwz3qj3af7n6e3arblw7xzutvnd3p3fjsngqjcb7utf3x3da", "")
664 self._testknown(hashutil.crypttext_hash, "itdj6e4njtkoiavlrmxkvpreosscssklunhwtvxn6ggho4rkqwga", "")
665 self._testknown(hashutil.crypttext_segment_hash, "aovy5aa7jej6ym5ikgwyoi4pxawnoj3wtaludjz7e2nb5xijb7aa", "")
666 self._testknown(hashutil.plaintext_segment_hash, "4fdgf6qruaisyukhqcmoth4t3li6bkolbxvjy4awwcpprdtva7za", "")
667 self._testknown(hashutil.convergence_hash, "3mo6ni7xweplycin6nowynw2we", 3, 10, 100, "", "converge")
668 self._testknown(hashutil.my_renewal_secret_hash, "ujhr5k5f7ypkp67jkpx6jl4p47pyta7hu5m527cpcgvkafsefm6q", "")
669 self._testknown(hashutil.my_cancel_secret_hash, "rjwzmafe2duixvqy6h47f5wfrokdziry6zhx4smew4cj6iocsfaa", "")
670 self._testknown(hashutil.file_renewal_secret_hash, "hzshk2kf33gzbd5n3a6eszkf6q6o6kixmnag25pniusyaulqjnia", "", "si")
671 self._testknown(hashutil.file_cancel_secret_hash, "bfciwvr6w7wcavsngxzxsxxaszj72dej54n4tu2idzp6b74g255q", "", "si")
672 self._testknown(hashutil.bucket_renewal_secret_hash, "e7imrzgzaoashsncacvy3oysdd2m5yvtooo4gmj4mjlopsazmvuq", "", "\x00"*20)
673 self._testknown(hashutil.bucket_cancel_secret_hash, "dvdujeyxeirj6uux6g7xcf4lvesk632aulwkzjar7srildvtqwma", "", "\x00"*20)
674 self._testknown(hashutil.hmac, "c54ypfi6pevb3nvo6ba42jtglpkry2kbdopqsi7dgrm4r7tw5sra", "tag", "")
675 self._testknown(hashutil.mutable_rwcap_key_hash, "6rvn2iqrghii5n4jbbwwqqsnqu", "iv", "wk")
676 self._testknown(hashutil.ssk_writekey_hash, "ykpgmdbpgbb6yqz5oluw2q26ye", "")
677 self._testknown(hashutil.ssk_write_enabler_master_hash, "izbfbfkoait4dummruol3gy2bnixrrrslgye6ycmkuyujnenzpia", "")
678 self._testknown(hashutil.ssk_write_enabler_hash, "fuu2dvx7g6gqu5x22vfhtyed7p4pd47y5hgxbqzgrlyvxoev62tq", "wk", "\x00"*20)
679 self._testknown(hashutil.ssk_pubkey_fingerprint_hash, "3opzw4hhm2sgncjx224qmt5ipqgagn7h5zivnfzqycvgqgmgz35q", "")
680 self._testknown(hashutil.ssk_readkey_hash, "vugid4as6qbqgeq2xczvvcedai", "")
681 self._testknown(hashutil.ssk_readkey_data_hash, "73wsaldnvdzqaf7v4pzbr2ae5a", "iv", "rk")
682 self._testknown(hashutil.ssk_storage_index_hash, "j7icz6kigb6hxrej3tv4z7ayym", "")
685 class Abbreviate(unittest.TestCase):
687 a = abbreviate.abbreviate_time
688 self.failUnlessEqual(a(None), "unknown")
689 self.failUnlessEqual(a(0), "0 seconds")
690 self.failUnlessEqual(a(1), "1 second")
691 self.failUnlessEqual(a(2), "2 seconds")
692 self.failUnlessEqual(a(119), "119 seconds")
694 self.failUnlessEqual(a(2*MIN), "2 minutes")
695 self.failUnlessEqual(a(60*MIN), "60 minutes")
696 self.failUnlessEqual(a(179*MIN), "179 minutes")
698 self.failUnlessEqual(a(180*MIN), "3 hours")
699 self.failUnlessEqual(a(4*HOUR), "4 hours")
702 self.failUnlessEqual(a(2*DAY), "2 days")
703 self.failUnlessEqual(a(2*MONTH), "2 months")
705 self.failUnlessEqual(a(5*YEAR), "5 years")
707 def test_space(self):
708 tests_si = [(None, "unknown"),
715 (20*1000, "20.00 kB"),
716 (1024*1024, "1.05 MB"),
717 (1000*1000, "1.00 MB"),
718 (1000*1000*1000, "1.00 GB"),
719 (1000*1000*1000*1000, "1.00 TB"),
720 (1000*1000*1000*1000*1000, "1.00 PB"),
721 (1000*1000*1000*1000*1000*1000, "1.00 EB"),
722 (1234567890123456789, "1.23 EB"),
724 for (x, expected) in tests_si:
725 got = abbreviate.abbreviate_space(x, SI=True)
726 self.failUnlessEqual(got, expected)
728 tests_base1024 = [(None, "unknown"),
735 (20*1024, "20.00 kiB"),
736 (1000*1000, "976.56 kiB"),
737 (1024*1024, "1.00 MiB"),
738 (1024*1024*1024, "1.00 GiB"),
739 (1024*1024*1024*1024, "1.00 TiB"),
740 (1000*1000*1000*1000*1000, "909.49 TiB"),
741 (1024*1024*1024*1024*1024, "1.00 PiB"),
742 (1024*1024*1024*1024*1024*1024, "1.00 EiB"),
743 (1234567890123456789, "1.07 EiB"),
745 for (x, expected) in tests_base1024:
746 got = abbreviate.abbreviate_space(x, SI=False)
747 self.failUnlessEqual(got, expected)
749 self.failUnlessEqual(abbreviate.abbreviate_space_both(1234567),
750 "(1.23 MB, 1.18 MiB)")
752 def test_parse_space(self):
753 p = abbreviate.parse_abbreviated_size
754 self.failUnlessEqual(p(""), None)
755 self.failUnlessEqual(p(None), None)
756 self.failUnlessEqual(p("123"), 123)
757 self.failUnlessEqual(p("123B"), 123)
758 self.failUnlessEqual(p("2K"), 2000)
759 self.failUnlessEqual(p("2kb"), 2000)
760 self.failUnlessEqual(p("2KiB"), 2048)
761 self.failUnlessEqual(p("10MB"), 10*1000*1000)
762 self.failUnlessEqual(p("10MiB"), 10*1024*1024)
763 self.failUnlessEqual(p("5G"), 5*1000*1000*1000)
764 self.failUnlessEqual(p("4GiB"), 4*1024*1024*1024)
765 self.failUnlessEqual(p("3TB"), 3*1000*1000*1000*1000)
766 self.failUnlessEqual(p("3TiB"), 3*1024*1024*1024*1024)
767 self.failUnlessEqual(p("6PB"), 6*1000*1000*1000*1000*1000)
768 self.failUnlessEqual(p("6PiB"), 6*1024*1024*1024*1024*1024)
769 self.failUnlessEqual(p("9EB"), 9*1000*1000*1000*1000*1000*1000)
770 self.failUnlessEqual(p("9EiB"), 9*1024*1024*1024*1024*1024*1024)
772 e = self.failUnlessRaises(ValueError, p, "12 cubits")
773 self.failUnlessIn("12 cubits", str(e))
774 e = self.failUnlessRaises(ValueError, p, "1 BB")
775 self.failUnlessIn("1 BB", str(e))
776 e = self.failUnlessRaises(ValueError, p, "fhtagn")
777 self.failUnlessIn("fhtagn", str(e))
779 class Limiter(unittest.TestCase):
780 timeout = 480 # This takes longer than 240 seconds on Francois's arm box.
782 def job(self, i, foo):
783 self.calls.append( (i, foo) )
784 self.simultaneous += 1
785 self.peak_simultaneous = max(self.simultaneous, self.peak_simultaneous)
788 self.simultaneous -= 1
789 d.callback("done %d" % i)
790 reactor.callLater(1.0, _done)
793 def bad_job(self, i, foo):
794 raise ValueError("bad_job %d" % i)
796 def test_limiter(self):
798 self.simultaneous = 0
799 self.peak_simultaneous = 0
800 l = limiter.ConcurrencyLimiter()
803 dl.append(l.add(self.job, i, foo=str(i)))
804 d = defer.DeferredList(dl, fireOnOneErrback=True)
806 self.failUnlessEqual(self.simultaneous, 0)
807 self.failUnless(self.peak_simultaneous <= 10)
808 self.failUnlessEqual(len(self.calls), 20)
810 self.failUnless( (i, str(i)) in self.calls)
814 def test_errors(self):
816 self.simultaneous = 0
817 self.peak_simultaneous = 0
818 l = limiter.ConcurrencyLimiter()
821 dl.append(l.add(self.job, i, foo=str(i)))
822 d2 = l.add(self.bad_job, 21, "21")
823 d = defer.DeferredList(dl, fireOnOneErrback=True)
826 for (success, result) in res:
827 self.failUnlessEqual(success, True)
828 results.append(result)
830 expected_results = ["done %d" % i for i in range(20)]
831 expected_results.sort()
832 self.failUnlessEqual(results, expected_results)
833 self.failUnless(self.peak_simultaneous <= 10)
834 self.failUnlessEqual(len(self.calls), 20)
836 self.failUnless( (i, str(i)) in self.calls)
838 self.fail("should have failed, not got %s" % (res,))
841 self.failUnless("bad_job 21" in str(f))
842 d2.addCallbacks(_good, _err)
844 d.addCallback(_most_done)
846 self.failUnlessEqual(self.simultaneous, 0)
847 self.failUnless(self.peak_simultaneous <= 10)
848 self.failUnlessEqual(len(self.calls), 20)
850 self.failUnless( (i, str(i)) in self.calls)
851 d.addCallback(_all_done)
854 class TimeFormat(unittest.TestCase):
855 def test_epoch(self):
856 return self._help_test_epoch()
858 def test_epoch_in_London(self):
859 # Europe/London is a particularly troublesome timezone. Nowadays, its
860 # offset from GMT is 0. But in 1970, its offset from GMT was 1.
861 # (Apparently in 1970 Britain had redefined standard time to be GMT+1
862 # and stayed in standard time all year round, whereas today
863 # Europe/London standard time is GMT and Europe/London Daylight
864 # Savings Time is GMT+1.) The current implementation of
865 # time_format.iso_utc_time_to_localseconds() breaks if the timezone is
866 # Europe/London. (As soon as this unit test is done then I'll change
867 # that implementation to something that works even in this case...)
868 origtz = os.environ.get('TZ')
869 os.environ['TZ'] = "Europe/London"
870 if hasattr(time, 'tzset'):
873 return self._help_test_epoch()
878 os.environ['TZ'] = origtz
879 if hasattr(time, 'tzset'):
882 def _help_test_epoch(self):
883 origtzname = time.tzname
884 s = time_format.iso_utc_time_to_seconds("1970-01-01T00:00:01")
885 self.failUnlessEqual(s, 1.0)
886 s = time_format.iso_utc_time_to_seconds("1970-01-01_00: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)
891 self.failUnlessEqual(time_format.iso_utc(1.0), "1970-01-01_00:00:01")
892 self.failUnlessEqual(time_format.iso_utc(1.0, sep=" "),
893 "1970-01-01 00:00:01")
896 isostr = time_format.iso_utc(now)
897 timestamp = time_format.iso_utc_time_to_seconds(isostr)
898 self.failUnlessEqual(int(timestamp), int(now))
902 self.failUnlessEqual(time_format.iso_utc(t=my_time),
903 "1970-01-01_00:00:01")
904 e = self.failUnlessRaises(ValueError,
905 time_format.iso_utc_time_to_seconds,
906 "invalid timestring")
907 self.failUnless("not a complete ISO8601 timestamp" in str(e))
908 s = time_format.iso_utc_time_to_seconds("1970-01-01_00:00:01.500")
909 self.failUnlessEqual(s, 1.5)
911 # Look for daylight-savings-related errors.
912 thatmomentinmarch = time_format.iso_utc_time_to_seconds("2009-03-20 21:49:02.226536")
913 self.failUnlessEqual(thatmomentinmarch, 1237585742.226536)
914 self.failUnlessEqual(origtzname, time.tzname)
916 def test_iso_utc(self):
917 when = 1266760143.7841301
918 out = time_format.iso_utc_date(when)
919 self.failUnlessEqual(out, "2010-02-21")
920 out = time_format.iso_utc_date(t=lambda: when)
921 self.failUnlessEqual(out, "2010-02-21")
922 out = time_format.iso_utc(when)
923 self.failUnlessEqual(out, "2010-02-21_13:49:03.784130")
924 out = time_format.iso_utc(when, sep="-")
925 self.failUnlessEqual(out, "2010-02-21-13:49:03.784130")
927 def test_parse_duration(self):
928 p = time_format.parse_duration
930 self.failUnlessEqual(p("1 day"), DAY)
931 self.failUnlessEqual(p("2 days"), 2*DAY)
932 self.failUnlessEqual(p("3 months"), 3*31*DAY)
933 self.failUnlessEqual(p("4 mo"), 4*31*DAY)
934 self.failUnlessEqual(p("5 years"), 5*365*DAY)
935 e = self.failUnlessRaises(ValueError, p, "123")
936 self.failUnlessIn("no unit (like day, month, or year) in '123'",
939 def test_parse_date(self):
940 self.failUnlessEqual(time_format.parse_date("2010-02-21"), 1266710400)
942 class CacheDir(unittest.TestCase):
943 def test_basic(self):
944 basedir = "test_util/CacheDir/test_basic"
946 def _failIfExists(name):
947 absfn = os.path.join(basedir, name)
948 self.failIf(os.path.exists(absfn),
949 "%s exists but it shouldn't" % absfn)
951 def _failUnlessExists(name):
952 absfn = os.path.join(basedir, name)
953 self.failUnless(os.path.exists(absfn),
954 "%s doesn't exist but it should" % absfn)
956 cdm = cachedir.CacheDirectoryManager(basedir)
957 a = cdm.get_file("a")
958 b = cdm.get_file("b")
959 c = cdm.get_file("c")
960 f = open(a.get_filename(), "wb"); f.write("hi"); f.close(); del f
961 f = open(b.get_filename(), "wb"); f.write("hi"); f.close(); del f
962 f = open(c.get_filename(), "wb"); f.write("hi"); f.close(); del f
964 _failUnlessExists("a")
965 _failUnlessExists("b")
966 _failUnlessExists("c")
970 _failUnlessExists("a")
971 _failUnlessExists("b")
972 _failUnlessExists("c")
975 # this file won't be deleted yet, because it isn't old enough
977 _failUnlessExists("a")
978 _failUnlessExists("b")
979 _failUnlessExists("c")
981 # we change the definition of "old" to make everything old
986 _failUnlessExists("b")
987 _failUnlessExists("c")
995 _failUnlessExists("b")
996 _failUnlessExists("c")
998 b2 = cdm.get_file("b")
1002 _failUnlessExists("b")
1003 _failUnlessExists("c")
1008 def __init__(self, x):
1013 return "<%s %s>" % (self.__class__.__name__, self.x,)
1016 def __le__(self, other):
1017 return self.x <= other
1018 def __lt__(self, other):
1019 return self.x < other
1020 def __ge__(self, other):
1021 return self.x >= other
1022 def __gt__(self, other):
1023 return self.x > other
1024 def __ne__(self, other):
1025 return self.x != other
1026 def __eq__(self, other):
1027 return self.x == other
1029 class DictUtil(unittest.TestCase):
1030 def _help_test_empty_dict(self, klass):
1034 self.failUnless(d1 == d2, "d1: %r, d2: %r" % (d1, d2,))
1035 self.failUnless(len(d1) == 0)
1036 self.failUnless(len(d2) == 0)
1038 def _help_test_nonempty_dict(self, klass):
1039 d1 = klass({'a': 1, 'b': "eggs", 3: "spam",})
1040 d2 = klass({'a': 1, 'b': "eggs", 3: "spam",})
1042 self.failUnless(d1 == d2)
1043 self.failUnless(len(d1) == 3, "%s, %s" % (len(d1), d1,))
1044 self.failUnless(len(d2) == 3)
1046 def _help_test_eq_but_notis(self, klass):
1047 d = klass({'a': 3, 'b': EqButNotIs(3), 'c': 3})
1052 d['b'] = EqButNotIs(3)
1057 d['b'] = EqButNotIs(3)
1063 d['a'] = EqButNotIs(3)
1068 fake3 = EqButNotIs(3)
1069 fake7 = EqButNotIs(7)
1073 self.failUnless(filter(lambda x: x is 8, d.itervalues()))
1074 self.failUnless(filter(lambda x: x is fake7, d.itervalues()))
1075 # The real 7 should have been ejected by the d[3] = 8.
1076 self.failUnless(not filter(lambda x: x is 7, d.itervalues()))
1077 self.failUnless(filter(lambda x: x is fake3, d.iterkeys()))
1078 self.failUnless(filter(lambda x: x is 3, d.iterkeys()))
1083 fake3 = EqButNotIs(3)
1084 fake7 = EqButNotIs(7)
1087 self.failUnless(filter(lambda x: x is 8, d.itervalues()))
1088 self.failUnless(filter(lambda x: x is fake7, d.itervalues()))
1089 # The real 7 should have been ejected by the d[3] = 8.
1090 self.failUnless(not filter(lambda x: x is 7, d.itervalues()))
1091 self.failUnless(filter(lambda x: x is fake3, d.iterkeys()))
1092 self.failUnless(filter(lambda x: x is 3, d.iterkeys()))
1096 self._help_test_eq_but_notis(dictutil.UtilDict)
1097 self._help_test_eq_but_notis(dictutil.NumDict)
1098 self._help_test_eq_but_notis(dictutil.ValueOrderedDict)
1099 self._help_test_nonempty_dict(dictutil.UtilDict)
1100 self._help_test_nonempty_dict(dictutil.NumDict)
1101 self._help_test_nonempty_dict(dictutil.ValueOrderedDict)
1102 self._help_test_eq_but_notis(dictutil.UtilDict)
1103 self._help_test_eq_but_notis(dictutil.NumDict)
1104 self._help_test_eq_but_notis(dictutil.ValueOrderedDict)
1106 def test_dict_of_sets(self):
1107 ds = dictutil.DictOfSets()
1112 self.failUnlessEqual(ds[1], set(["a"]))
1113 self.failUnlessEqual(ds[2], set(["b", "c"]))
1114 ds.discard(3, "d") # should not raise an exception
1116 self.failUnlessEqual(ds[2], set(["c"]))
1118 self.failIf(2 in ds)
1121 ds2 = dictutil.DictOfSets()
1126 self.failUnlessEqual(ds[1], set(["a"]))
1127 self.failUnlessEqual(ds[3], set(["f", "g"]))
1128 self.failUnlessEqual(ds[4], set(["h"]))
1130 def test_move(self):
1131 d1 = {1: "a", 2: "b"}
1132 d2 = {2: "c", 3: "d"}
1133 dictutil.move(1, d1, d2)
1134 self.failUnlessEqual(d1, {2: "b"})
1135 self.failUnlessEqual(d2, {1: "a", 2: "c", 3: "d"})
1137 d1 = {1: "a", 2: "b"}
1138 d2 = {2: "c", 3: "d"}
1139 dictutil.move(2, d1, d2)
1140 self.failUnlessEqual(d1, {1: "a"})
1141 self.failUnlessEqual(d2, {2: "b", 3: "d"})
1143 d1 = {1: "a", 2: "b"}
1144 d2 = {2: "c", 3: "d"}
1145 self.failUnlessRaises(KeyError, dictutil.move, 5, d1, d2, strict=True)
1147 def test_subtract(self):
1148 d1 = {1: "a", 2: "b"}
1149 d2 = {2: "c", 3: "d"}
1150 d3 = dictutil.subtract(d1, d2)
1151 self.failUnlessEqual(d3, {1: "a"})
1153 d1 = {1: "a", 2: "b"}
1155 d3 = dictutil.subtract(d1, d2)
1156 self.failUnlessEqual(d3, {1: "a"})
1158 def test_utildict(self):
1159 d = dictutil.UtilDict({1: "a", 2: "b"})
1162 self.failUnlessEqual(d, {2: "b"})
1165 self.failUnlessRaises(TypeError, eq, d, "not a dict")
1167 d = dictutil.UtilDict({1: "b", 2: "a"})
1168 self.failUnlessEqual(d.items_sorted_by_value(),
1169 [(2, "a"), (1, "b")])
1170 self.failUnlessEqual(d.items_sorted_by_key(),
1171 [(1, "b"), (2, "a")])
1172 self.failUnlessEqual(repr(d), "{1: 'b', 2: 'a'}")
1173 self.failUnless(1 in d)
1175 d2 = dictutil.UtilDict({3: "c", 4: "d"})
1176 self.failUnless(d != d2)
1177 self.failUnless(d2 > d)
1178 self.failUnless(d2 >= d)
1179 self.failUnless(d <= d2)
1180 self.failUnless(d < d2)
1181 self.failUnlessEqual(d[1], "b")
1182 self.failUnlessEqual(sorted(list([k for k in d])), [1,2])
1185 self.failUnlessEqual(d, d3)
1186 self.failUnless(isinstance(d3, dictutil.UtilDict))
1188 d4 = d.fromkeys([3,4], "e")
1189 self.failUnlessEqual(d4, {3: "e", 4: "e"})
1191 self.failUnlessEqual(d.get(1), "b")
1192 self.failUnlessEqual(d.get(3), None)
1193 self.failUnlessEqual(d.get(3, "default"), "default")
1194 self.failUnlessEqual(sorted(list(d.items())),
1195 [(1, "b"), (2, "a")])
1196 self.failUnlessEqual(sorted(list(d.iteritems())),
1197 [(1, "b"), (2, "a")])
1198 self.failUnlessEqual(sorted(d.keys()), [1, 2])
1199 self.failUnlessEqual(sorted(d.values()), ["a", "b"])
1200 x = d.setdefault(1, "new")
1201 self.failUnlessEqual(x, "b")
1202 self.failUnlessEqual(d[1], "b")
1203 x = d.setdefault(3, "new")
1204 self.failUnlessEqual(x, "new")
1205 self.failUnlessEqual(d[3], "new")
1209 self.failUnless(x in [(1, "b"), (2, "a")])
1211 self.failUnless(x in [(1, "b"), (2, "a")])
1212 self.failUnlessRaises(KeyError, d.popitem)
1214 def test_numdict(self):
1215 d = dictutil.NumDict({"a": 1, "b": 2})
1217 d.add_num("a", 10, 5)
1218 d.add_num("c", 20, 5)
1220 self.failUnlessEqual(d, {"a": 11, "b": 2, "c": 25, "d": 30})
1222 d.subtract_num("a", 10)
1223 d.subtract_num("e", 10)
1224 d.subtract_num("f", 10, 15)
1225 self.failUnlessEqual(d, {"a": 1, "b": 2, "c": 25, "d": 30,
1228 self.failUnlessEqual(d.sum(), sum([1, 2, 25, 30, -10, 5]))
1230 d = dictutil.NumDict()
1234 self.failUnlessEqual(d, {"a": 2, "b": 6})
1238 self.failUnlessEqual(d, {"a": 1, "b": 6, "c": -1, "d": 4})
1239 self.failUnlessEqual(d.items_sorted_by_key(),
1240 [("a", 1), ("b", 6), ("c", -1), ("d", 4)])
1241 self.failUnlessEqual(d.items_sorted_by_value(),
1242 [("c", -1), ("a", 1), ("d", 4), ("b", 6)])
1243 self.failUnlessEqual(d.item_with_largest_value(), ("b", 6))
1245 d = dictutil.NumDict({"a": 1, "b": 2})
1246 self.failUnlessEqual(repr(d), "{'a': 1, 'b': 2}")
1247 self.failUnless("a" in d)
1249 d2 = dictutil.NumDict({"c": 3, "d": 4})
1250 self.failUnless(d != d2)
1251 self.failUnless(d2 > d)
1252 self.failUnless(d2 >= d)
1253 self.failUnless(d <= d2)
1254 self.failUnless(d < d2)
1255 self.failUnlessEqual(d["a"], 1)
1256 self.failUnlessEqual(sorted(list([k for k in d])), ["a","b"])
1259 self.failUnlessRaises(TypeError, eq, d, "not a dict")
1262 self.failUnlessEqual(d, d3)
1263 self.failUnless(isinstance(d3, dictutil.NumDict))
1265 d4 = d.fromkeys(["a","b"], 5)
1266 self.failUnlessEqual(d4, {"a": 5, "b": 5})
1268 self.failUnlessEqual(d.get("a"), 1)
1269 self.failUnlessEqual(d.get("c"), 0)
1270 self.failUnlessEqual(d.get("c", 5), 5)
1271 self.failUnlessEqual(sorted(list(d.items())),
1272 [("a", 1), ("b", 2)])
1273 self.failUnlessEqual(sorted(list(d.iteritems())),
1274 [("a", 1), ("b", 2)])
1275 self.failUnlessEqual(sorted(d.keys()), ["a", "b"])
1276 self.failUnlessEqual(sorted(d.values()), [1, 2])
1277 self.failUnless(d.has_key("a"))
1278 self.failIf(d.has_key("c"))
1280 x = d.setdefault("c", 3)
1281 self.failUnlessEqual(x, 3)
1282 self.failUnlessEqual(d["c"], 3)
1283 x = d.setdefault("c", 5)
1284 self.failUnlessEqual(x, 3)
1285 self.failUnlessEqual(d["c"], 3)
1289 self.failUnless(x in [("a", 1), ("b", 2)])
1291 self.failUnless(x in [("a", 1), ("b", 2)])
1292 self.failUnlessRaises(KeyError, d.popitem)
1295 d.update({"c": 4, "d": 5})
1296 self.failUnlessEqual(d, {"c": 4, "d": 5})
1298 def test_del_if_present(self):
1299 d = {1: "a", 2: "b"}
1300 dictutil.del_if_present(d, 1)
1301 dictutil.del_if_present(d, 3)
1302 self.failUnlessEqual(d, {2: "b"})
1304 def test_valueordereddict(self):
1305 d = dictutil.ValueOrderedDict()
1310 self.failUnlessEqual(d, {"a": 3, "b": 2, "c": 1})
1311 self.failUnlessEqual(d.items(), [("c", 1), ("b", 2), ("a", 3)])
1312 self.failUnlessEqual(d.values(), [1, 2, 3])
1313 self.failUnlessEqual(d.keys(), ["c", "b", "a"])
1314 self.failUnlessEqual(repr(d), "<ValueOrderedDict {c: 1, b: 2, a: 3}>")
1317 self.failIf(d == {"a": 4})
1318 self.failUnless(d != {"a": 4})
1320 x = d.setdefault("d", 0)
1321 self.failUnlessEqual(x, 0)
1322 self.failUnlessEqual(d["d"], 0)
1323 x = d.setdefault("d", -1)
1324 self.failUnlessEqual(x, 0)
1325 self.failUnlessEqual(d["d"], 0)
1327 x = d.remove("e", "default", False)
1328 self.failUnlessEqual(x, "default")
1329 self.failUnlessRaises(KeyError, d.remove, "e", "default", True)
1330 x = d.remove("d", 5)
1331 self.failUnlessEqual(x, 0)
1333 x = d.__getitem__("c")
1334 self.failUnlessEqual(x, 1)
1335 x = d.__getitem__("e", "default", False)
1336 self.failUnlessEqual(x, "default")
1337 self.failUnlessRaises(KeyError, d.__getitem__, "e", "default", True)
1339 self.failUnlessEqual(d.popitem(), ("c", 1))
1340 self.failUnlessEqual(d.popitem(), ("b", 2))
1341 self.failUnlessEqual(d.popitem(), ("a", 3))
1342 self.failUnlessRaises(KeyError, d.popitem)
1344 d = dictutil.ValueOrderedDict({"a": 3, "b": 2, "c": 1})
1345 x = d.pop("d", "default", False)
1346 self.failUnlessEqual(x, "default")
1347 self.failUnlessRaises(KeyError, d.pop, "d", "default", True)
1349 self.failUnlessEqual(x, 2)
1350 self.failUnlessEqual(d.items(), [("c", 1), ("a", 3)])
1352 d = dictutil.ValueOrderedDict({"a": 3, "b": 2, "c": 1})
1353 x = d.pop_from_list(1) # pop the second item, b/2
1354 self.failUnlessEqual(x, "b")
1355 self.failUnlessEqual(d.items(), [("c", 1), ("a", 3)])
1357 def test_auxdict(self):
1358 d = dictutil.AuxValueDict()
1359 # we put the serialized form in the auxdata
1360 d.set_with_aux("key", ("filecap", "metadata"), "serialized")
1362 self.failUnlessEqual(d.keys(), ["key"])
1363 self.failUnlessEqual(d["key"], ("filecap", "metadata"))
1364 self.failUnlessEqual(d.get_aux("key"), "serialized")
1365 def _get_missing(key):
1367 self.failUnlessRaises(KeyError, _get_missing, "nonkey")
1368 self.failUnlessEqual(d.get("nonkey"), None)
1369 self.failUnlessEqual(d.get("nonkey", "nonvalue"), "nonvalue")
1370 self.failUnlessEqual(d.get_aux("nonkey"), None)
1371 self.failUnlessEqual(d.get_aux("nonkey", "nonvalue"), "nonvalue")
1373 d["key"] = ("filecap2", "metadata2")
1374 self.failUnlessEqual(d["key"], ("filecap2", "metadata2"))
1375 self.failUnlessEqual(d.get_aux("key"), None)
1377 d.set_with_aux("key2", "value2", "aux2")
1378 self.failUnlessEqual(sorted(d.keys()), ["key", "key2"])
1380 self.failUnlessEqual(d.keys(), ["key"])
1381 self.failIf("key2" in d)
1382 self.failUnlessRaises(KeyError, _get_missing, "key2")
1383 self.failUnlessEqual(d.get("key2"), None)
1384 self.failUnlessEqual(d.get_aux("key2"), None)
1385 d["key2"] = "newvalue2"
1386 self.failUnlessEqual(d.get("key2"), "newvalue2")
1387 self.failUnlessEqual(d.get_aux("key2"), None)
1389 d = dictutil.AuxValueDict({1:2,3:4})
1390 self.failUnlessEqual(sorted(d.keys()), [1,3])
1391 self.failUnlessEqual(d[1], 2)
1392 self.failUnlessEqual(d.get_aux(1), 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(one=1, two=2)
1400 self.failUnlessEqual(sorted(d.keys()), ["one","two"])
1401 self.failUnlessEqual(d["one"], 1)
1402 self.failUnlessEqual(d.get_aux("one"), None)
1404 class Pipeline(unittest.TestCase):
1405 def pause(self, *args, **kwargs):
1406 d = defer.Deferred()
1407 self.calls.append( (d, args, kwargs) )
1410 def failUnlessCallsAre(self, expected):
1413 self.failUnlessEqual(len(self.calls), len(expected), self.calls)
1414 for i,c in enumerate(self.calls):
1415 self.failUnlessEqual(c[1:], expected[i], str(i))
1417 def test_basic(self):
1420 p = pipeline.Pipeline(100)
1422 d = p.flush() # fires immediately
1423 d.addCallbacks(finished.append, log.err)
1424 self.failUnlessEqual(len(finished), 1)
1427 d = p.add(10, self.pause, "one")
1428 # the call should start right away, and our return Deferred should
1430 d.addCallbacks(finished.append, log.err)
1431 self.failUnlessEqual(len(finished), 1)
1432 self.failUnlessEqual(finished[0], None)
1433 self.failUnlessCallsAre([ ( ("one",) , {} ) ])
1434 self.failUnlessEqual(p.gauge, 10)
1439 d = p.add(20, self.pause, "two", kw=2)
1440 # pipeline: [one, two]
1442 # the call and the Deferred should fire right away
1443 d.addCallbacks(finished.append, log.err)
1444 self.failUnlessEqual(len(finished), 1)
1445 self.failUnlessEqual(finished[0], None)
1446 self.failUnlessCallsAre([ ( ("one",) , {} ),
1447 ( ("two",) , {"kw": 2} ),
1449 self.failUnlessEqual(p.gauge, 30)
1451 self.calls[0][0].callback("one-result")
1453 self.failUnlessEqual(p.gauge, 20)
1456 d = p.add(90, self.pause, "three", "posarg1")
1457 # pipeline: [two, three]
1460 fd.addCallbacks(flushed.append, log.err)
1461 self.failUnlessEqual(flushed, [])
1463 # the call will be made right away, but the return Deferred will not,
1464 # because the pipeline is now full.
1465 d.addCallbacks(finished.append, log.err)
1466 self.failUnlessEqual(len(finished), 0)
1467 self.failUnlessCallsAre([ ( ("one",) , {} ),
1468 ( ("two",) , {"kw": 2} ),
1469 ( ("three", "posarg1"), {} ),
1471 self.failUnlessEqual(p.gauge, 110)
1473 self.failUnlessRaises(pipeline.SingleFileError, p.add, 10, self.pause)
1475 # retiring either call will unblock the pipeline, causing the #3
1477 self.calls[2][0].callback("three-result")
1480 self.failUnlessEqual(len(finished), 1)
1481 self.failUnlessEqual(finished[0], None)
1482 self.failUnlessEqual(flushed, [])
1484 # retiring call#2 will finally allow the flush() Deferred to fire
1485 self.calls[1][0].callback("two-result")
1486 self.failUnlessEqual(len(flushed), 1)
1488 def test_errors(self):
1490 p = pipeline.Pipeline(100)
1492 d1 = p.add(200, self.pause, "one")
1496 d1.addBoth(finished.append)
1497 self.failUnlessEqual(finished, [])
1500 d2.addBoth(flushed.append)
1501 self.failUnlessEqual(flushed, [])
1503 self.calls[0][0].errback(ValueError("oops"))
1505 self.failUnlessEqual(len(finished), 1)
1507 self.failUnless(isinstance(f, Failure))
1508 self.failUnless(f.check(pipeline.PipelineError))
1509 self.failUnlessIn("PipelineError", str(f.value))
1510 self.failUnlessIn("ValueError", str(f.value))
1512 self.failUnless("ValueError" in r, r)
1514 self.failUnless(f2.check(ValueError))
1516 self.failUnlessEqual(len(flushed), 1)
1518 self.failUnless(isinstance(f, Failure))
1519 self.failUnless(f.check(pipeline.PipelineError))
1521 self.failUnless(f2.check(ValueError))
1523 # now that the pipeline is in the failed state, any new calls will
1526 d3 = p.add(20, self.pause, "two")
1529 d3.addBoth(finished.append)
1530 self.failUnlessEqual(len(finished), 1)
1532 self.failUnless(isinstance(f, Failure))
1533 self.failUnless(f.check(pipeline.PipelineError))
1535 self.failUnless("ValueError" in r, r)
1537 self.failUnless(f2.check(ValueError))
1541 d4.addBoth(flushed.append)
1542 self.failUnlessEqual(len(flushed), 1)
1544 self.failUnless(isinstance(f, Failure))
1545 self.failUnless(f.check(pipeline.PipelineError))
1547 self.failUnless(f2.check(ValueError))
1549 def test_errors2(self):
1551 p = pipeline.Pipeline(100)
1553 d1 = p.add(10, self.pause, "one")
1554 d2 = p.add(20, self.pause, "two")
1555 d3 = p.add(30, self.pause, "three")
1558 # one call fails, then the second one succeeds: make sure
1559 # ExpandableDeferredList tolerates the second one
1562 d4.addBoth(flushed.append)
1563 self.failUnlessEqual(flushed, [])
1565 self.calls[0][0].errback(ValueError("oops"))
1566 self.failUnlessEqual(len(flushed), 1)
1568 self.failUnless(isinstance(f, Failure))
1569 self.failUnless(f.check(pipeline.PipelineError))
1571 self.failUnless(f2.check(ValueError))
1573 self.calls[1][0].callback("two-result")
1574 self.calls[2][0].errback(ValueError("three-error"))
1578 class SampleError(Exception):
1581 class Log(unittest.TestCase):
1583 if not hasattr(self, "flushLoggedErrors"):
1584 # without flushLoggedErrors, we can't get rid of the
1585 # twisted.log.err that tahoe_log records, so we can't keep this
1586 # test from [ERROR]ing
1587 raise unittest.SkipTest("needs flushLoggedErrors from Twisted-2.5.0")
1589 raise SampleError("simple sample")
1592 tahoe_log.err(format="intentional sample error",
1593 failure=f, level=tahoe_log.OPERATIONAL, umid="wO9UoQ")
1594 self.flushLoggedErrors(SampleError)
1598 # this is a simple+inefficient form of util.spans.Spans . We compare the
1599 # behavior of this reference model against the real (efficient) form.
1601 def __init__(self, _span_or_start=None, length=None):
1603 if length is not None:
1604 for i in range(_span_or_start, _span_or_start+length):
1606 elif _span_or_start:
1607 for (start,length) in _span_or_start:
1608 self.add(start, length)
1610 def add(self, start, length):
1611 for i in range(start, start+length):
1615 def remove(self, start, length):
1616 for i in range(start, start+length):
1617 self._have.discard(i)
1621 return sorted(self._have)
1624 items = sorted(self._have)
1628 if prevstart is None:
1629 prevstart = prevend = i
1634 yield (prevstart, prevend-prevstart+1)
1635 prevstart = prevend = i
1636 if prevstart is not None:
1637 yield (prevstart, prevend-prevstart+1)
1639 def __nonzero__(self): # this gets us bool()
1643 return len(self._have)
1645 def __add__(self, other):
1646 s = self.__class__(self)
1647 for (start, length) in other:
1648 s.add(start, length)
1651 def __sub__(self, other):
1652 s = self.__class__(self)
1653 for (start, length) in other:
1654 s.remove(start, length)
1657 def __iadd__(self, other):
1658 for (start, length) in other:
1659 self.add(start, length)
1662 def __isub__(self, other):
1663 for (start, length) in other:
1664 self.remove(start, length)
1667 def __and__(self, other):
1668 s = self.__class__()
1669 for i in other.each():
1674 def __contains__(self, (start,length)):
1675 for i in range(start, start+length):
1676 if i not in self._have:
1680 class ByteSpans(unittest.TestCase):
1681 def test_basic(self):
1683 self.failUnlessEqual(list(s), [])
1685 self.failIf((0,1) in s)
1686 self.failUnlessEqual(s.len(), 0)
1688 s1 = Spans(3, 4) # 3,4,5,6
1691 s1 = Spans(3L, 4L) # 3,4,5,6
1697 s2.add(10,2) # 10,11
1699 self.failUnless((10,1) in s2)
1700 self.failIf((10,1) in s1)
1701 self.failUnlessEqual(list(s2.each()), [3,4,5,6,10,11])
1702 self.failUnlessEqual(s2.len(), 6)
1704 s2.add(15,2).add(20,2)
1705 self.failUnlessEqual(list(s2.each()), [3,4,5,6,10,11,15,16,20,21])
1706 self.failUnlessEqual(s2.len(), 10)
1708 s2.remove(4,3).remove(15,1)
1709 self.failUnlessEqual(list(s2.each()), [3,10,11,16,20,21])
1710 self.failUnlessEqual(s2.len(), 6)
1712 s1 = SimpleSpans(3, 4) # 3 4 5 6
1713 s2 = SimpleSpans(5, 4) # 5 6 7 8
1715 self.failUnlessEqual(list(i.each()), [5, 6])
1717 def _check1(self, s):
1718 self.failUnlessEqual(list(s), [(3,4)])
1720 self.failUnlessEqual(s.len(), 4)
1721 self.failIf((0,1) in s)
1722 self.failUnless((3,4) in s)
1723 self.failUnless((3,1) in s)
1724 self.failUnless((5,2) in s)
1725 self.failUnless((6,1) in s)
1726 self.failIf((6,2) in s)
1727 self.failIf((7,1) in s)
1728 self.failUnlessEqual(list(s.each()), [3,4,5,6])
1730 def test_large(self):
1731 s = Spans(4, 2**65) # don't do this with a SimpleSpans
1732 self.failUnlessEqual(list(s), [(4, 2**65)])
1734 self.failUnlessEqual(s.len(), 2**65)
1735 self.failIf((0,1) in s)
1736 self.failUnless((4,2) in s)
1737 self.failUnless((2**65,2) in s)
1739 def test_math(self):
1740 s1 = Spans(0, 10) # 0,1,2,3,4,5,6,7,8,9
1741 s2 = Spans(5, 3) # 5,6,7
1742 s3 = Spans(8, 4) # 8,9,10,11
1745 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,8,9])
1747 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7])
1749 self.failUnlessEqual(list(s.each()), [5,6,7])
1751 self.failUnlessEqual(list(s.each()), [5,6,7])
1753 self.failUnlessEqual(list(s.each()), [5,6,7])
1755 self.failUnlessEqual(list(s.each()), [8,9])
1757 self.failUnlessEqual(list(s.each()), [8,9])
1759 self.failUnlessEqual(list(s.each()), [])
1761 self.failUnlessEqual(list(s.each()), [])
1763 self.failUnlessEqual(list(s.each()), [])
1765 self.failUnlessEqual(list(s.each()), [])
1768 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7,8,9])
1770 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7,8,9,10,11])
1772 self.failUnlessEqual(list(s.each()), [5,6,7,8,9,10,11])
1776 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,8,9])
1779 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7])
1782 self.failUnlessEqual(list(s.each()), [5,6,7])
1786 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7,8,9])
1789 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7,8,9,10,11])
1792 self.failUnlessEqual(list(s.each()), [5,6,7,8,9,10,11])
1794 def test_random(self):
1795 # attempt to increase coverage of corner cases by comparing behavior
1796 # of a simple-but-slow model implementation against the
1797 # complex-but-fast actual implementation, in a large number of random
1801 s1 = S1(); s2 = S2()
1803 def _create(subseed):
1804 ns1 = S1(); ns2 = S2()
1806 what = _hash(subseed+str(i)).hexdigest()
1807 start = int(what[2:4], 16)
1808 length = max(1,int(what[5:6], 16))
1809 ns1.add(start, length); ns2.add(start, length)
1813 for i in range(1000):
1814 what = _hash(seed+str(i)).hexdigest()
1817 start = int(what[2:4], 16)
1818 length = max(1,int(what[5:6], 16))
1821 if subop in "01234":
1822 s1 = S1(); s2 = S2()
1823 elif subop in "5678":
1824 s1 = S1(start, length); s2 = S2(start, length)
1826 s1 = S1(s1); s2 = S2(s2)
1827 #print "s2 = %s" % s2.dump()
1829 #print "s2.add(%d,%d)" % (start, length)
1830 s1.add(start, length); s2.add(start, length)
1832 #print "s2.remove(%d,%d)" % (start, length)
1833 s1.remove(start, length); s2.remove(start, length)
1835 ns1, ns2 = _create(what[7:11])
1836 #print "s2 + %s" % ns2.dump()
1837 s1 = s1 + ns1; s2 = s2 + ns2
1839 ns1, ns2 = _create(what[7:11])
1840 #print "%s - %s" % (s2.dump(), ns2.dump())
1841 s1 = s1 - ns1; s2 = s2 - ns2
1843 ns1, ns2 = _create(what[7:11])
1844 #print "s2 += %s" % ns2.dump()
1845 s1 += ns1; s2 += ns2
1847 ns1, ns2 = _create(what[7:11])
1848 #print "%s -= %s" % (s2.dump(), ns2.dump())
1849 s1 -= ns1; s2 -= ns2
1851 ns1, ns2 = _create(what[7:11])
1852 #print "%s &= %s" % (s2.dump(), ns2.dump())
1853 s1 = s1 & ns1; s2 = s2 & ns2
1854 #print "s2 now %s" % s2.dump()
1855 self.failUnlessEqual(list(s1.each()), list(s2.each()))
1856 self.failUnlessEqual(s1.len(), s2.len())
1857 self.failUnlessEqual(bool(s1), bool(s2))
1858 self.failUnlessEqual(list(s1), list(s2))
1860 what = _hash(what[12:14]+str(j)).hexdigest()
1861 start = int(what[2:4], 16)
1862 length = max(1, int(what[5:6], 16))
1863 span = (start, length)
1864 self.failUnlessEqual(bool(span in s1), bool(span in s2))
1870 # s.add(start,length) : returns s
1871 # s.remove(start,length)
1872 # s.each() -> list of byte offsets, mostly for testing
1873 # list(s) -> list of (start,length) tuples, one per span
1874 # (start,length) in s -> True if (start..start+length-1) are all members
1875 # NOT equivalent to x in list(s)
1876 # s.len() -> number of bytes, for testing, bool(), and accounting/limiting
1877 # bool(s) (__nonzeron__)
1878 # s = s1+s2, s1-s2, +=s1, -=s1
1880 def test_overlap(self):
1885 self._test_overlap(a,b,c,d)
1887 def _test_overlap(self, a, b, c, d):
1888 s1 = set(range(a,a+b))
1889 s2 = set(range(c,c+d))
1891 #self._show_overlap(s1, "1")
1892 #self._show_overlap(s2, "2")
1893 o = overlap(a,b,c,d)
1894 expected = s1.intersection(s2)
1896 self.failUnlessEqual(o, None)
1899 so = set(range(start,start+length))
1900 #self._show(so, "o")
1901 self.failUnlessEqual(so, expected)
1903 def _show_overlap(self, s, c):
1907 for i in range(max(s)):
1914 def extend(s, start, length, fill):
1915 if len(s) >= start+length:
1917 assert len(fill) == 1
1918 return s + fill*(start+length-len(s))
1920 def replace(s, start, data):
1921 assert len(s) >= start+len(data)
1922 return s[:start] + data + s[start+len(data):]
1924 class SimpleDataSpans:
1925 def __init__(self, other=None):
1926 self.missing = "" # "1" where missing, "0" where found
1929 for (start, data) in other.get_chunks():
1930 self.add(start, data)
1932 def __nonzero__(self): # this gets us bool()
1935 return len(self.missing.replace("1", ""))
1937 return [i for (i,c) in enumerate(self.missing) if c == "0"]
1938 def _have(self, start, length):
1939 m = self.missing[start:start+length]
1940 if not m or len(m)<length or int(m):
1943 def get_chunks(self):
1944 for i in self._dump():
1945 yield (i, self.data[i])
1946 def get_spans(self):
1947 return SimpleSpans([(start,len(data))
1948 for (start,data) in self.get_chunks()])
1949 def get(self, start, length):
1950 if self._have(start, length):
1951 return self.data[start:start+length]
1953 def pop(self, start, length):
1954 data = self.get(start, length)
1956 self.remove(start, length)
1958 def remove(self, start, length):
1959 self.missing = replace(extend(self.missing, start, length, "1"),
1961 def add(self, start, data):
1962 self.missing = replace(extend(self.missing, start, len(data), "1"),
1963 start, "0"*len(data))
1964 self.data = replace(extend(self.data, start, len(data), " "),
1968 class StringSpans(unittest.TestCase):
1969 def do_basic(self, klass):
1971 self.failUnlessEqual(ds.len(), 0)
1972 self.failUnlessEqual(list(ds._dump()), [])
1973 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 0)
1975 self.failUnlessEqual(ds.get(0, 4), None)
1976 self.failUnlessEqual(ds.pop(0, 4), None)
1980 self.failUnlessEqual(ds.len(), 4)
1981 self.failUnlessEqual(list(ds._dump()), [2,3,4,5])
1982 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 4)
1984 self.failUnless((2,2) in s1)
1985 self.failUnlessEqual(ds.get(0, 4), None)
1986 self.failUnlessEqual(ds.pop(0, 4), None)
1987 self.failUnlessEqual(ds.get(4, 4), None)
1990 self.failUnlessEqual(ds2.len(), 4)
1991 self.failUnlessEqual(list(ds2._dump()), [2,3,4,5])
1992 self.failUnlessEqual(sum([len(d) for (s,d) in ds2.get_chunks()]), 4)
1993 self.failUnlessEqual(ds2.get(0, 4), None)
1994 self.failUnlessEqual(ds2.pop(0, 4), None)
1995 self.failUnlessEqual(ds2.pop(2, 3), "fou")
1996 self.failUnlessEqual(sum([len(d) for (s,d) in ds2.get_chunks()]), 1)
1997 self.failUnlessEqual(ds2.get(2, 3), None)
1998 self.failUnlessEqual(ds2.get(5, 1), "r")
1999 self.failUnlessEqual(ds.get(2, 3), "fou")
2000 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 4)
2003 self.failUnlessEqual(ds.len(), 6)
2004 self.failUnlessEqual(list(ds._dump()), [0,1,2,3,4,5])
2005 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 6)
2006 self.failUnlessEqual(ds.get(0, 4), "23fo")
2007 self.failUnlessEqual(ds.pop(0, 4), "23fo")
2008 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 2)
2009 self.failUnlessEqual(ds.get(0, 4), None)
2010 self.failUnlessEqual(ds.pop(0, 4), None)
2015 self.failUnlessEqual(ds.get(2, 4), "fear")
2020 self.failUnlessEqual(ds.get(2L, 4L), "fear")
2023 def do_scan(self, klass):
2024 # do a test with gaps and spans of size 1 and 2
2025 # left=(1,11) * right=(1,11) * gapsize=(1,2)
2026 # 111, 112, 121, 122, 211, 212, 221, 222
2035 # 11 1 1 11 11 11 1 1 111
2036 # 0123456789012345678901234567
2037 # abcdefghijklmnopqrstuvwxyz-=
2038 pieces = [(1, "bc"),
2048 p_elements = set([1,2,4,7,9,10,12,13,16,17,20,22,25,26,27])
2049 S = "abcdefghijklmnopqrstuvwxyz-="
2050 # TODO: when adding data, add capital letters, to make sure we aren't
2051 # just leaving the old data in place
2055 for start, data in pieces:
2060 d = "".join([((i not in p) and " " or S[i]) for i in range(l)])
2064 for start in range(0, l):
2065 for end in range(start+1, l):
2066 # add [start-end) to the baseline
2067 which = "%d-%d" % (start, end-1)
2068 p_added = set(range(start, end))
2072 print dump(b), which
2073 add = klass(); add.add(start, S[start:end])
2075 b.add(start, S[start:end])
2078 # check that the new span is there
2079 d = b.get(start, end-start)
2080 self.failUnlessEqual(d, S[start:end], which)
2081 # check that all the original pieces are still there
2082 for t_start, t_data in pieces:
2084 self.failUnlessEqual(b.get(t_start, t_len),
2085 S[t_start:t_start+t_len],
2086 "%s %d+%d" % (which, t_start, t_len))
2087 # check that a lot of subspans are mostly correct
2088 for t_start in range(l):
2089 for t_len in range(1,4):
2090 d = b.get(t_start, t_len)
2092 which2 = "%s+(%d-%d)" % (which, t_start,
2094 self.failUnlessEqual(d, S[t_start:t_start+t_len],
2096 # check that removing a subspan gives the right value
2098 b2.remove(t_start, t_len)
2099 removed = set(range(t_start, t_start+t_len))
2101 exp = (((i in p_elements) or (i in p_added))
2102 and (i not in removed))
2103 which2 = "%s-(%d-%d)" % (which, t_start,
2105 self.failUnlessEqual(bool(b2.get(i, 1)), exp,
2108 def test_test(self):
2109 self.do_basic(SimpleDataSpans)
2110 self.do_scan(SimpleDataSpans)
2112 def test_basic(self):
2113 self.do_basic(DataSpans)
2114 self.do_scan(DataSpans)
2116 def test_random(self):
2117 # attempt to increase coverage of corner cases by comparing behavior
2118 # of a simple-but-slow model implementation against the
2119 # complex-but-fast actual implementation, in a large number of random
2121 S1 = SimpleDataSpans
2123 s1 = S1(); s2 = S2()
2125 def _randstr(length, seed):
2128 while created < length:
2129 piece = _hash(seed + str(created)).hexdigest()
2130 pieces.append(piece)
2131 created += len(piece)
2132 return "".join(pieces)[:length]
2133 def _create(subseed):
2134 ns1 = S1(); ns2 = S2()
2136 what = _hash(subseed+str(i)).hexdigest()
2137 start = int(what[2:4], 16)
2138 length = max(1,int(what[5:6], 16))
2139 ns1.add(start, _randstr(length, what[7:9]));
2140 ns2.add(start, _randstr(length, what[7:9]))
2144 for i in range(1000):
2145 what = _hash(seed+str(i)).hexdigest()
2148 start = int(what[2:4], 16)
2149 length = max(1,int(what[5:6], 16))
2152 if subop in "0123456":
2153 s1 = S1(); s2 = S2()
2155 s1, s2 = _create(what[7:11])
2156 #print "s2 = %s" % list(s2._dump())
2157 elif op in "123456":
2158 #print "s2.add(%d,%d)" % (start, length)
2159 s1.add(start, _randstr(length, what[7:9]));
2160 s2.add(start, _randstr(length, what[7:9]))
2161 elif op in "789abc":
2162 #print "s2.remove(%d,%d)" % (start, length)
2163 s1.remove(start, length); s2.remove(start, length)
2165 #print "s2.pop(%d,%d)" % (start, length)
2166 d1 = s1.pop(start, length); d2 = s2.pop(start, length)
2167 self.failUnlessEqual(d1, d2)
2168 #print "s1 now %s" % list(s1._dump())
2169 #print "s2 now %s" % list(s2._dump())
2170 self.failUnlessEqual(s1.len(), s2.len())
2171 self.failUnlessEqual(list(s1._dump()), list(s2._dump()))
2172 for j in range(100):
2173 what = _hash(what[12:14]+str(j)).hexdigest()
2174 start = int(what[2:4], 16)
2175 length = max(1, int(what[5:6], 16))
2176 d1 = s1.get(start, length); d2 = s2.get(start, length)
2177 self.failUnlessEqual(d1, d2, "%d+%d" % (start, length))