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, deferredutil.WaitForDelayedCallsMixin):
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 def test_wait_for_delayed_calls(self):
626 This tests that 'wait_for_delayed_calls' does in fact wait for a
627 delayed call that is active when the test returns. If it didn't,
628 Trial would report an unclean reactor error for this test.
633 reactor.callLater(0.1, _trigger)
635 d = defer.succeed(None)
636 d.addBoth(self.wait_for_delayed_calls)
639 class HashUtilTests(unittest.TestCase):
641 def test_random_key(self):
642 k = hashutil.random_key()
643 self.failUnlessEqual(len(k), hashutil.KEYLEN)
645 def test_sha256d(self):
646 h1 = hashutil.tagged_hash("tag1", "value")
647 h2 = hashutil.tagged_hasher("tag1")
651 self.failUnlessEqual(h1, h2a)
652 self.failUnlessEqual(h2a, h2b)
654 def test_sha256d_truncated(self):
655 h1 = hashutil.tagged_hash("tag1", "value", 16)
656 h2 = hashutil.tagged_hasher("tag1", 16)
659 self.failUnlessEqual(len(h1), 16)
660 self.failUnlessEqual(len(h2), 16)
661 self.failUnlessEqual(h1, h2)
664 h1 = hashutil.convergence_hash(3, 10, 1000, "data", "secret")
665 h2 = hashutil.convergence_hasher(3, 10, 1000, "secret")
668 self.failUnlessEqual(h1, h2)
670 def test_hashers(self):
671 h1 = hashutil.block_hash("foo")
672 h2 = hashutil.block_hasher()
674 self.failUnlessEqual(h1, h2.digest())
676 h1 = hashutil.uri_extension_hash("foo")
677 h2 = hashutil.uri_extension_hasher()
679 self.failUnlessEqual(h1, h2.digest())
681 h1 = hashutil.plaintext_hash("foo")
682 h2 = hashutil.plaintext_hasher()
684 self.failUnlessEqual(h1, h2.digest())
686 h1 = hashutil.crypttext_hash("foo")
687 h2 = hashutil.crypttext_hasher()
689 self.failUnlessEqual(h1, h2.digest())
691 h1 = hashutil.crypttext_segment_hash("foo")
692 h2 = hashutil.crypttext_segment_hasher()
694 self.failUnlessEqual(h1, h2.digest())
696 h1 = hashutil.plaintext_segment_hash("foo")
697 h2 = hashutil.plaintext_segment_hasher()
699 self.failUnlessEqual(h1, h2.digest())
701 def test_timing_safe_compare(self):
702 self.failUnless(hashutil.timing_safe_compare("a", "a"))
703 self.failUnless(hashutil.timing_safe_compare("ab", "ab"))
704 self.failIf(hashutil.timing_safe_compare("a", "b"))
705 self.failIf(hashutil.timing_safe_compare("a", "aa"))
707 def _testknown(self, hashf, expected_a, *args):
709 got_a = base32.b2a(got)
710 self.failUnlessEqual(got_a, expected_a)
712 def test_known_answers(self):
713 # assert backwards compatibility
714 self._testknown(hashutil.storage_index_hash, "qb5igbhcc5esa6lwqorsy7e6am", "")
715 self._testknown(hashutil.block_hash, "msjr5bh4evuh7fa3zw7uovixfbvlnstr5b65mrerwfnvjxig2jvq", "")
716 self._testknown(hashutil.uri_extension_hash, "wthsu45q7zewac2mnivoaa4ulh5xvbzdmsbuyztq2a5fzxdrnkka", "")
717 self._testknown(hashutil.plaintext_hash, "5lz5hwz3qj3af7n6e3arblw7xzutvnd3p3fjsngqjcb7utf3x3da", "")
718 self._testknown(hashutil.crypttext_hash, "itdj6e4njtkoiavlrmxkvpreosscssklunhwtvxn6ggho4rkqwga", "")
719 self._testknown(hashutil.crypttext_segment_hash, "aovy5aa7jej6ym5ikgwyoi4pxawnoj3wtaludjz7e2nb5xijb7aa", "")
720 self._testknown(hashutil.plaintext_segment_hash, "4fdgf6qruaisyukhqcmoth4t3li6bkolbxvjy4awwcpprdtva7za", "")
721 self._testknown(hashutil.convergence_hash, "3mo6ni7xweplycin6nowynw2we", 3, 10, 100, "", "converge")
722 self._testknown(hashutil.my_renewal_secret_hash, "ujhr5k5f7ypkp67jkpx6jl4p47pyta7hu5m527cpcgvkafsefm6q", "")
723 self._testknown(hashutil.my_cancel_secret_hash, "rjwzmafe2duixvqy6h47f5wfrokdziry6zhx4smew4cj6iocsfaa", "")
724 self._testknown(hashutil.file_renewal_secret_hash, "hzshk2kf33gzbd5n3a6eszkf6q6o6kixmnag25pniusyaulqjnia", "", "si")
725 self._testknown(hashutil.file_cancel_secret_hash, "bfciwvr6w7wcavsngxzxsxxaszj72dej54n4tu2idzp6b74g255q", "", "si")
726 self._testknown(hashutil.bucket_renewal_secret_hash, "e7imrzgzaoashsncacvy3oysdd2m5yvtooo4gmj4mjlopsazmvuq", "", "\x00"*20)
727 self._testknown(hashutil.bucket_cancel_secret_hash, "dvdujeyxeirj6uux6g7xcf4lvesk632aulwkzjar7srildvtqwma", "", "\x00"*20)
728 self._testknown(hashutil.hmac, "c54ypfi6pevb3nvo6ba42jtglpkry2kbdopqsi7dgrm4r7tw5sra", "tag", "")
729 self._testknown(hashutil.mutable_rwcap_key_hash, "6rvn2iqrghii5n4jbbwwqqsnqu", "iv", "wk")
730 self._testknown(hashutil.ssk_writekey_hash, "ykpgmdbpgbb6yqz5oluw2q26ye", "")
731 self._testknown(hashutil.ssk_write_enabler_master_hash, "izbfbfkoait4dummruol3gy2bnixrrrslgye6ycmkuyujnenzpia", "")
732 self._testknown(hashutil.ssk_write_enabler_hash, "fuu2dvx7g6gqu5x22vfhtyed7p4pd47y5hgxbqzgrlyvxoev62tq", "wk", "\x00"*20)
733 self._testknown(hashutil.ssk_pubkey_fingerprint_hash, "3opzw4hhm2sgncjx224qmt5ipqgagn7h5zivnfzqycvgqgmgz35q", "")
734 self._testknown(hashutil.ssk_readkey_hash, "vugid4as6qbqgeq2xczvvcedai", "")
735 self._testknown(hashutil.ssk_readkey_data_hash, "73wsaldnvdzqaf7v4pzbr2ae5a", "iv", "rk")
736 self._testknown(hashutil.ssk_storage_index_hash, "j7icz6kigb6hxrej3tv4z7ayym", "")
739 class Abbreviate(unittest.TestCase):
741 a = abbreviate.abbreviate_time
742 self.failUnlessEqual(a(None), "unknown")
743 self.failUnlessEqual(a(0), "0 seconds")
744 self.failUnlessEqual(a(1), "1 second")
745 self.failUnlessEqual(a(2), "2 seconds")
746 self.failUnlessEqual(a(119), "119 seconds")
748 self.failUnlessEqual(a(2*MIN), "2 minutes")
749 self.failUnlessEqual(a(60*MIN), "60 minutes")
750 self.failUnlessEqual(a(179*MIN), "179 minutes")
752 self.failUnlessEqual(a(180*MIN), "3 hours")
753 self.failUnlessEqual(a(4*HOUR), "4 hours")
756 self.failUnlessEqual(a(2*DAY), "2 days")
757 self.failUnlessEqual(a(2*MONTH), "2 months")
759 self.failUnlessEqual(a(5*YEAR), "5 years")
761 def test_space(self):
762 tests_si = [(None, "unknown"),
769 (20*1000, "20.00 kB"),
770 (1024*1024, "1.05 MB"),
771 (1000*1000, "1.00 MB"),
772 (1000*1000*1000, "1.00 GB"),
773 (1000*1000*1000*1000, "1.00 TB"),
774 (1000*1000*1000*1000*1000, "1.00 PB"),
775 (1000*1000*1000*1000*1000*1000, "1.00 EB"),
776 (1234567890123456789, "1.23 EB"),
778 for (x, expected) in tests_si:
779 got = abbreviate.abbreviate_space(x, SI=True)
780 self.failUnlessEqual(got, expected)
782 tests_base1024 = [(None, "unknown"),
789 (20*1024, "20.00 kiB"),
790 (1000*1000, "976.56 kiB"),
791 (1024*1024, "1.00 MiB"),
792 (1024*1024*1024, "1.00 GiB"),
793 (1024*1024*1024*1024, "1.00 TiB"),
794 (1000*1000*1000*1000*1000, "909.49 TiB"),
795 (1024*1024*1024*1024*1024, "1.00 PiB"),
796 (1024*1024*1024*1024*1024*1024, "1.00 EiB"),
797 (1234567890123456789, "1.07 EiB"),
799 for (x, expected) in tests_base1024:
800 got = abbreviate.abbreviate_space(x, SI=False)
801 self.failUnlessEqual(got, expected)
803 self.failUnlessEqual(abbreviate.abbreviate_space_both(1234567),
804 "(1.23 MB, 1.18 MiB)")
806 def test_parse_space(self):
807 p = abbreviate.parse_abbreviated_size
808 self.failUnlessEqual(p(""), None)
809 self.failUnlessEqual(p(None), None)
810 self.failUnlessEqual(p("123"), 123)
811 self.failUnlessEqual(p("123B"), 123)
812 self.failUnlessEqual(p("2K"), 2000)
813 self.failUnlessEqual(p("2kb"), 2000)
814 self.failUnlessEqual(p("2KiB"), 2048)
815 self.failUnlessEqual(p("10MB"), 10*1000*1000)
816 self.failUnlessEqual(p("10MiB"), 10*1024*1024)
817 self.failUnlessEqual(p("5G"), 5*1000*1000*1000)
818 self.failUnlessEqual(p("4GiB"), 4*1024*1024*1024)
819 self.failUnlessEqual(p("3TB"), 3*1000*1000*1000*1000)
820 self.failUnlessEqual(p("3TiB"), 3*1024*1024*1024*1024)
821 self.failUnlessEqual(p("6PB"), 6*1000*1000*1000*1000*1000)
822 self.failUnlessEqual(p("6PiB"), 6*1024*1024*1024*1024*1024)
823 self.failUnlessEqual(p("9EB"), 9*1000*1000*1000*1000*1000*1000)
824 self.failUnlessEqual(p("9EiB"), 9*1024*1024*1024*1024*1024*1024)
826 e = self.failUnlessRaises(ValueError, p, "12 cubits")
827 self.failUnlessIn("12 cubits", str(e))
828 e = self.failUnlessRaises(ValueError, p, "1 BB")
829 self.failUnlessIn("1 BB", str(e))
830 e = self.failUnlessRaises(ValueError, p, "fhtagn")
831 self.failUnlessIn("fhtagn", str(e))
833 class Limiter(unittest.TestCase):
834 timeout = 480 # This takes longer than 240 seconds on Francois's arm box.
836 def job(self, i, foo):
837 self.calls.append( (i, foo) )
838 self.simultaneous += 1
839 self.peak_simultaneous = max(self.simultaneous, self.peak_simultaneous)
842 self.simultaneous -= 1
843 d.callback("done %d" % i)
844 reactor.callLater(1.0, _done)
847 def bad_job(self, i, foo):
848 raise ValueError("bad_job %d" % i)
850 def test_limiter(self):
852 self.simultaneous = 0
853 self.peak_simultaneous = 0
854 l = limiter.ConcurrencyLimiter()
857 dl.append(l.add(self.job, i, foo=str(i)))
858 d = defer.DeferredList(dl, fireOnOneErrback=True)
860 self.failUnlessEqual(self.simultaneous, 0)
861 self.failUnless(self.peak_simultaneous <= 10)
862 self.failUnlessEqual(len(self.calls), 20)
864 self.failUnless( (i, str(i)) in self.calls)
868 def test_errors(self):
870 self.simultaneous = 0
871 self.peak_simultaneous = 0
872 l = limiter.ConcurrencyLimiter()
875 dl.append(l.add(self.job, i, foo=str(i)))
876 d2 = l.add(self.bad_job, 21, "21")
877 d = defer.DeferredList(dl, fireOnOneErrback=True)
880 for (success, result) in res:
881 self.failUnlessEqual(success, True)
882 results.append(result)
884 expected_results = ["done %d" % i for i in range(20)]
885 expected_results.sort()
886 self.failUnlessEqual(results, expected_results)
887 self.failUnless(self.peak_simultaneous <= 10)
888 self.failUnlessEqual(len(self.calls), 20)
890 self.failUnless( (i, str(i)) in self.calls)
892 self.fail("should have failed, not got %s" % (res,))
895 self.failUnless("bad_job 21" in str(f))
896 d2.addCallbacks(_good, _err)
898 d.addCallback(_most_done)
900 self.failUnlessEqual(self.simultaneous, 0)
901 self.failUnless(self.peak_simultaneous <= 10)
902 self.failUnlessEqual(len(self.calls), 20)
904 self.failUnless( (i, str(i)) in self.calls)
905 d.addCallback(_all_done)
908 class TimeFormat(unittest.TestCase):
909 def test_epoch(self):
910 return self._help_test_epoch()
912 def test_epoch_in_London(self):
913 # Europe/London is a particularly troublesome timezone. Nowadays, its
914 # offset from GMT is 0. But in 1970, its offset from GMT was 1.
915 # (Apparently in 1970 Britain had redefined standard time to be GMT+1
916 # and stayed in standard time all year round, whereas today
917 # Europe/London standard time is GMT and Europe/London Daylight
918 # Savings Time is GMT+1.) The current implementation of
919 # time_format.iso_utc_time_to_localseconds() breaks if the timezone is
920 # Europe/London. (As soon as this unit test is done then I'll change
921 # that implementation to something that works even in this case...)
922 origtz = os.environ.get('TZ')
923 os.environ['TZ'] = "Europe/London"
924 if hasattr(time, 'tzset'):
927 return self._help_test_epoch()
932 os.environ['TZ'] = origtz
933 if hasattr(time, 'tzset'):
936 def _help_test_epoch(self):
937 origtzname = time.tzname
938 s = time_format.iso_utc_time_to_seconds("1970-01-01T00:00:01")
939 self.failUnlessEqual(s, 1.0)
940 s = time_format.iso_utc_time_to_seconds("1970-01-01_00:00:01")
941 self.failUnlessEqual(s, 1.0)
942 s = time_format.iso_utc_time_to_seconds("1970-01-01 00:00:01")
943 self.failUnlessEqual(s, 1.0)
945 self.failUnlessEqual(time_format.iso_utc(1.0), "1970-01-01_00:00:01")
946 self.failUnlessEqual(time_format.iso_utc(1.0, sep=" "),
947 "1970-01-01 00:00:01")
950 isostr = time_format.iso_utc(now)
951 timestamp = time_format.iso_utc_time_to_seconds(isostr)
952 self.failUnlessEqual(int(timestamp), int(now))
956 self.failUnlessEqual(time_format.iso_utc(t=my_time),
957 "1970-01-01_00:00:01")
958 e = self.failUnlessRaises(ValueError,
959 time_format.iso_utc_time_to_seconds,
960 "invalid timestring")
961 self.failUnless("not a complete ISO8601 timestamp" in str(e))
962 s = time_format.iso_utc_time_to_seconds("1970-01-01_00:00:01.500")
963 self.failUnlessEqual(s, 1.5)
965 # Look for daylight-savings-related errors.
966 thatmomentinmarch = time_format.iso_utc_time_to_seconds("2009-03-20 21:49:02.226536")
967 self.failUnlessEqual(thatmomentinmarch, 1237585742.226536)
968 self.failUnlessEqual(origtzname, time.tzname)
970 def test_iso_utc(self):
971 when = 1266760143.7841301
972 out = time_format.iso_utc_date(when)
973 self.failUnlessEqual(out, "2010-02-21")
974 out = time_format.iso_utc_date(t=lambda: when)
975 self.failUnlessEqual(out, "2010-02-21")
976 out = time_format.iso_utc(when)
977 self.failUnlessEqual(out, "2010-02-21_13:49:03.784130")
978 out = time_format.iso_utc(when, sep="-")
979 self.failUnlessEqual(out, "2010-02-21-13:49:03.784130")
981 def test_parse_duration(self):
982 p = time_format.parse_duration
984 self.failUnlessEqual(p("1 day"), DAY)
985 self.failUnlessEqual(p("2 days"), 2*DAY)
986 self.failUnlessEqual(p("3 months"), 3*31*DAY)
987 self.failUnlessEqual(p("4 mo"), 4*31*DAY)
988 self.failUnlessEqual(p("5 years"), 5*365*DAY)
989 e = self.failUnlessRaises(ValueError, p, "123")
990 self.failUnlessIn("no unit (like day, month, or year) in '123'",
993 def test_parse_date(self):
994 self.failUnlessEqual(time_format.parse_date("2010-02-21"), 1266710400)
996 class CacheDir(unittest.TestCase):
997 def test_basic(self):
998 basedir = "test_util/CacheDir/test_basic"
1000 def _failIfExists(name):
1001 absfn = os.path.join(basedir, name)
1002 self.failIf(os.path.exists(absfn),
1003 "%s exists but it shouldn't" % absfn)
1005 def _failUnlessExists(name):
1006 absfn = os.path.join(basedir, name)
1007 self.failUnless(os.path.exists(absfn),
1008 "%s doesn't exist but it should" % absfn)
1010 cdm = cachedir.CacheDirectoryManager(basedir)
1011 a = cdm.get_file("a")
1012 b = cdm.get_file("b")
1013 c = cdm.get_file("c")
1014 f = open(a.get_filename(), "wb"); f.write("hi"); f.close(); del f
1015 f = open(b.get_filename(), "wb"); f.write("hi"); f.close(); del f
1016 f = open(c.get_filename(), "wb"); f.write("hi"); f.close(); del f
1018 _failUnlessExists("a")
1019 _failUnlessExists("b")
1020 _failUnlessExists("c")
1024 _failUnlessExists("a")
1025 _failUnlessExists("b")
1026 _failUnlessExists("c")
1029 # this file won't be deleted yet, because it isn't old enough
1031 _failUnlessExists("a")
1032 _failUnlessExists("b")
1033 _failUnlessExists("c")
1035 # we change the definition of "old" to make everything old
1040 _failUnlessExists("b")
1041 _failUnlessExists("c")
1049 _failUnlessExists("b")
1050 _failUnlessExists("c")
1052 b2 = cdm.get_file("b")
1056 _failUnlessExists("b")
1057 _failUnlessExists("c")
1062 def __init__(self, x):
1067 return "<%s %s>" % (self.__class__.__name__, self.x,)
1070 def __le__(self, other):
1071 return self.x <= other
1072 def __lt__(self, other):
1073 return self.x < other
1074 def __ge__(self, other):
1075 return self.x >= other
1076 def __gt__(self, other):
1077 return self.x > other
1078 def __ne__(self, other):
1079 return self.x != other
1080 def __eq__(self, other):
1081 return self.x == other
1083 class DictUtil(unittest.TestCase):
1084 def _help_test_empty_dict(self, klass):
1088 self.failUnless(d1 == d2, "d1: %r, d2: %r" % (d1, d2,))
1089 self.failUnless(len(d1) == 0)
1090 self.failUnless(len(d2) == 0)
1092 def _help_test_nonempty_dict(self, klass):
1093 d1 = klass({'a': 1, 'b': "eggs", 3: "spam",})
1094 d2 = klass({'a': 1, 'b': "eggs", 3: "spam",})
1096 self.failUnless(d1 == d2)
1097 self.failUnless(len(d1) == 3, "%s, %s" % (len(d1), d1,))
1098 self.failUnless(len(d2) == 3)
1100 def _help_test_eq_but_notis(self, klass):
1101 d = klass({'a': 3, 'b': EqButNotIs(3), 'c': 3})
1106 d['b'] = EqButNotIs(3)
1111 d['b'] = EqButNotIs(3)
1117 d['a'] = EqButNotIs(3)
1122 fake3 = EqButNotIs(3)
1123 fake7 = EqButNotIs(7)
1127 self.failUnless(filter(lambda x: x is 8, d.itervalues()))
1128 self.failUnless(filter(lambda x: x is fake7, d.itervalues()))
1129 # The real 7 should have been ejected by the d[3] = 8.
1130 self.failUnless(not filter(lambda x: x is 7, d.itervalues()))
1131 self.failUnless(filter(lambda x: x is fake3, d.iterkeys()))
1132 self.failUnless(filter(lambda x: x is 3, d.iterkeys()))
1137 fake3 = EqButNotIs(3)
1138 fake7 = EqButNotIs(7)
1141 self.failUnless(filter(lambda x: x is 8, d.itervalues()))
1142 self.failUnless(filter(lambda x: x is fake7, d.itervalues()))
1143 # The real 7 should have been ejected by the d[3] = 8.
1144 self.failUnless(not filter(lambda x: x is 7, d.itervalues()))
1145 self.failUnless(filter(lambda x: x is fake3, d.iterkeys()))
1146 self.failUnless(filter(lambda x: x is 3, d.iterkeys()))
1150 self._help_test_eq_but_notis(dictutil.UtilDict)
1151 self._help_test_eq_but_notis(dictutil.NumDict)
1152 self._help_test_eq_but_notis(dictutil.ValueOrderedDict)
1153 self._help_test_nonempty_dict(dictutil.UtilDict)
1154 self._help_test_nonempty_dict(dictutil.NumDict)
1155 self._help_test_nonempty_dict(dictutil.ValueOrderedDict)
1156 self._help_test_eq_but_notis(dictutil.UtilDict)
1157 self._help_test_eq_but_notis(dictutil.NumDict)
1158 self._help_test_eq_but_notis(dictutil.ValueOrderedDict)
1160 def test_dict_of_sets(self):
1161 ds = dictutil.DictOfSets()
1166 self.failUnlessEqual(ds[1], set(["a"]))
1167 self.failUnlessEqual(ds[2], set(["b", "c"]))
1168 ds.discard(3, "d") # should not raise an exception
1170 self.failUnlessEqual(ds[2], set(["c"]))
1172 self.failIf(2 in ds)
1175 ds2 = dictutil.DictOfSets()
1180 self.failUnlessEqual(ds[1], set(["a"]))
1181 self.failUnlessEqual(ds[3], set(["f", "g"]))
1182 self.failUnlessEqual(ds[4], set(["h"]))
1184 def test_move(self):
1185 d1 = {1: "a", 2: "b"}
1186 d2 = {2: "c", 3: "d"}
1187 dictutil.move(1, d1, d2)
1188 self.failUnlessEqual(d1, {2: "b"})
1189 self.failUnlessEqual(d2, {1: "a", 2: "c", 3: "d"})
1191 d1 = {1: "a", 2: "b"}
1192 d2 = {2: "c", 3: "d"}
1193 dictutil.move(2, d1, d2)
1194 self.failUnlessEqual(d1, {1: "a"})
1195 self.failUnlessEqual(d2, {2: "b", 3: "d"})
1197 d1 = {1: "a", 2: "b"}
1198 d2 = {2: "c", 3: "d"}
1199 self.failUnlessRaises(KeyError, dictutil.move, 5, d1, d2, strict=True)
1201 def test_subtract(self):
1202 d1 = {1: "a", 2: "b"}
1203 d2 = {2: "c", 3: "d"}
1204 d3 = dictutil.subtract(d1, d2)
1205 self.failUnlessEqual(d3, {1: "a"})
1207 d1 = {1: "a", 2: "b"}
1209 d3 = dictutil.subtract(d1, d2)
1210 self.failUnlessEqual(d3, {1: "a"})
1212 def test_utildict(self):
1213 d = dictutil.UtilDict({1: "a", 2: "b"})
1216 self.failUnlessEqual(d, {2: "b"})
1219 self.failUnlessRaises(TypeError, eq, d, "not a dict")
1221 d = dictutil.UtilDict({1: "b", 2: "a"})
1222 self.failUnlessEqual(d.items_sorted_by_value(),
1223 [(2, "a"), (1, "b")])
1224 self.failUnlessEqual(d.items_sorted_by_key(),
1225 [(1, "b"), (2, "a")])
1226 self.failUnlessEqual(repr(d), "{1: 'b', 2: 'a'}")
1227 self.failUnless(1 in d)
1229 d2 = dictutil.UtilDict({3: "c", 4: "d"})
1230 self.failUnless(d != d2)
1231 self.failUnless(d2 > d)
1232 self.failUnless(d2 >= d)
1233 self.failUnless(d <= d2)
1234 self.failUnless(d < d2)
1235 self.failUnlessEqual(d[1], "b")
1236 self.failUnlessEqual(sorted(list([k for k in d])), [1,2])
1239 self.failUnlessEqual(d, d3)
1240 self.failUnless(isinstance(d3, dictutil.UtilDict))
1242 d4 = d.fromkeys([3,4], "e")
1243 self.failUnlessEqual(d4, {3: "e", 4: "e"})
1245 self.failUnlessEqual(d.get(1), "b")
1246 self.failUnlessEqual(d.get(3), None)
1247 self.failUnlessEqual(d.get(3, "default"), "default")
1248 self.failUnlessEqual(sorted(list(d.items())),
1249 [(1, "b"), (2, "a")])
1250 self.failUnlessEqual(sorted(list(d.iteritems())),
1251 [(1, "b"), (2, "a")])
1252 self.failUnlessEqual(sorted(d.keys()), [1, 2])
1253 self.failUnlessEqual(sorted(d.values()), ["a", "b"])
1254 x = d.setdefault(1, "new")
1255 self.failUnlessEqual(x, "b")
1256 self.failUnlessEqual(d[1], "b")
1257 x = d.setdefault(3, "new")
1258 self.failUnlessEqual(x, "new")
1259 self.failUnlessEqual(d[3], "new")
1263 self.failUnless(x in [(1, "b"), (2, "a")])
1265 self.failUnless(x in [(1, "b"), (2, "a")])
1266 self.failUnlessRaises(KeyError, d.popitem)
1268 def test_numdict(self):
1269 d = dictutil.NumDict({"a": 1, "b": 2})
1271 d.add_num("a", 10, 5)
1272 d.add_num("c", 20, 5)
1274 self.failUnlessEqual(d, {"a": 11, "b": 2, "c": 25, "d": 30})
1276 d.subtract_num("a", 10)
1277 d.subtract_num("e", 10)
1278 d.subtract_num("f", 10, 15)
1279 self.failUnlessEqual(d, {"a": 1, "b": 2, "c": 25, "d": 30,
1282 self.failUnlessEqual(d.sum(), sum([1, 2, 25, 30, -10, 5]))
1284 d = dictutil.NumDict()
1288 self.failUnlessEqual(d, {"a": 2, "b": 6})
1292 self.failUnlessEqual(d, {"a": 1, "b": 6, "c": -1, "d": 4})
1293 self.failUnlessEqual(d.items_sorted_by_key(),
1294 [("a", 1), ("b", 6), ("c", -1), ("d", 4)])
1295 self.failUnlessEqual(d.items_sorted_by_value(),
1296 [("c", -1), ("a", 1), ("d", 4), ("b", 6)])
1297 self.failUnlessEqual(d.item_with_largest_value(), ("b", 6))
1299 d = dictutil.NumDict({"a": 1, "b": 2})
1300 self.failUnlessEqual(repr(d), "{'a': 1, 'b': 2}")
1301 self.failUnless("a" in d)
1303 d2 = dictutil.NumDict({"c": 3, "d": 4})
1304 self.failUnless(d != d2)
1305 self.failUnless(d2 > d)
1306 self.failUnless(d2 >= d)
1307 self.failUnless(d <= d2)
1308 self.failUnless(d < d2)
1309 self.failUnlessEqual(d["a"], 1)
1310 self.failUnlessEqual(sorted(list([k for k in d])), ["a","b"])
1313 self.failUnlessRaises(TypeError, eq, d, "not a dict")
1316 self.failUnlessEqual(d, d3)
1317 self.failUnless(isinstance(d3, dictutil.NumDict))
1319 d4 = d.fromkeys(["a","b"], 5)
1320 self.failUnlessEqual(d4, {"a": 5, "b": 5})
1322 self.failUnlessEqual(d.get("a"), 1)
1323 self.failUnlessEqual(d.get("c"), 0)
1324 self.failUnlessEqual(d.get("c", 5), 5)
1325 self.failUnlessEqual(sorted(list(d.items())),
1326 [("a", 1), ("b", 2)])
1327 self.failUnlessEqual(sorted(list(d.iteritems())),
1328 [("a", 1), ("b", 2)])
1329 self.failUnlessEqual(sorted(d.keys()), ["a", "b"])
1330 self.failUnlessEqual(sorted(d.values()), [1, 2])
1331 self.failUnless(d.has_key("a"))
1332 self.failIf(d.has_key("c"))
1334 x = d.setdefault("c", 3)
1335 self.failUnlessEqual(x, 3)
1336 self.failUnlessEqual(d["c"], 3)
1337 x = d.setdefault("c", 5)
1338 self.failUnlessEqual(x, 3)
1339 self.failUnlessEqual(d["c"], 3)
1343 self.failUnless(x in [("a", 1), ("b", 2)])
1345 self.failUnless(x in [("a", 1), ("b", 2)])
1346 self.failUnlessRaises(KeyError, d.popitem)
1349 d.update({"c": 4, "d": 5})
1350 self.failUnlessEqual(d, {"c": 4, "d": 5})
1352 def test_del_if_present(self):
1353 d = {1: "a", 2: "b"}
1354 dictutil.del_if_present(d, 1)
1355 dictutil.del_if_present(d, 3)
1356 self.failUnlessEqual(d, {2: "b"})
1358 def test_valueordereddict(self):
1359 d = dictutil.ValueOrderedDict()
1364 self.failUnlessEqual(d, {"a": 3, "b": 2, "c": 1})
1365 self.failUnlessEqual(d.items(), [("c", 1), ("b", 2), ("a", 3)])
1366 self.failUnlessEqual(d.values(), [1, 2, 3])
1367 self.failUnlessEqual(d.keys(), ["c", "b", "a"])
1368 self.failUnlessEqual(repr(d), "<ValueOrderedDict {c: 1, b: 2, a: 3}>")
1371 self.failIf(d == {"a": 4})
1372 self.failUnless(d != {"a": 4})
1374 x = d.setdefault("d", 0)
1375 self.failUnlessEqual(x, 0)
1376 self.failUnlessEqual(d["d"], 0)
1377 x = d.setdefault("d", -1)
1378 self.failUnlessEqual(x, 0)
1379 self.failUnlessEqual(d["d"], 0)
1381 x = d.remove("e", "default", False)
1382 self.failUnlessEqual(x, "default")
1383 self.failUnlessRaises(KeyError, d.remove, "e", "default", True)
1384 x = d.remove("d", 5)
1385 self.failUnlessEqual(x, 0)
1387 x = d.__getitem__("c")
1388 self.failUnlessEqual(x, 1)
1389 x = d.__getitem__("e", "default", False)
1390 self.failUnlessEqual(x, "default")
1391 self.failUnlessRaises(KeyError, d.__getitem__, "e", "default", True)
1393 self.failUnlessEqual(d.popitem(), ("c", 1))
1394 self.failUnlessEqual(d.popitem(), ("b", 2))
1395 self.failUnlessEqual(d.popitem(), ("a", 3))
1396 self.failUnlessRaises(KeyError, d.popitem)
1398 d = dictutil.ValueOrderedDict({"a": 3, "b": 2, "c": 1})
1399 x = d.pop("d", "default", False)
1400 self.failUnlessEqual(x, "default")
1401 self.failUnlessRaises(KeyError, d.pop, "d", "default", True)
1403 self.failUnlessEqual(x, 2)
1404 self.failUnlessEqual(d.items(), [("c", 1), ("a", 3)])
1406 d = dictutil.ValueOrderedDict({"a": 3, "b": 2, "c": 1})
1407 x = d.pop_from_list(1) # pop the second item, b/2
1408 self.failUnlessEqual(x, "b")
1409 self.failUnlessEqual(d.items(), [("c", 1), ("a", 3)])
1411 def test_auxdict(self):
1412 d = dictutil.AuxValueDict()
1413 # we put the serialized form in the auxdata
1414 d.set_with_aux("key", ("filecap", "metadata"), "serialized")
1416 self.failUnlessEqual(d.keys(), ["key"])
1417 self.failUnlessEqual(d["key"], ("filecap", "metadata"))
1418 self.failUnlessEqual(d.get_aux("key"), "serialized")
1419 def _get_missing(key):
1421 self.failUnlessRaises(KeyError, _get_missing, "nonkey")
1422 self.failUnlessEqual(d.get("nonkey"), None)
1423 self.failUnlessEqual(d.get("nonkey", "nonvalue"), "nonvalue")
1424 self.failUnlessEqual(d.get_aux("nonkey"), None)
1425 self.failUnlessEqual(d.get_aux("nonkey", "nonvalue"), "nonvalue")
1427 d["key"] = ("filecap2", "metadata2")
1428 self.failUnlessEqual(d["key"], ("filecap2", "metadata2"))
1429 self.failUnlessEqual(d.get_aux("key"), None)
1431 d.set_with_aux("key2", "value2", "aux2")
1432 self.failUnlessEqual(sorted(d.keys()), ["key", "key2"])
1434 self.failUnlessEqual(d.keys(), ["key"])
1435 self.failIf("key2" in d)
1436 self.failUnlessRaises(KeyError, _get_missing, "key2")
1437 self.failUnlessEqual(d.get("key2"), None)
1438 self.failUnlessEqual(d.get_aux("key2"), None)
1439 d["key2"] = "newvalue2"
1440 self.failUnlessEqual(d.get("key2"), "newvalue2")
1441 self.failUnlessEqual(d.get_aux("key2"), None)
1443 d = dictutil.AuxValueDict({1:2,3:4})
1444 self.failUnlessEqual(sorted(d.keys()), [1,3])
1445 self.failUnlessEqual(d[1], 2)
1446 self.failUnlessEqual(d.get_aux(1), None)
1448 d = dictutil.AuxValueDict([ (1,2), (3,4) ])
1449 self.failUnlessEqual(sorted(d.keys()), [1,3])
1450 self.failUnlessEqual(d[1], 2)
1451 self.failUnlessEqual(d.get_aux(1), None)
1453 d = dictutil.AuxValueDict(one=1, two=2)
1454 self.failUnlessEqual(sorted(d.keys()), ["one","two"])
1455 self.failUnlessEqual(d["one"], 1)
1456 self.failUnlessEqual(d.get_aux("one"), None)
1458 class Pipeline(unittest.TestCase):
1459 def pause(self, *args, **kwargs):
1460 d = defer.Deferred()
1461 self.calls.append( (d, args, kwargs) )
1464 def failUnlessCallsAre(self, expected):
1467 self.failUnlessEqual(len(self.calls), len(expected), self.calls)
1468 for i,c in enumerate(self.calls):
1469 self.failUnlessEqual(c[1:], expected[i], str(i))
1471 def test_basic(self):
1474 p = pipeline.Pipeline(100)
1476 d = p.flush() # fires immediately
1477 d.addCallbacks(finished.append, log.err)
1478 self.failUnlessEqual(len(finished), 1)
1481 d = p.add(10, self.pause, "one")
1482 # the call should start right away, and our return Deferred should
1484 d.addCallbacks(finished.append, log.err)
1485 self.failUnlessEqual(len(finished), 1)
1486 self.failUnlessEqual(finished[0], None)
1487 self.failUnlessCallsAre([ ( ("one",) , {} ) ])
1488 self.failUnlessEqual(p.gauge, 10)
1493 d = p.add(20, self.pause, "two", kw=2)
1494 # pipeline: [one, two]
1496 # the call and the Deferred should fire right away
1497 d.addCallbacks(finished.append, log.err)
1498 self.failUnlessEqual(len(finished), 1)
1499 self.failUnlessEqual(finished[0], None)
1500 self.failUnlessCallsAre([ ( ("one",) , {} ),
1501 ( ("two",) , {"kw": 2} ),
1503 self.failUnlessEqual(p.gauge, 30)
1505 self.calls[0][0].callback("one-result")
1507 self.failUnlessEqual(p.gauge, 20)
1510 d = p.add(90, self.pause, "three", "posarg1")
1511 # pipeline: [two, three]
1514 fd.addCallbacks(flushed.append, log.err)
1515 self.failUnlessEqual(flushed, [])
1517 # the call will be made right away, but the return Deferred will not,
1518 # because the pipeline is now full.
1519 d.addCallbacks(finished.append, log.err)
1520 self.failUnlessEqual(len(finished), 0)
1521 self.failUnlessCallsAre([ ( ("one",) , {} ),
1522 ( ("two",) , {"kw": 2} ),
1523 ( ("three", "posarg1"), {} ),
1525 self.failUnlessEqual(p.gauge, 110)
1527 self.failUnlessRaises(pipeline.SingleFileError, p.add, 10, self.pause)
1529 # retiring either call will unblock the pipeline, causing the #3
1531 self.calls[2][0].callback("three-result")
1534 self.failUnlessEqual(len(finished), 1)
1535 self.failUnlessEqual(finished[0], None)
1536 self.failUnlessEqual(flushed, [])
1538 # retiring call#2 will finally allow the flush() Deferred to fire
1539 self.calls[1][0].callback("two-result")
1540 self.failUnlessEqual(len(flushed), 1)
1542 def test_errors(self):
1544 p = pipeline.Pipeline(100)
1546 d1 = p.add(200, self.pause, "one")
1550 d1.addBoth(finished.append)
1551 self.failUnlessEqual(finished, [])
1554 d2.addBoth(flushed.append)
1555 self.failUnlessEqual(flushed, [])
1557 self.calls[0][0].errback(ValueError("oops"))
1559 self.failUnlessEqual(len(finished), 1)
1561 self.failUnless(isinstance(f, Failure))
1562 self.failUnless(f.check(pipeline.PipelineError))
1563 self.failUnlessIn("PipelineError", str(f.value))
1564 self.failUnlessIn("ValueError", str(f.value))
1566 self.failUnless("ValueError" in r, r)
1568 self.failUnless(f2.check(ValueError))
1570 self.failUnlessEqual(len(flushed), 1)
1572 self.failUnless(isinstance(f, Failure))
1573 self.failUnless(f.check(pipeline.PipelineError))
1575 self.failUnless(f2.check(ValueError))
1577 # now that the pipeline is in the failed state, any new calls will
1580 d3 = p.add(20, self.pause, "two")
1583 d3.addBoth(finished.append)
1584 self.failUnlessEqual(len(finished), 1)
1586 self.failUnless(isinstance(f, Failure))
1587 self.failUnless(f.check(pipeline.PipelineError))
1589 self.failUnless("ValueError" in r, r)
1591 self.failUnless(f2.check(ValueError))
1595 d4.addBoth(flushed.append)
1596 self.failUnlessEqual(len(flushed), 1)
1598 self.failUnless(isinstance(f, Failure))
1599 self.failUnless(f.check(pipeline.PipelineError))
1601 self.failUnless(f2.check(ValueError))
1603 def test_errors2(self):
1605 p = pipeline.Pipeline(100)
1607 d1 = p.add(10, self.pause, "one")
1608 d2 = p.add(20, self.pause, "two")
1609 d3 = p.add(30, self.pause, "three")
1612 # one call fails, then the second one succeeds: make sure
1613 # ExpandableDeferredList tolerates the second one
1616 d4.addBoth(flushed.append)
1617 self.failUnlessEqual(flushed, [])
1619 self.calls[0][0].errback(ValueError("oops"))
1620 self.failUnlessEqual(len(flushed), 1)
1622 self.failUnless(isinstance(f, Failure))
1623 self.failUnless(f.check(pipeline.PipelineError))
1625 self.failUnless(f2.check(ValueError))
1627 self.calls[1][0].callback("two-result")
1628 self.calls[2][0].errback(ValueError("three-error"))
1632 class SampleError(Exception):
1635 class Log(unittest.TestCase):
1637 if not hasattr(self, "flushLoggedErrors"):
1638 # without flushLoggedErrors, we can't get rid of the
1639 # twisted.log.err that tahoe_log records, so we can't keep this
1640 # test from [ERROR]ing
1641 raise unittest.SkipTest("needs flushLoggedErrors from Twisted-2.5.0")
1643 raise SampleError("simple sample")
1646 tahoe_log.err(format="intentional sample error",
1647 failure=f, level=tahoe_log.OPERATIONAL, umid="wO9UoQ")
1648 self.flushLoggedErrors(SampleError)
1652 # this is a simple+inefficient form of util.spans.Spans . We compare the
1653 # behavior of this reference model against the real (efficient) form.
1655 def __init__(self, _span_or_start=None, length=None):
1657 if length is not None:
1658 for i in range(_span_or_start, _span_or_start+length):
1660 elif _span_or_start:
1661 for (start,length) in _span_or_start:
1662 self.add(start, length)
1664 def add(self, start, length):
1665 for i in range(start, start+length):
1669 def remove(self, start, length):
1670 for i in range(start, start+length):
1671 self._have.discard(i)
1675 return sorted(self._have)
1678 items = sorted(self._have)
1682 if prevstart is None:
1683 prevstart = prevend = i
1688 yield (prevstart, prevend-prevstart+1)
1689 prevstart = prevend = i
1690 if prevstart is not None:
1691 yield (prevstart, prevend-prevstart+1)
1693 def __nonzero__(self): # this gets us bool()
1697 return len(self._have)
1699 def __add__(self, other):
1700 s = self.__class__(self)
1701 for (start, length) in other:
1702 s.add(start, length)
1705 def __sub__(self, other):
1706 s = self.__class__(self)
1707 for (start, length) in other:
1708 s.remove(start, length)
1711 def __iadd__(self, other):
1712 for (start, length) in other:
1713 self.add(start, length)
1716 def __isub__(self, other):
1717 for (start, length) in other:
1718 self.remove(start, length)
1721 def __and__(self, other):
1722 s = self.__class__()
1723 for i in other.each():
1728 def __contains__(self, (start,length)):
1729 for i in range(start, start+length):
1730 if i not in self._have:
1734 class ByteSpans(unittest.TestCase):
1735 def test_basic(self):
1737 self.failUnlessEqual(list(s), [])
1739 self.failIf((0,1) in s)
1740 self.failUnlessEqual(s.len(), 0)
1742 s1 = Spans(3, 4) # 3,4,5,6
1745 s1 = Spans(3L, 4L) # 3,4,5,6
1751 s2.add(10,2) # 10,11
1753 self.failUnless((10,1) in s2)
1754 self.failIf((10,1) in s1)
1755 self.failUnlessEqual(list(s2.each()), [3,4,5,6,10,11])
1756 self.failUnlessEqual(s2.len(), 6)
1758 s2.add(15,2).add(20,2)
1759 self.failUnlessEqual(list(s2.each()), [3,4,5,6,10,11,15,16,20,21])
1760 self.failUnlessEqual(s2.len(), 10)
1762 s2.remove(4,3).remove(15,1)
1763 self.failUnlessEqual(list(s2.each()), [3,10,11,16,20,21])
1764 self.failUnlessEqual(s2.len(), 6)
1766 s1 = SimpleSpans(3, 4) # 3 4 5 6
1767 s2 = SimpleSpans(5, 4) # 5 6 7 8
1769 self.failUnlessEqual(list(i.each()), [5, 6])
1771 def _check1(self, s):
1772 self.failUnlessEqual(list(s), [(3,4)])
1774 self.failUnlessEqual(s.len(), 4)
1775 self.failIf((0,1) in s)
1776 self.failUnless((3,4) in s)
1777 self.failUnless((3,1) in s)
1778 self.failUnless((5,2) in s)
1779 self.failUnless((6,1) in s)
1780 self.failIf((6,2) in s)
1781 self.failIf((7,1) in s)
1782 self.failUnlessEqual(list(s.each()), [3,4,5,6])
1784 def test_large(self):
1785 s = Spans(4, 2**65) # don't do this with a SimpleSpans
1786 self.failUnlessEqual(list(s), [(4, 2**65)])
1788 self.failUnlessEqual(s.len(), 2**65)
1789 self.failIf((0,1) in s)
1790 self.failUnless((4,2) in s)
1791 self.failUnless((2**65,2) in s)
1793 def test_math(self):
1794 s1 = Spans(0, 10) # 0,1,2,3,4,5,6,7,8,9
1795 s2 = Spans(5, 3) # 5,6,7
1796 s3 = Spans(8, 4) # 8,9,10,11
1799 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,8,9])
1801 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7])
1803 self.failUnlessEqual(list(s.each()), [5,6,7])
1805 self.failUnlessEqual(list(s.each()), [5,6,7])
1807 self.failUnlessEqual(list(s.each()), [5,6,7])
1809 self.failUnlessEqual(list(s.each()), [8,9])
1811 self.failUnlessEqual(list(s.each()), [8,9])
1813 self.failUnlessEqual(list(s.each()), [])
1815 self.failUnlessEqual(list(s.each()), [])
1817 self.failUnlessEqual(list(s.each()), [])
1819 self.failUnlessEqual(list(s.each()), [])
1822 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7,8,9])
1824 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7,8,9,10,11])
1826 self.failUnlessEqual(list(s.each()), [5,6,7,8,9,10,11])
1830 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,8,9])
1833 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7])
1836 self.failUnlessEqual(list(s.each()), [5,6,7])
1840 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7,8,9])
1843 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7,8,9,10,11])
1846 self.failUnlessEqual(list(s.each()), [5,6,7,8,9,10,11])
1848 def test_random(self):
1849 # attempt to increase coverage of corner cases by comparing behavior
1850 # of a simple-but-slow model implementation against the
1851 # complex-but-fast actual implementation, in a large number of random
1855 s1 = S1(); s2 = S2()
1857 def _create(subseed):
1858 ns1 = S1(); ns2 = S2()
1860 what = _hash(subseed+str(i)).hexdigest()
1861 start = int(what[2:4], 16)
1862 length = max(1,int(what[5:6], 16))
1863 ns1.add(start, length); ns2.add(start, length)
1867 for i in range(1000):
1868 what = _hash(seed+str(i)).hexdigest()
1871 start = int(what[2:4], 16)
1872 length = max(1,int(what[5:6], 16))
1875 if subop in "01234":
1876 s1 = S1(); s2 = S2()
1877 elif subop in "5678":
1878 s1 = S1(start, length); s2 = S2(start, length)
1880 s1 = S1(s1); s2 = S2(s2)
1881 #print "s2 = %s" % s2.dump()
1883 #print "s2.add(%d,%d)" % (start, length)
1884 s1.add(start, length); s2.add(start, length)
1886 #print "s2.remove(%d,%d)" % (start, length)
1887 s1.remove(start, length); s2.remove(start, length)
1889 ns1, ns2 = _create(what[7:11])
1890 #print "s2 + %s" % ns2.dump()
1891 s1 = s1 + ns1; s2 = s2 + ns2
1893 ns1, ns2 = _create(what[7:11])
1894 #print "%s - %s" % (s2.dump(), ns2.dump())
1895 s1 = s1 - ns1; s2 = s2 - ns2
1897 ns1, ns2 = _create(what[7:11])
1898 #print "s2 += %s" % ns2.dump()
1899 s1 += ns1; s2 += ns2
1901 ns1, ns2 = _create(what[7:11])
1902 #print "%s -= %s" % (s2.dump(), ns2.dump())
1903 s1 -= ns1; s2 -= ns2
1905 ns1, ns2 = _create(what[7:11])
1906 #print "%s &= %s" % (s2.dump(), ns2.dump())
1907 s1 = s1 & ns1; s2 = s2 & ns2
1908 #print "s2 now %s" % s2.dump()
1909 self.failUnlessEqual(list(s1.each()), list(s2.each()))
1910 self.failUnlessEqual(s1.len(), s2.len())
1911 self.failUnlessEqual(bool(s1), bool(s2))
1912 self.failUnlessEqual(list(s1), list(s2))
1914 what = _hash(what[12:14]+str(j)).hexdigest()
1915 start = int(what[2:4], 16)
1916 length = max(1, int(what[5:6], 16))
1917 span = (start, length)
1918 self.failUnlessEqual(bool(span in s1), bool(span in s2))
1924 # s.add(start,length) : returns s
1925 # s.remove(start,length)
1926 # s.each() -> list of byte offsets, mostly for testing
1927 # list(s) -> list of (start,length) tuples, one per span
1928 # (start,length) in s -> True if (start..start+length-1) are all members
1929 # NOT equivalent to x in list(s)
1930 # s.len() -> number of bytes, for testing, bool(), and accounting/limiting
1931 # bool(s) (__nonzeron__)
1932 # s = s1+s2, s1-s2, +=s1, -=s1
1934 def test_overlap(self):
1939 self._test_overlap(a,b,c,d)
1941 def _test_overlap(self, a, b, c, d):
1942 s1 = set(range(a,a+b))
1943 s2 = set(range(c,c+d))
1945 #self._show_overlap(s1, "1")
1946 #self._show_overlap(s2, "2")
1947 o = overlap(a,b,c,d)
1948 expected = s1.intersection(s2)
1950 self.failUnlessEqual(o, None)
1953 so = set(range(start,start+length))
1954 #self._show(so, "o")
1955 self.failUnlessEqual(so, expected)
1957 def _show_overlap(self, s, c):
1961 for i in range(max(s)):
1968 def extend(s, start, length, fill):
1969 if len(s) >= start+length:
1971 assert len(fill) == 1
1972 return s + fill*(start+length-len(s))
1974 def replace(s, start, data):
1975 assert len(s) >= start+len(data)
1976 return s[:start] + data + s[start+len(data):]
1978 class SimpleDataSpans:
1979 def __init__(self, other=None):
1980 self.missing = "" # "1" where missing, "0" where found
1983 for (start, data) in other.get_chunks():
1984 self.add(start, data)
1986 def __nonzero__(self): # this gets us bool()
1989 return len(self.missing.replace("1", ""))
1991 return [i for (i,c) in enumerate(self.missing) if c == "0"]
1992 def _have(self, start, length):
1993 m = self.missing[start:start+length]
1994 if not m or len(m)<length or int(m):
1997 def get_chunks(self):
1998 for i in self._dump():
1999 yield (i, self.data[i])
2000 def get_spans(self):
2001 return SimpleSpans([(start,len(data))
2002 for (start,data) in self.get_chunks()])
2003 def get(self, start, length):
2004 if self._have(start, length):
2005 return self.data[start:start+length]
2007 def pop(self, start, length):
2008 data = self.get(start, length)
2010 self.remove(start, length)
2012 def remove(self, start, length):
2013 self.missing = replace(extend(self.missing, start, length, "1"),
2015 def add(self, start, data):
2016 self.missing = replace(extend(self.missing, start, len(data), "1"),
2017 start, "0"*len(data))
2018 self.data = replace(extend(self.data, start, len(data), " "),
2022 class StringSpans(unittest.TestCase):
2023 def do_basic(self, klass):
2025 self.failUnlessEqual(ds.len(), 0)
2026 self.failUnlessEqual(list(ds._dump()), [])
2027 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 0)
2029 self.failUnlessEqual(ds.get(0, 4), None)
2030 self.failUnlessEqual(ds.pop(0, 4), None)
2034 self.failUnlessEqual(ds.len(), 4)
2035 self.failUnlessEqual(list(ds._dump()), [2,3,4,5])
2036 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 4)
2038 self.failUnless((2,2) in s1)
2039 self.failUnlessEqual(ds.get(0, 4), None)
2040 self.failUnlessEqual(ds.pop(0, 4), None)
2041 self.failUnlessEqual(ds.get(4, 4), None)
2044 self.failUnlessEqual(ds2.len(), 4)
2045 self.failUnlessEqual(list(ds2._dump()), [2,3,4,5])
2046 self.failUnlessEqual(sum([len(d) for (s,d) in ds2.get_chunks()]), 4)
2047 self.failUnlessEqual(ds2.get(0, 4), None)
2048 self.failUnlessEqual(ds2.pop(0, 4), None)
2049 self.failUnlessEqual(ds2.pop(2, 3), "fou")
2050 self.failUnlessEqual(sum([len(d) for (s,d) in ds2.get_chunks()]), 1)
2051 self.failUnlessEqual(ds2.get(2, 3), None)
2052 self.failUnlessEqual(ds2.get(5, 1), "r")
2053 self.failUnlessEqual(ds.get(2, 3), "fou")
2054 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 4)
2057 self.failUnlessEqual(ds.len(), 6)
2058 self.failUnlessEqual(list(ds._dump()), [0,1,2,3,4,5])
2059 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 6)
2060 self.failUnlessEqual(ds.get(0, 4), "23fo")
2061 self.failUnlessEqual(ds.pop(0, 4), "23fo")
2062 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 2)
2063 self.failUnlessEqual(ds.get(0, 4), None)
2064 self.failUnlessEqual(ds.pop(0, 4), None)
2069 self.failUnlessEqual(ds.get(2, 4), "fear")
2074 self.failUnlessEqual(ds.get(2L, 4L), "fear")
2077 def do_scan(self, klass):
2078 # do a test with gaps and spans of size 1 and 2
2079 # left=(1,11) * right=(1,11) * gapsize=(1,2)
2080 # 111, 112, 121, 122, 211, 212, 221, 222
2089 # 11 1 1 11 11 11 1 1 111
2090 # 0123456789012345678901234567
2091 # abcdefghijklmnopqrstuvwxyz-=
2092 pieces = [(1, "bc"),
2102 p_elements = set([1,2,4,7,9,10,12,13,16,17,20,22,25,26,27])
2103 S = "abcdefghijklmnopqrstuvwxyz-="
2104 # TODO: when adding data, add capital letters, to make sure we aren't
2105 # just leaving the old data in place
2109 for start, data in pieces:
2114 d = "".join([((i not in p) and " " or S[i]) for i in range(l)])
2118 for start in range(0, l):
2119 for end in range(start+1, l):
2120 # add [start-end) to the baseline
2121 which = "%d-%d" % (start, end-1)
2122 p_added = set(range(start, end))
2126 print dump(b), which
2127 add = klass(); add.add(start, S[start:end])
2129 b.add(start, S[start:end])
2132 # check that the new span is there
2133 d = b.get(start, end-start)
2134 self.failUnlessEqual(d, S[start:end], which)
2135 # check that all the original pieces are still there
2136 for t_start, t_data in pieces:
2138 self.failUnlessEqual(b.get(t_start, t_len),
2139 S[t_start:t_start+t_len],
2140 "%s %d+%d" % (which, t_start, t_len))
2141 # check that a lot of subspans are mostly correct
2142 for t_start in range(l):
2143 for t_len in range(1,4):
2144 d = b.get(t_start, t_len)
2146 which2 = "%s+(%d-%d)" % (which, t_start,
2148 self.failUnlessEqual(d, S[t_start:t_start+t_len],
2150 # check that removing a subspan gives the right value
2152 b2.remove(t_start, t_len)
2153 removed = set(range(t_start, t_start+t_len))
2155 exp = (((i in p_elements) or (i in p_added))
2156 and (i not in removed))
2157 which2 = "%s-(%d-%d)" % (which, t_start,
2159 self.failUnlessEqual(bool(b2.get(i, 1)), exp,
2162 def test_test(self):
2163 self.do_basic(SimpleDataSpans)
2164 self.do_scan(SimpleDataSpans)
2166 def test_basic(self):
2167 self.do_basic(DataSpans)
2168 self.do_scan(DataSpans)
2170 def test_random(self):
2171 # attempt to increase coverage of corner cases by comparing behavior
2172 # of a simple-but-slow model implementation against the
2173 # complex-but-fast actual implementation, in a large number of random
2175 S1 = SimpleDataSpans
2177 s1 = S1(); s2 = S2()
2179 def _randstr(length, seed):
2182 while created < length:
2183 piece = _hash(seed + str(created)).hexdigest()
2184 pieces.append(piece)
2185 created += len(piece)
2186 return "".join(pieces)[:length]
2187 def _create(subseed):
2188 ns1 = S1(); ns2 = S2()
2190 what = _hash(subseed+str(i)).hexdigest()
2191 start = int(what[2:4], 16)
2192 length = max(1,int(what[5:6], 16))
2193 ns1.add(start, _randstr(length, what[7:9]));
2194 ns2.add(start, _randstr(length, what[7:9]))
2198 for i in range(1000):
2199 what = _hash(seed+str(i)).hexdigest()
2202 start = int(what[2:4], 16)
2203 length = max(1,int(what[5:6], 16))
2206 if subop in "0123456":
2207 s1 = S1(); s2 = S2()
2209 s1, s2 = _create(what[7:11])
2210 #print "s2 = %s" % list(s2._dump())
2211 elif op in "123456":
2212 #print "s2.add(%d,%d)" % (start, length)
2213 s1.add(start, _randstr(length, what[7:9]));
2214 s2.add(start, _randstr(length, what[7:9]))
2215 elif op in "789abc":
2216 #print "s2.remove(%d,%d)" % (start, length)
2217 s1.remove(start, length); s2.remove(start, length)
2219 #print "s2.pop(%d,%d)" % (start, length)
2220 d1 = s1.pop(start, length); d2 = s2.pop(start, length)
2221 self.failUnlessEqual(d1, d2)
2222 #print "s1 now %s" % list(s1._dump())
2223 #print "s2 now %s" % list(s2._dump())
2224 self.failUnlessEqual(s1.len(), s2.len())
2225 self.failUnlessEqual(list(s1._dump()), list(s2._dump()))
2226 for j in range(100):
2227 what = _hash(what[12:14]+str(j)).hexdigest()
2228 start = int(what[2:4], 16)
2229 length = max(1, int(what[5:6], 16))
2230 d1 = s1.get(start, length); d2 = s2.get(start, length)
2231 self.failUnlessEqual(d1, d2, "%d+%d" % (start, length))