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
18 from allmydata.test.common_util import ReallyEqualMixin
21 class Base32(unittest.TestCase):
22 def test_b2a_matches_Pythons(self):
24 y = "\x12\x34\x45\x67\x89\x0a\xbc\xde\xf0"
25 x = base64.b32encode(y)
26 while x and x[-1] == '=':
29 self.failUnlessEqual(base32.b2a(y), x)
31 self.failUnlessEqual(base32.b2a("\x12\x34"), "ci2a")
32 def test_b2a_or_none(self):
33 self.failUnlessEqual(base32.b2a_or_none(None), None)
34 self.failUnlessEqual(base32.b2a_or_none("\x12\x34"), "ci2a")
36 self.failUnlessEqual(base32.a2b("ci2a"), "\x12\x34")
37 self.failUnlessRaises(AssertionError, base32.a2b, "b0gus")
39 class IDLib(unittest.TestCase):
40 def test_nodeid_b2a(self):
41 self.failUnlessEqual(idlib.nodeid_b2a("\x00"*20), "a"*32)
43 class NoArgumentException(Exception):
47 class HumanReadable(unittest.TestCase):
50 self.failUnlessEqual(hr(foo), "<foo() at test_util.py:2>")
51 self.failUnlessEqual(hr(self.test_repr),
52 "<bound method HumanReadable.test_repr of <allmydata.test.test_util.HumanReadable testMethod=test_repr>>")
53 self.failUnlessEqual(hr(1L), "1")
54 self.failUnlessEqual(hr(10**40),
55 "100000000000000000...000000000000000000")
56 self.failUnlessEqual(hr(self), "<allmydata.test.test_util.HumanReadable testMethod=test_repr>")
57 self.failUnlessEqual(hr([1,2]), "[1, 2]")
58 self.failUnlessEqual(hr({1:2}), "{1:2}")
63 hr(e) == "<ValueError: ()>" # python-2.4
64 or hr(e) == "ValueError()") # python-2.5
66 raise ValueError("oops")
69 hr(e) == "<ValueError: 'oops'>" # python-2.4
70 or hr(e) == "ValueError('oops',)") # python-2.5
72 raise NoArgumentException
75 hr(e) == "<NoArgumentException>" # python-2.4
76 or hr(e) == "NoArgumentException()") # python-2.5
82 class Math(unittest.TestCase):
83 def test_div_ceil(self):
85 self.failUnlessEqual(f(0, 1), 0)
86 self.failUnlessEqual(f(0, 2), 0)
87 self.failUnlessEqual(f(0, 3), 0)
88 self.failUnlessEqual(f(1, 3), 1)
89 self.failUnlessEqual(f(2, 3), 1)
90 self.failUnlessEqual(f(3, 3), 1)
91 self.failUnlessEqual(f(4, 3), 2)
92 self.failUnlessEqual(f(5, 3), 2)
93 self.failUnlessEqual(f(6, 3), 2)
94 self.failUnlessEqual(f(7, 3), 3)
96 def test_next_multiple(self):
97 f = mathutil.next_multiple
98 self.failUnlessEqual(f(5, 1), 5)
99 self.failUnlessEqual(f(5, 2), 6)
100 self.failUnlessEqual(f(5, 3), 6)
101 self.failUnlessEqual(f(5, 4), 8)
102 self.failUnlessEqual(f(5, 5), 5)
103 self.failUnlessEqual(f(5, 6), 6)
104 self.failUnlessEqual(f(32, 1), 32)
105 self.failUnlessEqual(f(32, 2), 32)
106 self.failUnlessEqual(f(32, 3), 33)
107 self.failUnlessEqual(f(32, 4), 32)
108 self.failUnlessEqual(f(32, 5), 35)
109 self.failUnlessEqual(f(32, 6), 36)
110 self.failUnlessEqual(f(32, 7), 35)
111 self.failUnlessEqual(f(32, 8), 32)
112 self.failUnlessEqual(f(32, 9), 36)
113 self.failUnlessEqual(f(32, 10), 40)
114 self.failUnlessEqual(f(32, 11), 33)
115 self.failUnlessEqual(f(32, 12), 36)
116 self.failUnlessEqual(f(32, 13), 39)
117 self.failUnlessEqual(f(32, 14), 42)
118 self.failUnlessEqual(f(32, 15), 45)
119 self.failUnlessEqual(f(32, 16), 32)
120 self.failUnlessEqual(f(32, 17), 34)
121 self.failUnlessEqual(f(32, 18), 36)
122 self.failUnlessEqual(f(32, 589), 589)
124 def test_pad_size(self):
125 f = mathutil.pad_size
126 self.failUnlessEqual(f(0, 4), 0)
127 self.failUnlessEqual(f(1, 4), 3)
128 self.failUnlessEqual(f(2, 4), 2)
129 self.failUnlessEqual(f(3, 4), 1)
130 self.failUnlessEqual(f(4, 4), 0)
131 self.failUnlessEqual(f(5, 4), 3)
133 def test_is_power_of_k(self):
134 f = mathutil.is_power_of_k
135 for i in range(1, 100):
136 if i in (1, 2, 4, 8, 16, 32, 64):
137 self.failUnless(f(i, 2), "but %d *is* a power of 2" % i)
139 self.failIf(f(i, 2), "but %d is *not* a power of 2" % i)
140 for i in range(1, 100):
141 if i in (1, 3, 9, 27, 81):
142 self.failUnless(f(i, 3), "but %d *is* a power of 3" % i)
144 self.failIf(f(i, 3), "but %d is *not* a power of 3" % i)
146 def test_next_power_of_k(self):
147 f = mathutil.next_power_of_k
148 self.failUnlessEqual(f(0,2), 1)
149 self.failUnlessEqual(f(1,2), 1)
150 self.failUnlessEqual(f(2,2), 2)
151 self.failUnlessEqual(f(3,2), 4)
152 self.failUnlessEqual(f(4,2), 4)
153 for i in range(5, 8): self.failUnlessEqual(f(i,2), 8, "%d" % i)
154 for i in range(9, 16): self.failUnlessEqual(f(i,2), 16, "%d" % i)
155 for i in range(17, 32): self.failUnlessEqual(f(i,2), 32, "%d" % i)
156 for i in range(33, 64): self.failUnlessEqual(f(i,2), 64, "%d" % i)
157 for i in range(65, 100): self.failUnlessEqual(f(i,2), 128, "%d" % i)
159 self.failUnlessEqual(f(0,3), 1)
160 self.failUnlessEqual(f(1,3), 1)
161 self.failUnlessEqual(f(2,3), 3)
162 self.failUnlessEqual(f(3,3), 3)
163 for i in range(4, 9): self.failUnlessEqual(f(i,3), 9, "%d" % i)
164 for i in range(10, 27): self.failUnlessEqual(f(i,3), 27, "%d" % i)
165 for i in range(28, 81): self.failUnlessEqual(f(i,3), 81, "%d" % i)
166 for i in range(82, 200): self.failUnlessEqual(f(i,3), 243, "%d" % i)
170 self.failUnlessEqual(f([1,2,3]), 2)
171 self.failUnlessEqual(f([0,0,0,4]), 1)
172 self.failUnlessAlmostEqual(f([0.0, 1.0, 1.0]), .666666666666)
174 def test_round_sigfigs(self):
175 f = mathutil.round_sigfigs
176 self.failUnlessEqual(f(22.0/3, 4), 7.3330000000000002)
178 class Statistics(unittest.TestCase):
179 def should_assert(self, msg, func, *args, **kwargs):
181 func(*args, **kwargs)
183 except AssertionError:
186 def failUnlessListEqual(self, a, b, msg = None):
187 self.failUnlessEqual(len(a), len(b))
188 for i in range(len(a)):
189 self.failUnlessEqual(a[i], b[i], msg)
191 def failUnlessListAlmostEqual(self, a, b, places = 7, msg = None):
192 self.failUnlessEqual(len(a), len(b))
193 for i in range(len(a)):
194 self.failUnlessAlmostEqual(a[i], b[i], places, msg)
196 def test_binomial_coeff(self):
197 f = statistics.binomial_coeff
198 self.failUnlessEqual(f(20, 0), 1)
199 self.failUnlessEqual(f(20, 1), 20)
200 self.failUnlessEqual(f(20, 2), 190)
201 self.failUnlessEqual(f(20, 8), f(20, 12))
202 self.should_assert("Should assert if n < k", f, 2, 3)
204 def test_binomial_distribution_pmf(self):
205 f = statistics.binomial_distribution_pmf
208 pmf_stat = [0.81, 0.18, 0.01]
209 self.failUnlessListAlmostEqual(pmf_comp, pmf_stat)
211 # Summing across a PMF should give the total probability 1
212 self.failUnlessAlmostEqual(sum(pmf_comp), 1)
213 self.should_assert("Should assert if not 0<=p<=1", f, 1, -1)
214 self.should_assert("Should assert if n < 1", f, 0, .1)
217 statistics.print_pmf(pmf_comp, out=out)
218 lines = out.getvalue().splitlines()
219 self.failUnlessEqual(lines[0], "i=0: 0.81")
220 self.failUnlessEqual(lines[1], "i=1: 0.18")
221 self.failUnlessEqual(lines[2], "i=2: 0.01")
223 def test_survival_pmf(self):
224 f = statistics.survival_pmf
225 # Cross-check binomial-distribution method against convolution
227 p_list = [.9999] * 100 + [.99] * 50 + [.8] * 20
228 pmf1 = statistics.survival_pmf_via_conv(p_list)
229 pmf2 = statistics.survival_pmf_via_bd(p_list)
230 self.failUnlessListAlmostEqual(pmf1, pmf2)
231 self.failUnlessTrue(statistics.valid_pmf(pmf1))
232 self.should_assert("Should assert if p_i > 1", f, [1.1]);
233 self.should_assert("Should assert if p_i < 0", f, [-.1]);
235 def test_repair_count_pmf(self):
236 survival_pmf = statistics.binomial_distribution_pmf(5, .9)
237 repair_pmf = statistics.repair_count_pmf(survival_pmf, 3)
238 # repair_pmf[0] == sum(survival_pmf[0,1,2,5])
239 # repair_pmf[1] == survival_pmf[4]
240 # repair_pmf[2] = survival_pmf[3]
241 self.failUnlessListAlmostEqual(repair_pmf,
242 [0.00001 + 0.00045 + 0.0081 + 0.59049,
247 def test_repair_cost(self):
248 survival_pmf = statistics.binomial_distribution_pmf(5, .9)
249 bwcost = statistics.bandwidth_cost_function
250 cost = statistics.mean_repair_cost(bwcost, 1000,
251 survival_pmf, 3, ul_dl_ratio=1.0)
252 self.failUnlessAlmostEqual(cost, 558.90)
253 cost = statistics.mean_repair_cost(bwcost, 1000,
254 survival_pmf, 3, ul_dl_ratio=8.0)
255 self.failUnlessAlmostEqual(cost, 1664.55)
257 # I haven't manually checked the math beyond here -warner
258 cost = statistics.eternal_repair_cost(bwcost, 1000,
260 discount_rate=0, ul_dl_ratio=1.0)
261 self.failUnlessAlmostEqual(cost, 65292.056074766246)
262 cost = statistics.eternal_repair_cost(bwcost, 1000,
266 self.failUnlessAlmostEqual(cost, 9133.6097158191551)
268 def test_convolve(self):
269 f = statistics.convolve
273 v1v2result = [ 4, 13, 28, 27, 18 ]
274 # Convolution is commutative
277 self.failUnlessListEqual(r1, r2, "Convolution should be commutative")
278 self.failUnlessListEqual(r1, v1v2result, "Didn't match known result")
279 # Convolution is associative
280 r1 = f(f(v1, v2), v3)
281 r2 = f(v1, f(v2, v3))
282 self.failUnlessListEqual(r1, r2, "Convolution should be associative")
283 # Convolution is distributive
284 r1 = f(v3, [ a + b for a, b in zip(v1, v2) ])
287 r2 = [ a + b for a, b in zip(tmp1, tmp2) ]
288 self.failUnlessListEqual(r1, r2, "Convolution should be distributive")
289 # Convolution is scalar multiplication associative
291 r1 = [ a * 4 for a in tmp1 ]
292 tmp2 = [ a * 4 for a in v1 ]
294 self.failUnlessListEqual(r1, r2, "Convolution should be scalar multiplication associative")
296 def test_find_k(self):
297 f = statistics.find_k
298 g = statistics.pr_file_loss
299 plist = [.9] * 10 + [.8] * 10 # N=20
302 self.failUnlessEqual(k, 10)
303 self.failUnless(g(plist, k) < t)
305 def test_pr_file_loss(self):
306 f = statistics.pr_file_loss
308 self.failUnlessEqual(f(plist, 3), .0546875)
310 def test_pr_backup_file_loss(self):
311 f = statistics.pr_backup_file_loss
313 self.failUnlessEqual(f(plist, .5, 3), .02734375)
316 class Asserts(unittest.TestCase):
317 def should_assert(self, func, *args, **kwargs):
319 func(*args, **kwargs)
320 except AssertionError, e:
323 self.fail("assert failed with non-AssertionError: %s" % e)
324 self.fail("assert was not caught")
326 def should_not_assert(self, func, *args, **kwargs):
328 func(*args, **kwargs)
329 except AssertionError, e:
330 self.fail("assertion fired when it should not have: %s" % e)
332 self.fail("assertion (which shouldn't have failed) failed with non-AssertionError: %s" % e)
336 def test_assert(self):
337 f = assertutil._assert
338 self.should_assert(f)
339 self.should_assert(f, False)
340 self.should_not_assert(f, True)
342 m = self.should_assert(f, False, "message")
343 self.failUnlessEqual(m, "'message' <type 'str'>", m)
344 m = self.should_assert(f, False, "message1", othermsg=12)
345 self.failUnlessEqual("'message1' <type 'str'>, othermsg: 12 <type 'int'>", m)
346 m = self.should_assert(f, False, othermsg="message2")
347 self.failUnlessEqual("othermsg: 'message2' <type 'str'>", m)
349 def test_precondition(self):
350 f = assertutil.precondition
351 self.should_assert(f)
352 self.should_assert(f, False)
353 self.should_not_assert(f, True)
355 m = self.should_assert(f, False, "message")
356 self.failUnlessEqual("precondition: 'message' <type 'str'>", m)
357 m = self.should_assert(f, False, "message1", othermsg=12)
358 self.failUnlessEqual("precondition: 'message1' <type 'str'>, othermsg: 12 <type 'int'>", m)
359 m = self.should_assert(f, False, othermsg="message2")
360 self.failUnlessEqual("precondition: othermsg: 'message2' <type 'str'>", m)
362 def test_postcondition(self):
363 f = assertutil.postcondition
364 self.should_assert(f)
365 self.should_assert(f, False)
366 self.should_not_assert(f, True)
368 m = self.should_assert(f, False, "message")
369 self.failUnlessEqual("postcondition: 'message' <type 'str'>", m)
370 m = self.should_assert(f, False, "message1", othermsg=12)
371 self.failUnlessEqual("postcondition: 'message1' <type 'str'>, othermsg: 12 <type 'int'>", m)
372 m = self.should_assert(f, False, othermsg="message2")
373 self.failUnlessEqual("postcondition: othermsg: 'message2' <type 'str'>", m)
375 class FileUtil(ReallyEqualMixin, unittest.TestCase):
376 def mkdir(self, basedir, path, mode=0777):
377 fn = os.path.join(basedir, path)
378 fileutil.make_dirs(fn, mode)
380 def touch(self, basedir, path, mode=None, data="touch\n"):
381 fn = os.path.join(basedir, path)
388 def test_rm_dir(self):
389 basedir = "util/FileUtil/test_rm_dir"
390 fileutil.make_dirs(basedir)
391 # create it again to test idempotency
392 fileutil.make_dirs(basedir)
393 d = os.path.join(basedir, "doomed")
395 self.touch(d, "a/b/1.txt")
396 self.touch(d, "a/b/2.txt", 0444)
397 self.touch(d, "a/b/3.txt", 0)
399 self.touch(d, "a/c/1.txt")
400 self.touch(d, "a/c/2.txt", 0444)
401 self.touch(d, "a/c/3.txt", 0)
402 os.chmod(os.path.join(d, "a/c"), 0444)
404 self.touch(d, "a/d/1.txt")
405 self.touch(d, "a/d/2.txt", 0444)
406 self.touch(d, "a/d/3.txt", 0)
407 os.chmod(os.path.join(d, "a/d"), 0)
410 self.failIf(os.path.exists(d))
411 # remove it again to test idempotency
414 def test_remove_if_possible(self):
415 basedir = "util/FileUtil/test_remove_if_possible"
416 fileutil.make_dirs(basedir)
417 self.touch(basedir, "here")
418 fn = os.path.join(basedir, "here")
419 fileutil.remove_if_possible(fn)
420 self.failIf(os.path.exists(fn))
421 fileutil.remove_if_possible(fn) # should be idempotent
422 fileutil.rm_dir(basedir)
423 fileutil.remove_if_possible(fn) # should survive errors
425 def test_write_atomically(self):
426 basedir = "util/FileUtil/test_write_atomically"
427 fileutil.make_dirs(basedir)
428 fn = os.path.join(basedir, "here")
429 fileutil.write_atomically(fn, "one")
430 self.failUnlessEqual(fileutil.read(fn), "one")
431 fileutil.write_atomically(fn, "two", mode="") # non-binary
432 self.failUnlessEqual(fileutil.read(fn), "two")
434 def test_NamedTemporaryDirectory(self):
435 basedir = "util/FileUtil/test_NamedTemporaryDirectory"
436 fileutil.make_dirs(basedir)
437 td = fileutil.NamedTemporaryDirectory(dir=basedir)
439 self.failUnless(basedir in name)
440 self.failUnless(basedir in repr(td))
441 self.failUnless(os.path.isdir(name))
443 # it is conceivable that we need to force gc here, but I'm not sure
444 self.failIf(os.path.isdir(name))
446 def test_rename(self):
447 basedir = "util/FileUtil/test_rename"
448 fileutil.make_dirs(basedir)
449 self.touch(basedir, "here")
450 fn = os.path.join(basedir, "here")
451 fn2 = os.path.join(basedir, "there")
452 fileutil.rename(fn, fn2)
453 self.failIf(os.path.exists(fn))
454 self.failUnless(os.path.exists(fn2))
457 basedir = "util/FileUtil/test_du"
458 fileutil.make_dirs(basedir)
459 d = os.path.join(basedir, "space-consuming")
461 self.touch(d, "a/b/1.txt", data="a"*10)
462 self.touch(d, "a/b/2.txt", data="b"*11)
464 self.touch(d, "a/c/1.txt", data="c"*12)
465 self.touch(d, "a/c/2.txt", data="d"*13)
467 used = fileutil.du(basedir)
468 self.failUnlessEqual(10+11+12+13, used)
470 def test_abspath_expanduser_unicode(self):
471 self.failUnlessRaises(AssertionError, fileutil.abspath_expanduser_unicode, "bytestring")
473 saved_cwd = os.path.normpath(os.getcwdu())
474 abspath_cwd = fileutil.abspath_expanduser_unicode(u".")
475 self.failUnless(isinstance(saved_cwd, unicode), saved_cwd)
476 self.failUnless(isinstance(abspath_cwd, unicode), abspath_cwd)
477 if sys.platform == "win32":
478 self.failUnlessReallyEqual(abspath_cwd, fileutil.to_windows_long_path(saved_cwd))
480 self.failUnlessReallyEqual(abspath_cwd, saved_cwd)
482 self.failUnlessReallyEqual(fileutil.to_windows_long_path(u"\\\\?\\foo"), u"\\\\?\\foo")
483 self.failUnlessReallyEqual(fileutil.to_windows_long_path(u"\\\\.\\foo"), u"\\\\.\\foo")
484 self.failUnlessReallyEqual(fileutil.to_windows_long_path(u"\\\\server\\foo"), u"\\\\?\\UNC\\server\\foo")
485 self.failUnlessReallyEqual(fileutil.to_windows_long_path(u"C:\\foo"), u"\\\\?\\C:\\foo")
486 self.failUnlessReallyEqual(fileutil.to_windows_long_path(u"C:\\foo/bar"), u"\\\\?\\C:\\foo\\bar")
488 # adapted from <http://svn.python.org/view/python/branches/release26-maint/Lib/test/test_posixpath.py?view=markup&pathrev=78279#test_abspath>
490 self.failUnlessIn(u"foo", fileutil.abspath_expanduser_unicode(u"foo"))
491 self.failIfIn(u"~", fileutil.abspath_expanduser_unicode(u"~"))
495 cwds.append(u'\xe7w\xf0'.encode(sys.getfilesystemencoding()
497 except UnicodeEncodeError:
498 pass # the cwd can't be encoded -- test with ascii cwd only
504 for upath in (u'', u'fuu', u'f\xf9\xf9', u'/fuu', u'U:\\', u'~'):
505 uabspath = fileutil.abspath_expanduser_unicode(upath)
506 self.failUnless(isinstance(uabspath, unicode), uabspath)
510 def test_create_long_path(self):
511 workdir = u"test_create_long_path"
512 fileutil.make_dirs(workdir)
513 long_path = fileutil.abspath_expanduser_unicode(os.path.join(workdir, u'x'*255))
515 fileutil.remove(long_path)
516 self.addCleanup(_cleanup)
518 fileutil.write(long_path, "test")
519 self.failUnless(os.path.exists(long_path))
520 self.failUnlessEqual(fileutil.read(long_path), "test")
522 self.failIf(os.path.exists(long_path))
524 def test_windows_expanduser(self):
525 def call_windows_getenv(name):
526 if name == u"HOMEDRIVE": return u"C:"
527 if name == u"HOMEPATH": return u"\\Documents and Settings\\\u0100"
528 self.fail("unexpected argument to call_windows_getenv")
529 self.patch(fileutil, 'windows_getenv', call_windows_getenv)
531 self.failUnlessReallyEqual(fileutil.windows_expanduser(u"~"), os.path.join(u"C:", u"\\Documents and Settings\\\u0100"))
532 self.failUnlessReallyEqual(fileutil.windows_expanduser(u"~\\foo"), os.path.join(u"C:", u"\\Documents and Settings\\\u0100", u"foo"))
533 self.failUnlessReallyEqual(fileutil.windows_expanduser(u"~/foo"), os.path.join(u"C:", u"\\Documents and Settings\\\u0100", u"foo"))
534 self.failUnlessReallyEqual(fileutil.windows_expanduser(u"a"), u"a")
535 self.failUnlessReallyEqual(fileutil.windows_expanduser(u"a~"), u"a~")
536 self.failUnlessReallyEqual(fileutil.windows_expanduser(u"a\\~\\foo"), u"a\\~\\foo")
538 def test_disk_stats(self):
539 avail = fileutil.get_available_space('.', 2**14)
541 raise unittest.SkipTest("This test will spuriously fail there is no disk space left.")
543 disk = fileutil.get_disk_stats('.', 2**13)
544 self.failUnless(disk['total'] > 0, disk['total'])
545 # we tolerate used==0 for a Travis-CI bug, see #2290
546 self.failUnless(disk['used'] >= 0, disk['used'])
547 self.failUnless(disk['free_for_root'] > 0, disk['free_for_root'])
548 self.failUnless(disk['free_for_nonroot'] > 0, disk['free_for_nonroot'])
549 self.failUnless(disk['avail'] > 0, disk['avail'])
551 def test_disk_stats_avail_nonnegative(self):
552 # This test will spuriously fail if you have more than 2^128
553 # bytes of available space on your filesystem.
554 disk = fileutil.get_disk_stats('.', 2**128)
555 self.failUnlessEqual(disk['avail'], 0)
557 class PollMixinTests(unittest.TestCase):
559 self.pm = pollmixin.PollMixin()
561 def test_PollMixin_True(self):
562 d = self.pm.poll(check_f=lambda : True,
566 def test_PollMixin_False_then_True(self):
567 i = iter([False, True])
568 d = self.pm.poll(check_f=i.next,
572 def test_timeout(self):
573 d = self.pm.poll(check_f=lambda: False,
577 self.fail("poll should have failed, not returned %s" % (res,))
579 f.trap(pollmixin.TimeoutError)
580 return None # success
581 d.addCallbacks(_suc, _err)
584 class DeferredUtilTests(unittest.TestCase):
585 def test_gather_results(self):
586 d1 = defer.Deferred()
587 d2 = defer.Deferred()
588 res = deferredutil.gatherResults([d1, d2])
589 d1.errback(ValueError("BAD"))
591 self.fail("Should have errbacked, not resulted in %s" % (res,))
593 thef.trap(ValueError)
594 res.addCallbacks(_callb, _errb)
597 def test_success(self):
598 d1, d2 = defer.Deferred(), defer.Deferred()
601 dlss = deferredutil.DeferredListShouldSucceed([d1,d2])
602 dlss.addCallbacks(good.append, bad.append)
605 self.failUnlessEqual(good, [[1,2]])
606 self.failUnlessEqual(bad, [])
608 def test_failure(self):
609 d1, d2 = defer.Deferred(), defer.Deferred()
612 dlss = deferredutil.DeferredListShouldSucceed([d1,d2])
613 dlss.addCallbacks(good.append, bad.append)
614 d1.addErrback(lambda _ignore: None)
615 d2.addErrback(lambda _ignore: None)
617 d2.errback(ValueError())
618 self.failUnlessEqual(good, [])
619 self.failUnlessEqual(len(bad), 1)
621 self.failUnless(isinstance(f, Failure))
622 self.failUnless(f.check(ValueError))
624 class HashUtilTests(unittest.TestCase):
626 def test_random_key(self):
627 k = hashutil.random_key()
628 self.failUnlessEqual(len(k), hashutil.KEYLEN)
630 def test_sha256d(self):
631 h1 = hashutil.tagged_hash("tag1", "value")
632 h2 = hashutil.tagged_hasher("tag1")
636 self.failUnlessEqual(h1, h2a)
637 self.failUnlessEqual(h2a, h2b)
639 def test_sha256d_truncated(self):
640 h1 = hashutil.tagged_hash("tag1", "value", 16)
641 h2 = hashutil.tagged_hasher("tag1", 16)
644 self.failUnlessEqual(len(h1), 16)
645 self.failUnlessEqual(len(h2), 16)
646 self.failUnlessEqual(h1, h2)
649 h1 = hashutil.convergence_hash(3, 10, 1000, "data", "secret")
650 h2 = hashutil.convergence_hasher(3, 10, 1000, "secret")
653 self.failUnlessEqual(h1, h2)
655 def test_hashers(self):
656 h1 = hashutil.block_hash("foo")
657 h2 = hashutil.block_hasher()
659 self.failUnlessEqual(h1, h2.digest())
661 h1 = hashutil.uri_extension_hash("foo")
662 h2 = hashutil.uri_extension_hasher()
664 self.failUnlessEqual(h1, h2.digest())
666 h1 = hashutil.plaintext_hash("foo")
667 h2 = hashutil.plaintext_hasher()
669 self.failUnlessEqual(h1, h2.digest())
671 h1 = hashutil.crypttext_hash("foo")
672 h2 = hashutil.crypttext_hasher()
674 self.failUnlessEqual(h1, h2.digest())
676 h1 = hashutil.crypttext_segment_hash("foo")
677 h2 = hashutil.crypttext_segment_hasher()
679 self.failUnlessEqual(h1, h2.digest())
681 h1 = hashutil.plaintext_segment_hash("foo")
682 h2 = hashutil.plaintext_segment_hasher()
684 self.failUnlessEqual(h1, h2.digest())
686 def test_timing_safe_compare(self):
687 self.failUnless(hashutil.timing_safe_compare("a", "a"))
688 self.failUnless(hashutil.timing_safe_compare("ab", "ab"))
689 self.failIf(hashutil.timing_safe_compare("a", "b"))
690 self.failIf(hashutil.timing_safe_compare("a", "aa"))
692 def _testknown(self, hashf, expected_a, *args):
694 got_a = base32.b2a(got)
695 self.failUnlessEqual(got_a, expected_a)
697 def test_known_answers(self):
698 # assert backwards compatibility
699 self._testknown(hashutil.storage_index_hash, "qb5igbhcc5esa6lwqorsy7e6am", "")
700 self._testknown(hashutil.block_hash, "msjr5bh4evuh7fa3zw7uovixfbvlnstr5b65mrerwfnvjxig2jvq", "")
701 self._testknown(hashutil.uri_extension_hash, "wthsu45q7zewac2mnivoaa4ulh5xvbzdmsbuyztq2a5fzxdrnkka", "")
702 self._testknown(hashutil.plaintext_hash, "5lz5hwz3qj3af7n6e3arblw7xzutvnd3p3fjsngqjcb7utf3x3da", "")
703 self._testknown(hashutil.crypttext_hash, "itdj6e4njtkoiavlrmxkvpreosscssklunhwtvxn6ggho4rkqwga", "")
704 self._testknown(hashutil.crypttext_segment_hash, "aovy5aa7jej6ym5ikgwyoi4pxawnoj3wtaludjz7e2nb5xijb7aa", "")
705 self._testknown(hashutil.plaintext_segment_hash, "4fdgf6qruaisyukhqcmoth4t3li6bkolbxvjy4awwcpprdtva7za", "")
706 self._testknown(hashutil.convergence_hash, "3mo6ni7xweplycin6nowynw2we", 3, 10, 100, "", "converge")
707 self._testknown(hashutil.my_renewal_secret_hash, "ujhr5k5f7ypkp67jkpx6jl4p47pyta7hu5m527cpcgvkafsefm6q", "")
708 self._testknown(hashutil.my_cancel_secret_hash, "rjwzmafe2duixvqy6h47f5wfrokdziry6zhx4smew4cj6iocsfaa", "")
709 self._testknown(hashutil.file_renewal_secret_hash, "hzshk2kf33gzbd5n3a6eszkf6q6o6kixmnag25pniusyaulqjnia", "", "si")
710 self._testknown(hashutil.file_cancel_secret_hash, "bfciwvr6w7wcavsngxzxsxxaszj72dej54n4tu2idzp6b74g255q", "", "si")
711 self._testknown(hashutil.bucket_renewal_secret_hash, "e7imrzgzaoashsncacvy3oysdd2m5yvtooo4gmj4mjlopsazmvuq", "", "\x00"*20)
712 self._testknown(hashutil.bucket_cancel_secret_hash, "dvdujeyxeirj6uux6g7xcf4lvesk632aulwkzjar7srildvtqwma", "", "\x00"*20)
713 self._testknown(hashutil.hmac, "c54ypfi6pevb3nvo6ba42jtglpkry2kbdopqsi7dgrm4r7tw5sra", "tag", "")
714 self._testknown(hashutil.mutable_rwcap_key_hash, "6rvn2iqrghii5n4jbbwwqqsnqu", "iv", "wk")
715 self._testknown(hashutil.ssk_writekey_hash, "ykpgmdbpgbb6yqz5oluw2q26ye", "")
716 self._testknown(hashutil.ssk_write_enabler_master_hash, "izbfbfkoait4dummruol3gy2bnixrrrslgye6ycmkuyujnenzpia", "")
717 self._testknown(hashutil.ssk_write_enabler_hash, "fuu2dvx7g6gqu5x22vfhtyed7p4pd47y5hgxbqzgrlyvxoev62tq", "wk", "\x00"*20)
718 self._testknown(hashutil.ssk_pubkey_fingerprint_hash, "3opzw4hhm2sgncjx224qmt5ipqgagn7h5zivnfzqycvgqgmgz35q", "")
719 self._testknown(hashutil.ssk_readkey_hash, "vugid4as6qbqgeq2xczvvcedai", "")
720 self._testknown(hashutil.ssk_readkey_data_hash, "73wsaldnvdzqaf7v4pzbr2ae5a", "iv", "rk")
721 self._testknown(hashutil.ssk_storage_index_hash, "j7icz6kigb6hxrej3tv4z7ayym", "")
724 class Abbreviate(unittest.TestCase):
726 a = abbreviate.abbreviate_time
727 self.failUnlessEqual(a(None), "unknown")
728 self.failUnlessEqual(a(0), "0 seconds")
729 self.failUnlessEqual(a(1), "1 second")
730 self.failUnlessEqual(a(2), "2 seconds")
731 self.failUnlessEqual(a(119), "119 seconds")
733 self.failUnlessEqual(a(2*MIN), "2 minutes")
734 self.failUnlessEqual(a(60*MIN), "60 minutes")
735 self.failUnlessEqual(a(179*MIN), "179 minutes")
737 self.failUnlessEqual(a(180*MIN), "3 hours")
738 self.failUnlessEqual(a(4*HOUR), "4 hours")
741 self.failUnlessEqual(a(2*DAY), "2 days")
742 self.failUnlessEqual(a(2*MONTH), "2 months")
744 self.failUnlessEqual(a(5*YEAR), "5 years")
746 def test_space(self):
747 tests_si = [(None, "unknown"),
754 (20*1000, "20.00 kB"),
755 (1024*1024, "1.05 MB"),
756 (1000*1000, "1.00 MB"),
757 (1000*1000*1000, "1.00 GB"),
758 (1000*1000*1000*1000, "1.00 TB"),
759 (1000*1000*1000*1000*1000, "1.00 PB"),
760 (1000*1000*1000*1000*1000*1000, "1.00 EB"),
761 (1234567890123456789, "1.23 EB"),
763 for (x, expected) in tests_si:
764 got = abbreviate.abbreviate_space(x, SI=True)
765 self.failUnlessEqual(got, expected)
767 tests_base1024 = [(None, "unknown"),
774 (20*1024, "20.00 kiB"),
775 (1000*1000, "976.56 kiB"),
776 (1024*1024, "1.00 MiB"),
777 (1024*1024*1024, "1.00 GiB"),
778 (1024*1024*1024*1024, "1.00 TiB"),
779 (1000*1000*1000*1000*1000, "909.49 TiB"),
780 (1024*1024*1024*1024*1024, "1.00 PiB"),
781 (1024*1024*1024*1024*1024*1024, "1.00 EiB"),
782 (1234567890123456789, "1.07 EiB"),
784 for (x, expected) in tests_base1024:
785 got = abbreviate.abbreviate_space(x, SI=False)
786 self.failUnlessEqual(got, expected)
788 self.failUnlessEqual(abbreviate.abbreviate_space_both(1234567),
789 "(1.23 MB, 1.18 MiB)")
791 def test_parse_space(self):
792 p = abbreviate.parse_abbreviated_size
793 self.failUnlessEqual(p(""), None)
794 self.failUnlessEqual(p(None), None)
795 self.failUnlessEqual(p("123"), 123)
796 self.failUnlessEqual(p("123B"), 123)
797 self.failUnlessEqual(p("2K"), 2000)
798 self.failUnlessEqual(p("2kb"), 2000)
799 self.failUnlessEqual(p("2KiB"), 2048)
800 self.failUnlessEqual(p("10MB"), 10*1000*1000)
801 self.failUnlessEqual(p("10MiB"), 10*1024*1024)
802 self.failUnlessEqual(p("5G"), 5*1000*1000*1000)
803 self.failUnlessEqual(p("4GiB"), 4*1024*1024*1024)
804 self.failUnlessEqual(p("3TB"), 3*1000*1000*1000*1000)
805 self.failUnlessEqual(p("3TiB"), 3*1024*1024*1024*1024)
806 self.failUnlessEqual(p("6PB"), 6*1000*1000*1000*1000*1000)
807 self.failUnlessEqual(p("6PiB"), 6*1024*1024*1024*1024*1024)
808 self.failUnlessEqual(p("9EB"), 9*1000*1000*1000*1000*1000*1000)
809 self.failUnlessEqual(p("9EiB"), 9*1024*1024*1024*1024*1024*1024)
811 e = self.failUnlessRaises(ValueError, p, "12 cubits")
812 self.failUnlessIn("12 cubits", str(e))
813 e = self.failUnlessRaises(ValueError, p, "1 BB")
814 self.failUnlessIn("1 BB", str(e))
815 e = self.failUnlessRaises(ValueError, p, "fhtagn")
816 self.failUnlessIn("fhtagn", str(e))
818 class Limiter(unittest.TestCase):
819 timeout = 480 # This takes longer than 240 seconds on Francois's arm box.
821 def job(self, i, foo):
822 self.calls.append( (i, foo) )
823 self.simultaneous += 1
824 self.peak_simultaneous = max(self.simultaneous, self.peak_simultaneous)
827 self.simultaneous -= 1
828 d.callback("done %d" % i)
829 reactor.callLater(1.0, _done)
832 def bad_job(self, i, foo):
833 raise ValueError("bad_job %d" % i)
835 def test_limiter(self):
837 self.simultaneous = 0
838 self.peak_simultaneous = 0
839 l = limiter.ConcurrencyLimiter()
842 dl.append(l.add(self.job, i, foo=str(i)))
843 d = defer.DeferredList(dl, fireOnOneErrback=True)
845 self.failUnlessEqual(self.simultaneous, 0)
846 self.failUnless(self.peak_simultaneous <= 10)
847 self.failUnlessEqual(len(self.calls), 20)
849 self.failUnless( (i, str(i)) in self.calls)
853 def test_errors(self):
855 self.simultaneous = 0
856 self.peak_simultaneous = 0
857 l = limiter.ConcurrencyLimiter()
860 dl.append(l.add(self.job, i, foo=str(i)))
861 d2 = l.add(self.bad_job, 21, "21")
862 d = defer.DeferredList(dl, fireOnOneErrback=True)
865 for (success, result) in res:
866 self.failUnlessEqual(success, True)
867 results.append(result)
869 expected_results = ["done %d" % i for i in range(20)]
870 expected_results.sort()
871 self.failUnlessEqual(results, expected_results)
872 self.failUnless(self.peak_simultaneous <= 10)
873 self.failUnlessEqual(len(self.calls), 20)
875 self.failUnless( (i, str(i)) in self.calls)
877 self.fail("should have failed, not got %s" % (res,))
880 self.failUnless("bad_job 21" in str(f))
881 d2.addCallbacks(_good, _err)
883 d.addCallback(_most_done)
885 self.failUnlessEqual(self.simultaneous, 0)
886 self.failUnless(self.peak_simultaneous <= 10)
887 self.failUnlessEqual(len(self.calls), 20)
889 self.failUnless( (i, str(i)) in self.calls)
890 d.addCallback(_all_done)
893 class TimeFormat(unittest.TestCase):
894 def test_epoch(self):
895 return self._help_test_epoch()
897 def test_epoch_in_London(self):
898 # Europe/London is a particularly troublesome timezone. Nowadays, its
899 # offset from GMT is 0. But in 1970, its offset from GMT was 1.
900 # (Apparently in 1970 Britain had redefined standard time to be GMT+1
901 # and stayed in standard time all year round, whereas today
902 # Europe/London standard time is GMT and Europe/London Daylight
903 # Savings Time is GMT+1.) The current implementation of
904 # time_format.iso_utc_time_to_localseconds() breaks if the timezone is
905 # Europe/London. (As soon as this unit test is done then I'll change
906 # that implementation to something that works even in this case...)
907 origtz = os.environ.get('TZ')
908 os.environ['TZ'] = "Europe/London"
909 if hasattr(time, 'tzset'):
912 return self._help_test_epoch()
917 os.environ['TZ'] = origtz
918 if hasattr(time, 'tzset'):
921 def _help_test_epoch(self):
922 origtzname = time.tzname
923 s = time_format.iso_utc_time_to_seconds("1970-01-01T00:00:01")
924 self.failUnlessEqual(s, 1.0)
925 s = time_format.iso_utc_time_to_seconds("1970-01-01_00:00:01")
926 self.failUnlessEqual(s, 1.0)
927 s = time_format.iso_utc_time_to_seconds("1970-01-01 00:00:01")
928 self.failUnlessEqual(s, 1.0)
930 self.failUnlessEqual(time_format.iso_utc(1.0), "1970-01-01_00:00:01")
931 self.failUnlessEqual(time_format.iso_utc(1.0, sep=" "),
932 "1970-01-01 00:00:01")
935 isostr = time_format.iso_utc(now)
936 timestamp = time_format.iso_utc_time_to_seconds(isostr)
937 self.failUnlessEqual(int(timestamp), int(now))
941 self.failUnlessEqual(time_format.iso_utc(t=my_time),
942 "1970-01-01_00:00:01")
943 e = self.failUnlessRaises(ValueError,
944 time_format.iso_utc_time_to_seconds,
945 "invalid timestring")
946 self.failUnless("not a complete ISO8601 timestamp" in str(e))
947 s = time_format.iso_utc_time_to_seconds("1970-01-01_00:00:01.500")
948 self.failUnlessEqual(s, 1.5)
950 # Look for daylight-savings-related errors.
951 thatmomentinmarch = time_format.iso_utc_time_to_seconds("2009-03-20 21:49:02.226536")
952 self.failUnlessEqual(thatmomentinmarch, 1237585742.226536)
953 self.failUnlessEqual(origtzname, time.tzname)
955 def test_iso_utc(self):
956 when = 1266760143.7841301
957 out = time_format.iso_utc_date(when)
958 self.failUnlessEqual(out, "2010-02-21")
959 out = time_format.iso_utc_date(t=lambda: when)
960 self.failUnlessEqual(out, "2010-02-21")
961 out = time_format.iso_utc(when)
962 self.failUnlessEqual(out, "2010-02-21_13:49:03.784130")
963 out = time_format.iso_utc(when, sep="-")
964 self.failUnlessEqual(out, "2010-02-21-13:49:03.784130")
966 def test_parse_duration(self):
967 p = time_format.parse_duration
969 self.failUnlessEqual(p("1 day"), DAY)
970 self.failUnlessEqual(p("2 days"), 2*DAY)
971 self.failUnlessEqual(p("3 months"), 3*31*DAY)
972 self.failUnlessEqual(p("4 mo"), 4*31*DAY)
973 self.failUnlessEqual(p("5 years"), 5*365*DAY)
974 e = self.failUnlessRaises(ValueError, p, "123")
975 self.failUnlessIn("no unit (like day, month, or year) in '123'",
978 def test_parse_date(self):
979 self.failUnlessEqual(time_format.parse_date("2010-02-21"), 1266710400)
981 class CacheDir(unittest.TestCase):
982 def test_basic(self):
983 basedir = "test_util/CacheDir/test_basic"
985 def _failIfExists(name):
986 absfn = os.path.join(basedir, name)
987 self.failIf(os.path.exists(absfn),
988 "%s exists but it shouldn't" % absfn)
990 def _failUnlessExists(name):
991 absfn = os.path.join(basedir, name)
992 self.failUnless(os.path.exists(absfn),
993 "%s doesn't exist but it should" % absfn)
995 cdm = cachedir.CacheDirectoryManager(basedir)
996 a = cdm.get_file("a")
997 b = cdm.get_file("b")
998 c = cdm.get_file("c")
999 f = open(a.get_filename(), "wb"); f.write("hi"); f.close(); del f
1000 f = open(b.get_filename(), "wb"); f.write("hi"); f.close(); del f
1001 f = open(c.get_filename(), "wb"); f.write("hi"); f.close(); del f
1003 _failUnlessExists("a")
1004 _failUnlessExists("b")
1005 _failUnlessExists("c")
1009 _failUnlessExists("a")
1010 _failUnlessExists("b")
1011 _failUnlessExists("c")
1014 # this file won't be deleted yet, because it isn't old enough
1016 _failUnlessExists("a")
1017 _failUnlessExists("b")
1018 _failUnlessExists("c")
1020 # we change the definition of "old" to make everything old
1025 _failUnlessExists("b")
1026 _failUnlessExists("c")
1034 _failUnlessExists("b")
1035 _failUnlessExists("c")
1037 b2 = cdm.get_file("b")
1041 _failUnlessExists("b")
1042 _failUnlessExists("c")
1047 def __init__(self, x):
1052 return "<%s %s>" % (self.__class__.__name__, self.x,)
1055 def __le__(self, other):
1056 return self.x <= other
1057 def __lt__(self, other):
1058 return self.x < other
1059 def __ge__(self, other):
1060 return self.x >= other
1061 def __gt__(self, other):
1062 return self.x > other
1063 def __ne__(self, other):
1064 return self.x != other
1065 def __eq__(self, other):
1066 return self.x == other
1068 class DictUtil(unittest.TestCase):
1069 def _help_test_empty_dict(self, klass):
1073 self.failUnless(d1 == d2, "d1: %r, d2: %r" % (d1, d2,))
1074 self.failUnless(len(d1) == 0)
1075 self.failUnless(len(d2) == 0)
1077 def _help_test_nonempty_dict(self, klass):
1078 d1 = klass({'a': 1, 'b': "eggs", 3: "spam",})
1079 d2 = klass({'a': 1, 'b': "eggs", 3: "spam",})
1081 self.failUnless(d1 == d2)
1082 self.failUnless(len(d1) == 3, "%s, %s" % (len(d1), d1,))
1083 self.failUnless(len(d2) == 3)
1085 def _help_test_eq_but_notis(self, klass):
1086 d = klass({'a': 3, 'b': EqButNotIs(3), 'c': 3})
1091 d['b'] = EqButNotIs(3)
1096 d['b'] = EqButNotIs(3)
1102 d['a'] = EqButNotIs(3)
1107 fake3 = EqButNotIs(3)
1108 fake7 = EqButNotIs(7)
1112 self.failUnless(filter(lambda x: x is 8, d.itervalues()))
1113 self.failUnless(filter(lambda x: x is fake7, d.itervalues()))
1114 # The real 7 should have been ejected by the d[3] = 8.
1115 self.failUnless(not filter(lambda x: x is 7, d.itervalues()))
1116 self.failUnless(filter(lambda x: x is fake3, d.iterkeys()))
1117 self.failUnless(filter(lambda x: x is 3, d.iterkeys()))
1122 fake3 = EqButNotIs(3)
1123 fake7 = EqButNotIs(7)
1126 self.failUnless(filter(lambda x: x is 8, d.itervalues()))
1127 self.failUnless(filter(lambda x: x is fake7, d.itervalues()))
1128 # The real 7 should have been ejected by the d[3] = 8.
1129 self.failUnless(not filter(lambda x: x is 7, d.itervalues()))
1130 self.failUnless(filter(lambda x: x is fake3, d.iterkeys()))
1131 self.failUnless(filter(lambda x: x is 3, d.iterkeys()))
1135 self._help_test_eq_but_notis(dictutil.UtilDict)
1136 self._help_test_eq_but_notis(dictutil.NumDict)
1137 self._help_test_eq_but_notis(dictutil.ValueOrderedDict)
1138 self._help_test_nonempty_dict(dictutil.UtilDict)
1139 self._help_test_nonempty_dict(dictutil.NumDict)
1140 self._help_test_nonempty_dict(dictutil.ValueOrderedDict)
1141 self._help_test_eq_but_notis(dictutil.UtilDict)
1142 self._help_test_eq_but_notis(dictutil.NumDict)
1143 self._help_test_eq_but_notis(dictutil.ValueOrderedDict)
1145 def test_dict_of_sets(self):
1146 ds = dictutil.DictOfSets()
1151 self.failUnlessEqual(ds[1], set(["a"]))
1152 self.failUnlessEqual(ds[2], set(["b", "c"]))
1153 ds.discard(3, "d") # should not raise an exception
1155 self.failUnlessEqual(ds[2], set(["c"]))
1157 self.failIf(2 in ds)
1160 ds2 = dictutil.DictOfSets()
1165 self.failUnlessEqual(ds[1], set(["a"]))
1166 self.failUnlessEqual(ds[3], set(["f", "g"]))
1167 self.failUnlessEqual(ds[4], set(["h"]))
1169 def test_move(self):
1170 d1 = {1: "a", 2: "b"}
1171 d2 = {2: "c", 3: "d"}
1172 dictutil.move(1, d1, d2)
1173 self.failUnlessEqual(d1, {2: "b"})
1174 self.failUnlessEqual(d2, {1: "a", 2: "c", 3: "d"})
1176 d1 = {1: "a", 2: "b"}
1177 d2 = {2: "c", 3: "d"}
1178 dictutil.move(2, d1, d2)
1179 self.failUnlessEqual(d1, {1: "a"})
1180 self.failUnlessEqual(d2, {2: "b", 3: "d"})
1182 d1 = {1: "a", 2: "b"}
1183 d2 = {2: "c", 3: "d"}
1184 self.failUnlessRaises(KeyError, dictutil.move, 5, d1, d2, strict=True)
1186 def test_subtract(self):
1187 d1 = {1: "a", 2: "b"}
1188 d2 = {2: "c", 3: "d"}
1189 d3 = dictutil.subtract(d1, d2)
1190 self.failUnlessEqual(d3, {1: "a"})
1192 d1 = {1: "a", 2: "b"}
1194 d3 = dictutil.subtract(d1, d2)
1195 self.failUnlessEqual(d3, {1: "a"})
1197 def test_utildict(self):
1198 d = dictutil.UtilDict({1: "a", 2: "b"})
1201 self.failUnlessEqual(d, {2: "b"})
1204 self.failUnlessRaises(TypeError, eq, d, "not a dict")
1206 d = dictutil.UtilDict({1: "b", 2: "a"})
1207 self.failUnlessEqual(d.items_sorted_by_value(),
1208 [(2, "a"), (1, "b")])
1209 self.failUnlessEqual(d.items_sorted_by_key(),
1210 [(1, "b"), (2, "a")])
1211 self.failUnlessEqual(repr(d), "{1: 'b', 2: 'a'}")
1212 self.failUnless(1 in d)
1214 d2 = dictutil.UtilDict({3: "c", 4: "d"})
1215 self.failUnless(d != d2)
1216 self.failUnless(d2 > d)
1217 self.failUnless(d2 >= d)
1218 self.failUnless(d <= d2)
1219 self.failUnless(d < d2)
1220 self.failUnlessEqual(d[1], "b")
1221 self.failUnlessEqual(sorted(list([k for k in d])), [1,2])
1224 self.failUnlessEqual(d, d3)
1225 self.failUnless(isinstance(d3, dictutil.UtilDict))
1227 d4 = d.fromkeys([3,4], "e")
1228 self.failUnlessEqual(d4, {3: "e", 4: "e"})
1230 self.failUnlessEqual(d.get(1), "b")
1231 self.failUnlessEqual(d.get(3), None)
1232 self.failUnlessEqual(d.get(3, "default"), "default")
1233 self.failUnlessEqual(sorted(list(d.items())),
1234 [(1, "b"), (2, "a")])
1235 self.failUnlessEqual(sorted(list(d.iteritems())),
1236 [(1, "b"), (2, "a")])
1237 self.failUnlessEqual(sorted(d.keys()), [1, 2])
1238 self.failUnlessEqual(sorted(d.values()), ["a", "b"])
1239 x = d.setdefault(1, "new")
1240 self.failUnlessEqual(x, "b")
1241 self.failUnlessEqual(d[1], "b")
1242 x = d.setdefault(3, "new")
1243 self.failUnlessEqual(x, "new")
1244 self.failUnlessEqual(d[3], "new")
1248 self.failUnless(x in [(1, "b"), (2, "a")])
1250 self.failUnless(x in [(1, "b"), (2, "a")])
1251 self.failUnlessRaises(KeyError, d.popitem)
1253 def test_numdict(self):
1254 d = dictutil.NumDict({"a": 1, "b": 2})
1256 d.add_num("a", 10, 5)
1257 d.add_num("c", 20, 5)
1259 self.failUnlessEqual(d, {"a": 11, "b": 2, "c": 25, "d": 30})
1261 d.subtract_num("a", 10)
1262 d.subtract_num("e", 10)
1263 d.subtract_num("f", 10, 15)
1264 self.failUnlessEqual(d, {"a": 1, "b": 2, "c": 25, "d": 30,
1267 self.failUnlessEqual(d.sum(), sum([1, 2, 25, 30, -10, 5]))
1269 d = dictutil.NumDict()
1273 self.failUnlessEqual(d, {"a": 2, "b": 6})
1277 self.failUnlessEqual(d, {"a": 1, "b": 6, "c": -1, "d": 4})
1278 self.failUnlessEqual(d.items_sorted_by_key(),
1279 [("a", 1), ("b", 6), ("c", -1), ("d", 4)])
1280 self.failUnlessEqual(d.items_sorted_by_value(),
1281 [("c", -1), ("a", 1), ("d", 4), ("b", 6)])
1282 self.failUnlessEqual(d.item_with_largest_value(), ("b", 6))
1284 d = dictutil.NumDict({"a": 1, "b": 2})
1285 self.failUnlessEqual(repr(d), "{'a': 1, 'b': 2}")
1286 self.failUnless("a" in d)
1288 d2 = dictutil.NumDict({"c": 3, "d": 4})
1289 self.failUnless(d != d2)
1290 self.failUnless(d2 > d)
1291 self.failUnless(d2 >= d)
1292 self.failUnless(d <= d2)
1293 self.failUnless(d < d2)
1294 self.failUnlessEqual(d["a"], 1)
1295 self.failUnlessEqual(sorted(list([k for k in d])), ["a","b"])
1298 self.failUnlessRaises(TypeError, eq, d, "not a dict")
1301 self.failUnlessEqual(d, d3)
1302 self.failUnless(isinstance(d3, dictutil.NumDict))
1304 d4 = d.fromkeys(["a","b"], 5)
1305 self.failUnlessEqual(d4, {"a": 5, "b": 5})
1307 self.failUnlessEqual(d.get("a"), 1)
1308 self.failUnlessEqual(d.get("c"), 0)
1309 self.failUnlessEqual(d.get("c", 5), 5)
1310 self.failUnlessEqual(sorted(list(d.items())),
1311 [("a", 1), ("b", 2)])
1312 self.failUnlessEqual(sorted(list(d.iteritems())),
1313 [("a", 1), ("b", 2)])
1314 self.failUnlessEqual(sorted(d.keys()), ["a", "b"])
1315 self.failUnlessEqual(sorted(d.values()), [1, 2])
1316 self.failUnless(d.has_key("a"))
1317 self.failIf(d.has_key("c"))
1319 x = d.setdefault("c", 3)
1320 self.failUnlessEqual(x, 3)
1321 self.failUnlessEqual(d["c"], 3)
1322 x = d.setdefault("c", 5)
1323 self.failUnlessEqual(x, 3)
1324 self.failUnlessEqual(d["c"], 3)
1328 self.failUnless(x in [("a", 1), ("b", 2)])
1330 self.failUnless(x in [("a", 1), ("b", 2)])
1331 self.failUnlessRaises(KeyError, d.popitem)
1334 d.update({"c": 4, "d": 5})
1335 self.failUnlessEqual(d, {"c": 4, "d": 5})
1337 def test_del_if_present(self):
1338 d = {1: "a", 2: "b"}
1339 dictutil.del_if_present(d, 1)
1340 dictutil.del_if_present(d, 3)
1341 self.failUnlessEqual(d, {2: "b"})
1343 def test_valueordereddict(self):
1344 d = dictutil.ValueOrderedDict()
1349 self.failUnlessEqual(d, {"a": 3, "b": 2, "c": 1})
1350 self.failUnlessEqual(d.items(), [("c", 1), ("b", 2), ("a", 3)])
1351 self.failUnlessEqual(d.values(), [1, 2, 3])
1352 self.failUnlessEqual(d.keys(), ["c", "b", "a"])
1353 self.failUnlessEqual(repr(d), "<ValueOrderedDict {c: 1, b: 2, a: 3}>")
1356 self.failIf(d == {"a": 4})
1357 self.failUnless(d != {"a": 4})
1359 x = d.setdefault("d", 0)
1360 self.failUnlessEqual(x, 0)
1361 self.failUnlessEqual(d["d"], 0)
1362 x = d.setdefault("d", -1)
1363 self.failUnlessEqual(x, 0)
1364 self.failUnlessEqual(d["d"], 0)
1366 x = d.remove("e", "default", False)
1367 self.failUnlessEqual(x, "default")
1368 self.failUnlessRaises(KeyError, d.remove, "e", "default", True)
1369 x = d.remove("d", 5)
1370 self.failUnlessEqual(x, 0)
1372 x = d.__getitem__("c")
1373 self.failUnlessEqual(x, 1)
1374 x = d.__getitem__("e", "default", False)
1375 self.failUnlessEqual(x, "default")
1376 self.failUnlessRaises(KeyError, d.__getitem__, "e", "default", True)
1378 self.failUnlessEqual(d.popitem(), ("c", 1))
1379 self.failUnlessEqual(d.popitem(), ("b", 2))
1380 self.failUnlessEqual(d.popitem(), ("a", 3))
1381 self.failUnlessRaises(KeyError, d.popitem)
1383 d = dictutil.ValueOrderedDict({"a": 3, "b": 2, "c": 1})
1384 x = d.pop("d", "default", False)
1385 self.failUnlessEqual(x, "default")
1386 self.failUnlessRaises(KeyError, d.pop, "d", "default", True)
1388 self.failUnlessEqual(x, 2)
1389 self.failUnlessEqual(d.items(), [("c", 1), ("a", 3)])
1391 d = dictutil.ValueOrderedDict({"a": 3, "b": 2, "c": 1})
1392 x = d.pop_from_list(1) # pop the second item, b/2
1393 self.failUnlessEqual(x, "b")
1394 self.failUnlessEqual(d.items(), [("c", 1), ("a", 3)])
1396 def test_auxdict(self):
1397 d = dictutil.AuxValueDict()
1398 # we put the serialized form in the auxdata
1399 d.set_with_aux("key", ("filecap", "metadata"), "serialized")
1401 self.failUnlessEqual(d.keys(), ["key"])
1402 self.failUnlessEqual(d["key"], ("filecap", "metadata"))
1403 self.failUnlessEqual(d.get_aux("key"), "serialized")
1404 def _get_missing(key):
1406 self.failUnlessRaises(KeyError, _get_missing, "nonkey")
1407 self.failUnlessEqual(d.get("nonkey"), None)
1408 self.failUnlessEqual(d.get("nonkey", "nonvalue"), "nonvalue")
1409 self.failUnlessEqual(d.get_aux("nonkey"), None)
1410 self.failUnlessEqual(d.get_aux("nonkey", "nonvalue"), "nonvalue")
1412 d["key"] = ("filecap2", "metadata2")
1413 self.failUnlessEqual(d["key"], ("filecap2", "metadata2"))
1414 self.failUnlessEqual(d.get_aux("key"), None)
1416 d.set_with_aux("key2", "value2", "aux2")
1417 self.failUnlessEqual(sorted(d.keys()), ["key", "key2"])
1419 self.failUnlessEqual(d.keys(), ["key"])
1420 self.failIf("key2" in d)
1421 self.failUnlessRaises(KeyError, _get_missing, "key2")
1422 self.failUnlessEqual(d.get("key2"), None)
1423 self.failUnlessEqual(d.get_aux("key2"), None)
1424 d["key2"] = "newvalue2"
1425 self.failUnlessEqual(d.get("key2"), "newvalue2")
1426 self.failUnlessEqual(d.get_aux("key2"), None)
1428 d = dictutil.AuxValueDict({1:2,3:4})
1429 self.failUnlessEqual(sorted(d.keys()), [1,3])
1430 self.failUnlessEqual(d[1], 2)
1431 self.failUnlessEqual(d.get_aux(1), None)
1433 d = dictutil.AuxValueDict([ (1,2), (3,4) ])
1434 self.failUnlessEqual(sorted(d.keys()), [1,3])
1435 self.failUnlessEqual(d[1], 2)
1436 self.failUnlessEqual(d.get_aux(1), None)
1438 d = dictutil.AuxValueDict(one=1, two=2)
1439 self.failUnlessEqual(sorted(d.keys()), ["one","two"])
1440 self.failUnlessEqual(d["one"], 1)
1441 self.failUnlessEqual(d.get_aux("one"), None)
1443 class Pipeline(unittest.TestCase):
1444 def pause(self, *args, **kwargs):
1445 d = defer.Deferred()
1446 self.calls.append( (d, args, kwargs) )
1449 def failUnlessCallsAre(self, expected):
1452 self.failUnlessEqual(len(self.calls), len(expected), self.calls)
1453 for i,c in enumerate(self.calls):
1454 self.failUnlessEqual(c[1:], expected[i], str(i))
1456 def test_basic(self):
1459 p = pipeline.Pipeline(100)
1461 d = p.flush() # fires immediately
1462 d.addCallbacks(finished.append, log.err)
1463 self.failUnlessEqual(len(finished), 1)
1466 d = p.add(10, self.pause, "one")
1467 # the call should start right away, and our return Deferred should
1469 d.addCallbacks(finished.append, log.err)
1470 self.failUnlessEqual(len(finished), 1)
1471 self.failUnlessEqual(finished[0], None)
1472 self.failUnlessCallsAre([ ( ("one",) , {} ) ])
1473 self.failUnlessEqual(p.gauge, 10)
1478 d = p.add(20, self.pause, "two", kw=2)
1479 # pipeline: [one, two]
1481 # the call and the Deferred should fire right away
1482 d.addCallbacks(finished.append, log.err)
1483 self.failUnlessEqual(len(finished), 1)
1484 self.failUnlessEqual(finished[0], None)
1485 self.failUnlessCallsAre([ ( ("one",) , {} ),
1486 ( ("two",) , {"kw": 2} ),
1488 self.failUnlessEqual(p.gauge, 30)
1490 self.calls[0][0].callback("one-result")
1492 self.failUnlessEqual(p.gauge, 20)
1495 d = p.add(90, self.pause, "three", "posarg1")
1496 # pipeline: [two, three]
1499 fd.addCallbacks(flushed.append, log.err)
1500 self.failUnlessEqual(flushed, [])
1502 # the call will be made right away, but the return Deferred will not,
1503 # because the pipeline is now full.
1504 d.addCallbacks(finished.append, log.err)
1505 self.failUnlessEqual(len(finished), 0)
1506 self.failUnlessCallsAre([ ( ("one",) , {} ),
1507 ( ("two",) , {"kw": 2} ),
1508 ( ("three", "posarg1"), {} ),
1510 self.failUnlessEqual(p.gauge, 110)
1512 self.failUnlessRaises(pipeline.SingleFileError, p.add, 10, self.pause)
1514 # retiring either call will unblock the pipeline, causing the #3
1516 self.calls[2][0].callback("three-result")
1519 self.failUnlessEqual(len(finished), 1)
1520 self.failUnlessEqual(finished[0], None)
1521 self.failUnlessEqual(flushed, [])
1523 # retiring call#2 will finally allow the flush() Deferred to fire
1524 self.calls[1][0].callback("two-result")
1525 self.failUnlessEqual(len(flushed), 1)
1527 def test_errors(self):
1529 p = pipeline.Pipeline(100)
1531 d1 = p.add(200, self.pause, "one")
1535 d1.addBoth(finished.append)
1536 self.failUnlessEqual(finished, [])
1539 d2.addBoth(flushed.append)
1540 self.failUnlessEqual(flushed, [])
1542 self.calls[0][0].errback(ValueError("oops"))
1544 self.failUnlessEqual(len(finished), 1)
1546 self.failUnless(isinstance(f, Failure))
1547 self.failUnless(f.check(pipeline.PipelineError))
1548 self.failUnlessIn("PipelineError", str(f.value))
1549 self.failUnlessIn("ValueError", str(f.value))
1551 self.failUnless("ValueError" in r, r)
1553 self.failUnless(f2.check(ValueError))
1555 self.failUnlessEqual(len(flushed), 1)
1557 self.failUnless(isinstance(f, Failure))
1558 self.failUnless(f.check(pipeline.PipelineError))
1560 self.failUnless(f2.check(ValueError))
1562 # now that the pipeline is in the failed state, any new calls will
1565 d3 = p.add(20, self.pause, "two")
1568 d3.addBoth(finished.append)
1569 self.failUnlessEqual(len(finished), 1)
1571 self.failUnless(isinstance(f, Failure))
1572 self.failUnless(f.check(pipeline.PipelineError))
1574 self.failUnless("ValueError" in r, r)
1576 self.failUnless(f2.check(ValueError))
1580 d4.addBoth(flushed.append)
1581 self.failUnlessEqual(len(flushed), 1)
1583 self.failUnless(isinstance(f, Failure))
1584 self.failUnless(f.check(pipeline.PipelineError))
1586 self.failUnless(f2.check(ValueError))
1588 def test_errors2(self):
1590 p = pipeline.Pipeline(100)
1592 d1 = p.add(10, self.pause, "one")
1593 d2 = p.add(20, self.pause, "two")
1594 d3 = p.add(30, self.pause, "three")
1597 # one call fails, then the second one succeeds: make sure
1598 # ExpandableDeferredList tolerates the second one
1601 d4.addBoth(flushed.append)
1602 self.failUnlessEqual(flushed, [])
1604 self.calls[0][0].errback(ValueError("oops"))
1605 self.failUnlessEqual(len(flushed), 1)
1607 self.failUnless(isinstance(f, Failure))
1608 self.failUnless(f.check(pipeline.PipelineError))
1610 self.failUnless(f2.check(ValueError))
1612 self.calls[1][0].callback("two-result")
1613 self.calls[2][0].errback(ValueError("three-error"))
1617 class SampleError(Exception):
1620 class Log(unittest.TestCase):
1622 if not hasattr(self, "flushLoggedErrors"):
1623 # without flushLoggedErrors, we can't get rid of the
1624 # twisted.log.err that tahoe_log records, so we can't keep this
1625 # test from [ERROR]ing
1626 raise unittest.SkipTest("needs flushLoggedErrors from Twisted-2.5.0")
1628 raise SampleError("simple sample")
1631 tahoe_log.err(format="intentional sample error",
1632 failure=f, level=tahoe_log.OPERATIONAL, umid="wO9UoQ")
1633 self.flushLoggedErrors(SampleError)
1637 # this is a simple+inefficient form of util.spans.Spans . We compare the
1638 # behavior of this reference model against the real (efficient) form.
1640 def __init__(self, _span_or_start=None, length=None):
1642 if length is not None:
1643 for i in range(_span_or_start, _span_or_start+length):
1645 elif _span_or_start:
1646 for (start,length) in _span_or_start:
1647 self.add(start, length)
1649 def add(self, start, length):
1650 for i in range(start, start+length):
1654 def remove(self, start, length):
1655 for i in range(start, start+length):
1656 self._have.discard(i)
1660 return sorted(self._have)
1663 items = sorted(self._have)
1667 if prevstart is None:
1668 prevstart = prevend = i
1673 yield (prevstart, prevend-prevstart+1)
1674 prevstart = prevend = i
1675 if prevstart is not None:
1676 yield (prevstart, prevend-prevstart+1)
1678 def __nonzero__(self): # this gets us bool()
1682 return len(self._have)
1684 def __add__(self, other):
1685 s = self.__class__(self)
1686 for (start, length) in other:
1687 s.add(start, length)
1690 def __sub__(self, other):
1691 s = self.__class__(self)
1692 for (start, length) in other:
1693 s.remove(start, length)
1696 def __iadd__(self, other):
1697 for (start, length) in other:
1698 self.add(start, length)
1701 def __isub__(self, other):
1702 for (start, length) in other:
1703 self.remove(start, length)
1706 def __and__(self, other):
1707 s = self.__class__()
1708 for i in other.each():
1713 def __contains__(self, (start,length)):
1714 for i in range(start, start+length):
1715 if i not in self._have:
1719 class ByteSpans(unittest.TestCase):
1720 def test_basic(self):
1722 self.failUnlessEqual(list(s), [])
1724 self.failIf((0,1) in s)
1725 self.failUnlessEqual(s.len(), 0)
1727 s1 = Spans(3, 4) # 3,4,5,6
1730 s1 = Spans(3L, 4L) # 3,4,5,6
1736 s2.add(10,2) # 10,11
1738 self.failUnless((10,1) in s2)
1739 self.failIf((10,1) in s1)
1740 self.failUnlessEqual(list(s2.each()), [3,4,5,6,10,11])
1741 self.failUnlessEqual(s2.len(), 6)
1743 s2.add(15,2).add(20,2)
1744 self.failUnlessEqual(list(s2.each()), [3,4,5,6,10,11,15,16,20,21])
1745 self.failUnlessEqual(s2.len(), 10)
1747 s2.remove(4,3).remove(15,1)
1748 self.failUnlessEqual(list(s2.each()), [3,10,11,16,20,21])
1749 self.failUnlessEqual(s2.len(), 6)
1751 s1 = SimpleSpans(3, 4) # 3 4 5 6
1752 s2 = SimpleSpans(5, 4) # 5 6 7 8
1754 self.failUnlessEqual(list(i.each()), [5, 6])
1756 def _check1(self, s):
1757 self.failUnlessEqual(list(s), [(3,4)])
1759 self.failUnlessEqual(s.len(), 4)
1760 self.failIf((0,1) in s)
1761 self.failUnless((3,4) in s)
1762 self.failUnless((3,1) in s)
1763 self.failUnless((5,2) in s)
1764 self.failUnless((6,1) in s)
1765 self.failIf((6,2) in s)
1766 self.failIf((7,1) in s)
1767 self.failUnlessEqual(list(s.each()), [3,4,5,6])
1769 def test_large(self):
1770 s = Spans(4, 2**65) # don't do this with a SimpleSpans
1771 self.failUnlessEqual(list(s), [(4, 2**65)])
1773 self.failUnlessEqual(s.len(), 2**65)
1774 self.failIf((0,1) in s)
1775 self.failUnless((4,2) in s)
1776 self.failUnless((2**65,2) in s)
1778 def test_math(self):
1779 s1 = Spans(0, 10) # 0,1,2,3,4,5,6,7,8,9
1780 s2 = Spans(5, 3) # 5,6,7
1781 s3 = Spans(8, 4) # 8,9,10,11
1784 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,8,9])
1786 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7])
1788 self.failUnlessEqual(list(s.each()), [5,6,7])
1790 self.failUnlessEqual(list(s.each()), [5,6,7])
1792 self.failUnlessEqual(list(s.each()), [5,6,7])
1794 self.failUnlessEqual(list(s.each()), [8,9])
1796 self.failUnlessEqual(list(s.each()), [8,9])
1798 self.failUnlessEqual(list(s.each()), [])
1800 self.failUnlessEqual(list(s.each()), [])
1802 self.failUnlessEqual(list(s.each()), [])
1804 self.failUnlessEqual(list(s.each()), [])
1807 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7,8,9])
1809 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7,8,9,10,11])
1811 self.failUnlessEqual(list(s.each()), [5,6,7,8,9,10,11])
1815 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,8,9])
1818 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7])
1821 self.failUnlessEqual(list(s.each()), [5,6,7])
1825 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7,8,9])
1828 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7,8,9,10,11])
1831 self.failUnlessEqual(list(s.each()), [5,6,7,8,9,10,11])
1833 def test_random(self):
1834 # attempt to increase coverage of corner cases by comparing behavior
1835 # of a simple-but-slow model implementation against the
1836 # complex-but-fast actual implementation, in a large number of random
1840 s1 = S1(); s2 = S2()
1842 def _create(subseed):
1843 ns1 = S1(); ns2 = S2()
1845 what = _hash(subseed+str(i)).hexdigest()
1846 start = int(what[2:4], 16)
1847 length = max(1,int(what[5:6], 16))
1848 ns1.add(start, length); ns2.add(start, length)
1852 for i in range(1000):
1853 what = _hash(seed+str(i)).hexdigest()
1856 start = int(what[2:4], 16)
1857 length = max(1,int(what[5:6], 16))
1860 if subop in "01234":
1861 s1 = S1(); s2 = S2()
1862 elif subop in "5678":
1863 s1 = S1(start, length); s2 = S2(start, length)
1865 s1 = S1(s1); s2 = S2(s2)
1866 #print "s2 = %s" % s2.dump()
1868 #print "s2.add(%d,%d)" % (start, length)
1869 s1.add(start, length); s2.add(start, length)
1871 #print "s2.remove(%d,%d)" % (start, length)
1872 s1.remove(start, length); s2.remove(start, length)
1874 ns1, ns2 = _create(what[7:11])
1875 #print "s2 + %s" % ns2.dump()
1876 s1 = s1 + ns1; s2 = s2 + ns2
1878 ns1, ns2 = _create(what[7:11])
1879 #print "%s - %s" % (s2.dump(), ns2.dump())
1880 s1 = s1 - ns1; s2 = s2 - ns2
1882 ns1, ns2 = _create(what[7:11])
1883 #print "s2 += %s" % ns2.dump()
1884 s1 += ns1; s2 += ns2
1886 ns1, ns2 = _create(what[7:11])
1887 #print "%s -= %s" % (s2.dump(), ns2.dump())
1888 s1 -= ns1; s2 -= ns2
1890 ns1, ns2 = _create(what[7:11])
1891 #print "%s &= %s" % (s2.dump(), ns2.dump())
1892 s1 = s1 & ns1; s2 = s2 & ns2
1893 #print "s2 now %s" % s2.dump()
1894 self.failUnlessEqual(list(s1.each()), list(s2.each()))
1895 self.failUnlessEqual(s1.len(), s2.len())
1896 self.failUnlessEqual(bool(s1), bool(s2))
1897 self.failUnlessEqual(list(s1), list(s2))
1899 what = _hash(what[12:14]+str(j)).hexdigest()
1900 start = int(what[2:4], 16)
1901 length = max(1, int(what[5:6], 16))
1902 span = (start, length)
1903 self.failUnlessEqual(bool(span in s1), bool(span in s2))
1909 # s.add(start,length) : returns s
1910 # s.remove(start,length)
1911 # s.each() -> list of byte offsets, mostly for testing
1912 # list(s) -> list of (start,length) tuples, one per span
1913 # (start,length) in s -> True if (start..start+length-1) are all members
1914 # NOT equivalent to x in list(s)
1915 # s.len() -> number of bytes, for testing, bool(), and accounting/limiting
1916 # bool(s) (__nonzeron__)
1917 # s = s1+s2, s1-s2, +=s1, -=s1
1919 def test_overlap(self):
1924 self._test_overlap(a,b,c,d)
1926 def _test_overlap(self, a, b, c, d):
1927 s1 = set(range(a,a+b))
1928 s2 = set(range(c,c+d))
1930 #self._show_overlap(s1, "1")
1931 #self._show_overlap(s2, "2")
1932 o = overlap(a,b,c,d)
1933 expected = s1.intersection(s2)
1935 self.failUnlessEqual(o, None)
1938 so = set(range(start,start+length))
1939 #self._show(so, "o")
1940 self.failUnlessEqual(so, expected)
1942 def _show_overlap(self, s, c):
1946 for i in range(max(s)):
1953 def extend(s, start, length, fill):
1954 if len(s) >= start+length:
1956 assert len(fill) == 1
1957 return s + fill*(start+length-len(s))
1959 def replace(s, start, data):
1960 assert len(s) >= start+len(data)
1961 return s[:start] + data + s[start+len(data):]
1963 class SimpleDataSpans:
1964 def __init__(self, other=None):
1965 self.missing = "" # "1" where missing, "0" where found
1968 for (start, data) in other.get_chunks():
1969 self.add(start, data)
1971 def __nonzero__(self): # this gets us bool()
1974 return len(self.missing.replace("1", ""))
1976 return [i for (i,c) in enumerate(self.missing) if c == "0"]
1977 def _have(self, start, length):
1978 m = self.missing[start:start+length]
1979 if not m or len(m)<length or int(m):
1982 def get_chunks(self):
1983 for i in self._dump():
1984 yield (i, self.data[i])
1985 def get_spans(self):
1986 return SimpleSpans([(start,len(data))
1987 for (start,data) in self.get_chunks()])
1988 def get(self, start, length):
1989 if self._have(start, length):
1990 return self.data[start:start+length]
1992 def pop(self, start, length):
1993 data = self.get(start, length)
1995 self.remove(start, length)
1997 def remove(self, start, length):
1998 self.missing = replace(extend(self.missing, start, length, "1"),
2000 def add(self, start, data):
2001 self.missing = replace(extend(self.missing, start, len(data), "1"),
2002 start, "0"*len(data))
2003 self.data = replace(extend(self.data, start, len(data), " "),
2007 class StringSpans(unittest.TestCase):
2008 def do_basic(self, klass):
2010 self.failUnlessEqual(ds.len(), 0)
2011 self.failUnlessEqual(list(ds._dump()), [])
2012 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 0)
2014 self.failUnlessEqual(ds.get(0, 4), None)
2015 self.failUnlessEqual(ds.pop(0, 4), None)
2019 self.failUnlessEqual(ds.len(), 4)
2020 self.failUnlessEqual(list(ds._dump()), [2,3,4,5])
2021 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 4)
2023 self.failUnless((2,2) in s1)
2024 self.failUnlessEqual(ds.get(0, 4), None)
2025 self.failUnlessEqual(ds.pop(0, 4), None)
2026 self.failUnlessEqual(ds.get(4, 4), None)
2029 self.failUnlessEqual(ds2.len(), 4)
2030 self.failUnlessEqual(list(ds2._dump()), [2,3,4,5])
2031 self.failUnlessEqual(sum([len(d) for (s,d) in ds2.get_chunks()]), 4)
2032 self.failUnlessEqual(ds2.get(0, 4), None)
2033 self.failUnlessEqual(ds2.pop(0, 4), None)
2034 self.failUnlessEqual(ds2.pop(2, 3), "fou")
2035 self.failUnlessEqual(sum([len(d) for (s,d) in ds2.get_chunks()]), 1)
2036 self.failUnlessEqual(ds2.get(2, 3), None)
2037 self.failUnlessEqual(ds2.get(5, 1), "r")
2038 self.failUnlessEqual(ds.get(2, 3), "fou")
2039 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 4)
2042 self.failUnlessEqual(ds.len(), 6)
2043 self.failUnlessEqual(list(ds._dump()), [0,1,2,3,4,5])
2044 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 6)
2045 self.failUnlessEqual(ds.get(0, 4), "23fo")
2046 self.failUnlessEqual(ds.pop(0, 4), "23fo")
2047 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 2)
2048 self.failUnlessEqual(ds.get(0, 4), None)
2049 self.failUnlessEqual(ds.pop(0, 4), None)
2054 self.failUnlessEqual(ds.get(2, 4), "fear")
2059 self.failUnlessEqual(ds.get(2L, 4L), "fear")
2062 def do_scan(self, klass):
2063 # do a test with gaps and spans of size 1 and 2
2064 # left=(1,11) * right=(1,11) * gapsize=(1,2)
2065 # 111, 112, 121, 122, 211, 212, 221, 222
2074 # 11 1 1 11 11 11 1 1 111
2075 # 0123456789012345678901234567
2076 # abcdefghijklmnopqrstuvwxyz-=
2077 pieces = [(1, "bc"),
2087 p_elements = set([1,2,4,7,9,10,12,13,16,17,20,22,25,26,27])
2088 S = "abcdefghijklmnopqrstuvwxyz-="
2089 # TODO: when adding data, add capital letters, to make sure we aren't
2090 # just leaving the old data in place
2094 for start, data in pieces:
2099 d = "".join([((i not in p) and " " or S[i]) for i in range(l)])
2103 for start in range(0, l):
2104 for end in range(start+1, l):
2105 # add [start-end) to the baseline
2106 which = "%d-%d" % (start, end-1)
2107 p_added = set(range(start, end))
2111 print dump(b), which
2112 add = klass(); add.add(start, S[start:end])
2114 b.add(start, S[start:end])
2117 # check that the new span is there
2118 d = b.get(start, end-start)
2119 self.failUnlessEqual(d, S[start:end], which)
2120 # check that all the original pieces are still there
2121 for t_start, t_data in pieces:
2123 self.failUnlessEqual(b.get(t_start, t_len),
2124 S[t_start:t_start+t_len],
2125 "%s %d+%d" % (which, t_start, t_len))
2126 # check that a lot of subspans are mostly correct
2127 for t_start in range(l):
2128 for t_len in range(1,4):
2129 d = b.get(t_start, t_len)
2131 which2 = "%s+(%d-%d)" % (which, t_start,
2133 self.failUnlessEqual(d, S[t_start:t_start+t_len],
2135 # check that removing a subspan gives the right value
2137 b2.remove(t_start, t_len)
2138 removed = set(range(t_start, t_start+t_len))
2140 exp = (((i in p_elements) or (i in p_added))
2141 and (i not in removed))
2142 which2 = "%s-(%d-%d)" % (which, t_start,
2144 self.failUnlessEqual(bool(b2.get(i, 1)), exp,
2147 def test_test(self):
2148 self.do_basic(SimpleDataSpans)
2149 self.do_scan(SimpleDataSpans)
2151 def test_basic(self):
2152 self.do_basic(DataSpans)
2153 self.do_scan(DataSpans)
2155 def test_random(self):
2156 # attempt to increase coverage of corner cases by comparing behavior
2157 # of a simple-but-slow model implementation against the
2158 # complex-but-fast actual implementation, in a large number of random
2160 S1 = SimpleDataSpans
2162 s1 = S1(); s2 = S2()
2164 def _randstr(length, seed):
2167 while created < length:
2168 piece = _hash(seed + str(created)).hexdigest()
2169 pieces.append(piece)
2170 created += len(piece)
2171 return "".join(pieces)[:length]
2172 def _create(subseed):
2173 ns1 = S1(); ns2 = S2()
2175 what = _hash(subseed+str(i)).hexdigest()
2176 start = int(what[2:4], 16)
2177 length = max(1,int(what[5:6], 16))
2178 ns1.add(start, _randstr(length, what[7:9]));
2179 ns2.add(start, _randstr(length, what[7:9]))
2183 for i in range(1000):
2184 what = _hash(seed+str(i)).hexdigest()
2187 start = int(what[2:4], 16)
2188 length = max(1,int(what[5:6], 16))
2191 if subop in "0123456":
2192 s1 = S1(); s2 = S2()
2194 s1, s2 = _create(what[7:11])
2195 #print "s2 = %s" % list(s2._dump())
2196 elif op in "123456":
2197 #print "s2.add(%d,%d)" % (start, length)
2198 s1.add(start, _randstr(length, what[7:9]));
2199 s2.add(start, _randstr(length, what[7:9]))
2200 elif op in "789abc":
2201 #print "s2.remove(%d,%d)" % (start, length)
2202 s1.remove(start, length); s2.remove(start, length)
2204 #print "s2.pop(%d,%d)" % (start, length)
2205 d1 = s1.pop(start, length); d2 = s2.pop(start, length)
2206 self.failUnlessEqual(d1, d2)
2207 #print "s1 now %s" % list(s1._dump())
2208 #print "s2 now %s" % list(s2._dump())
2209 self.failUnlessEqual(s1.len(), s2.len())
2210 self.failUnlessEqual(list(s1._dump()), list(s2._dump()))
2211 for j in range(100):
2212 what = _hash(what[12:14]+str(j)).hexdigest()
2213 start = int(what[2:4], 16)
2214 length = max(1, int(what[5:6], 16))
2215 d1 = s1.get(start, length); d2 = s2.get(start, length)
2216 self.failUnlessEqual(d1, d2, "%d+%d" % (start, length))