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_rename(self):
435 basedir = "util/FileUtil/test_rename"
436 fileutil.make_dirs(basedir)
437 self.touch(basedir, "here")
438 fn = os.path.join(basedir, "here")
439 fn2 = os.path.join(basedir, "there")
440 fileutil.rename(fn, fn2)
441 self.failIf(os.path.exists(fn))
442 self.failUnless(os.path.exists(fn2))
445 basedir = "util/FileUtil/test_du"
446 fileutil.make_dirs(basedir)
447 d = os.path.join(basedir, "space-consuming")
449 self.touch(d, "a/b/1.txt", data="a"*10)
450 self.touch(d, "a/b/2.txt", data="b"*11)
452 self.touch(d, "a/c/1.txt", data="c"*12)
453 self.touch(d, "a/c/2.txt", data="d"*13)
455 used = fileutil.du(basedir)
456 self.failUnlessEqual(10+11+12+13, used)
458 def test_abspath_expanduser_unicode(self):
459 self.failUnlessRaises(AssertionError, fileutil.abspath_expanduser_unicode, "bytestring")
461 saved_cwd = os.path.normpath(os.getcwdu())
462 abspath_cwd = fileutil.abspath_expanduser_unicode(u".")
463 abspath_cwd_notlong = fileutil.abspath_expanduser_unicode(u".", long_path=False)
464 self.failUnless(isinstance(saved_cwd, unicode), saved_cwd)
465 self.failUnless(isinstance(abspath_cwd, unicode), abspath_cwd)
466 if sys.platform == "win32":
467 self.failUnlessReallyEqual(abspath_cwd, fileutil.to_windows_long_path(saved_cwd))
469 self.failUnlessReallyEqual(abspath_cwd, saved_cwd)
470 self.failUnlessReallyEqual(abspath_cwd_notlong, saved_cwd)
472 self.failUnlessReallyEqual(fileutil.to_windows_long_path(u"\\\\?\\foo"), u"\\\\?\\foo")
473 self.failUnlessReallyEqual(fileutil.to_windows_long_path(u"\\\\.\\foo"), u"\\\\.\\foo")
474 self.failUnlessReallyEqual(fileutil.to_windows_long_path(u"\\\\server\\foo"), u"\\\\?\\UNC\\server\\foo")
475 self.failUnlessReallyEqual(fileutil.to_windows_long_path(u"C:\\foo"), u"\\\\?\\C:\\foo")
476 self.failUnlessReallyEqual(fileutil.to_windows_long_path(u"C:\\foo/bar"), u"\\\\?\\C:\\foo\\bar")
478 # adapted from <http://svn.python.org/view/python/branches/release26-maint/Lib/test/test_posixpath.py?view=markup&pathrev=78279#test_abspath>
480 foo = fileutil.abspath_expanduser_unicode(u"foo")
481 self.failUnless(foo.endswith(u"%sfoo" % (os.path.sep,)), foo)
483 foobar = fileutil.abspath_expanduser_unicode(u"bar", base=foo)
484 self.failUnless(foobar.endswith(u"%sfoo%sbar" % (os.path.sep, os.path.sep)), foobar)
486 if sys.platform == "win32":
487 # This is checking that a drive letter is added for a path without one.
488 baz = fileutil.abspath_expanduser_unicode(u"\\baz")
489 self.failUnless(baz.startswith(u"\\\\?\\"), baz)
490 self.failUnlessReallyEqual(baz[5 :], u":\\baz")
492 bar = fileutil.abspath_expanduser_unicode(u"\\bar", base=baz)
493 self.failUnless(bar.startswith(u"\\\\?\\"), bar)
494 self.failUnlessReallyEqual(bar[5 :], u":\\bar")
495 # not u":\\baz\\bar", because \bar is absolute on the current drive.
497 self.failUnlessReallyEqual(baz[4], bar[4]) # same drive
499 baz_notlong = fileutil.abspath_expanduser_unicode(u"\\baz", long_path=False)
500 self.failIf(baz_notlong.startswith(u"\\\\?\\"), baz_notlong)
501 self.failUnlessReallyEqual(baz_notlong[1 :], u":\\baz")
503 bar_notlong = fileutil.abspath_expanduser_unicode(u"\\bar", base=baz_notlong, long_path=False)
504 self.failIf(bar_notlong.startswith(u"\\\\?\\"), bar_notlong)
505 self.failUnlessReallyEqual(bar_notlong[1 :], u":\\bar")
506 # not u":\\baz\\bar", because \bar is absolute on the current drive.
508 self.failUnlessReallyEqual(baz_notlong[0], bar_notlong[0]) # same drive
510 self.failIfIn(u"~", fileutil.abspath_expanduser_unicode(u"~"))
511 self.failIfIn(u"~", fileutil.abspath_expanduser_unicode(u"~", long_path=False))
515 cwds.append(u'\xe7w\xf0'.encode(sys.getfilesystemencoding()
517 except UnicodeEncodeError:
518 pass # the cwd can't be encoded -- test with ascii cwd only
524 for upath in (u'', u'fuu', u'f\xf9\xf9', u'/fuu', u'U:\\', u'~'):
525 uabspath = fileutil.abspath_expanduser_unicode(upath)
526 self.failUnless(isinstance(uabspath, unicode), uabspath)
528 uabspath_notlong = fileutil.abspath_expanduser_unicode(upath, long_path=False)
529 self.failUnless(isinstance(uabspath_notlong, unicode), uabspath_notlong)
533 def test_create_long_path(self):
534 workdir = u"test_create_long_path"
535 fileutil.make_dirs(workdir)
536 long_path = fileutil.abspath_expanduser_unicode(os.path.join(workdir, u'x'*255))
538 fileutil.remove(long_path)
539 self.addCleanup(_cleanup)
541 fileutil.write(long_path, "test")
542 self.failUnless(os.path.exists(long_path))
543 self.failUnlessEqual(fileutil.read(long_path), "test")
545 self.failIf(os.path.exists(long_path))
547 def _test_windows_expanduser(self, userprofile=None, homedrive=None, homepath=None):
548 def call_windows_getenv(name):
549 if name == u"USERPROFILE": return userprofile
550 if name == u"HOMEDRIVE": return homedrive
551 if name == u"HOMEPATH": return homepath
552 self.fail("unexpected argument to call_windows_getenv")
553 self.patch(fileutil, 'windows_getenv', call_windows_getenv)
555 self.failUnlessReallyEqual(fileutil.windows_expanduser(u"~"), os.path.join(u"C:", u"\\Documents and Settings\\\u0100"))
556 self.failUnlessReallyEqual(fileutil.windows_expanduser(u"~\\foo"), os.path.join(u"C:", u"\\Documents and Settings\\\u0100", u"foo"))
557 self.failUnlessReallyEqual(fileutil.windows_expanduser(u"~/foo"), os.path.join(u"C:", u"\\Documents and Settings\\\u0100", u"foo"))
558 self.failUnlessReallyEqual(fileutil.windows_expanduser(u"a"), u"a")
559 self.failUnlessReallyEqual(fileutil.windows_expanduser(u"a~"), u"a~")
560 self.failUnlessReallyEqual(fileutil.windows_expanduser(u"a\\~\\foo"), u"a\\~\\foo")
562 def test_windows_expanduser_xp(self):
563 return self._test_windows_expanduser(homedrive=u"C:", homepath=u"\\Documents and Settings\\\u0100")
565 def test_windows_expanduser_win7(self):
566 return self._test_windows_expanduser(userprofile=os.path.join(u"C:", u"\\Documents and Settings\\\u0100"))
568 def test_disk_stats(self):
569 avail = fileutil.get_available_space('.', 2**14)
571 raise unittest.SkipTest("This test will spuriously fail there is no disk space left.")
573 disk = fileutil.get_disk_stats('.', 2**13)
574 self.failUnless(disk['total'] > 0, disk['total'])
575 # we tolerate used==0 for a Travis-CI bug, see #2290
576 self.failUnless(disk['used'] >= 0, disk['used'])
577 self.failUnless(disk['free_for_root'] > 0, disk['free_for_root'])
578 self.failUnless(disk['free_for_nonroot'] > 0, disk['free_for_nonroot'])
579 self.failUnless(disk['avail'] > 0, disk['avail'])
581 def test_disk_stats_avail_nonnegative(self):
582 # This test will spuriously fail if you have more than 2^128
583 # bytes of available space on your filesystem.
584 disk = fileutil.get_disk_stats('.', 2**128)
585 self.failUnlessEqual(disk['avail'], 0)
587 class PollMixinTests(unittest.TestCase):
589 self.pm = pollmixin.PollMixin()
591 def test_PollMixin_True(self):
592 d = self.pm.poll(check_f=lambda : True,
596 def test_PollMixin_False_then_True(self):
597 i = iter([False, True])
598 d = self.pm.poll(check_f=i.next,
602 def test_timeout(self):
603 d = self.pm.poll(check_f=lambda: False,
607 self.fail("poll should have failed, not returned %s" % (res,))
609 f.trap(pollmixin.TimeoutError)
610 return None # success
611 d.addCallbacks(_suc, _err)
614 class DeferredUtilTests(unittest.TestCase, deferredutil.WaitForDelayedCallsMixin):
615 def test_gather_results(self):
616 d1 = defer.Deferred()
617 d2 = defer.Deferred()
618 res = deferredutil.gatherResults([d1, d2])
619 d1.errback(ValueError("BAD"))
621 self.fail("Should have errbacked, not resulted in %s" % (res,))
623 thef.trap(ValueError)
624 res.addCallbacks(_callb, _errb)
627 def test_success(self):
628 d1, d2 = defer.Deferred(), defer.Deferred()
631 dlss = deferredutil.DeferredListShouldSucceed([d1,d2])
632 dlss.addCallbacks(good.append, bad.append)
635 self.failUnlessEqual(good, [[1,2]])
636 self.failUnlessEqual(bad, [])
638 def test_failure(self):
639 d1, d2 = defer.Deferred(), defer.Deferred()
642 dlss = deferredutil.DeferredListShouldSucceed([d1,d2])
643 dlss.addCallbacks(good.append, bad.append)
644 d1.addErrback(lambda _ignore: None)
645 d2.addErrback(lambda _ignore: None)
647 d2.errback(ValueError())
648 self.failUnlessEqual(good, [])
649 self.failUnlessEqual(len(bad), 1)
651 self.failUnless(isinstance(f, Failure))
652 self.failUnless(f.check(ValueError))
654 def test_wait_for_delayed_calls(self):
656 This tests that 'wait_for_delayed_calls' does in fact wait for a
657 delayed call that is active when the test returns. If it didn't,
658 Trial would report an unclean reactor error for this test.
663 reactor.callLater(0.1, _trigger)
665 d = defer.succeed(None)
666 d.addBoth(self.wait_for_delayed_calls)
669 class HashUtilTests(unittest.TestCase):
671 def test_random_key(self):
672 k = hashutil.random_key()
673 self.failUnlessEqual(len(k), hashutil.KEYLEN)
675 def test_sha256d(self):
676 h1 = hashutil.tagged_hash("tag1", "value")
677 h2 = hashutil.tagged_hasher("tag1")
681 self.failUnlessEqual(h1, h2a)
682 self.failUnlessEqual(h2a, h2b)
684 def test_sha256d_truncated(self):
685 h1 = hashutil.tagged_hash("tag1", "value", 16)
686 h2 = hashutil.tagged_hasher("tag1", 16)
689 self.failUnlessEqual(len(h1), 16)
690 self.failUnlessEqual(len(h2), 16)
691 self.failUnlessEqual(h1, h2)
694 h1 = hashutil.convergence_hash(3, 10, 1000, "data", "secret")
695 h2 = hashutil.convergence_hasher(3, 10, 1000, "secret")
698 self.failUnlessEqual(h1, h2)
700 def test_hashers(self):
701 h1 = hashutil.block_hash("foo")
702 h2 = hashutil.block_hasher()
704 self.failUnlessEqual(h1, h2.digest())
706 h1 = hashutil.uri_extension_hash("foo")
707 h2 = hashutil.uri_extension_hasher()
709 self.failUnlessEqual(h1, h2.digest())
711 h1 = hashutil.plaintext_hash("foo")
712 h2 = hashutil.plaintext_hasher()
714 self.failUnlessEqual(h1, h2.digest())
716 h1 = hashutil.crypttext_hash("foo")
717 h2 = hashutil.crypttext_hasher()
719 self.failUnlessEqual(h1, h2.digest())
721 h1 = hashutil.crypttext_segment_hash("foo")
722 h2 = hashutil.crypttext_segment_hasher()
724 self.failUnlessEqual(h1, h2.digest())
726 h1 = hashutil.plaintext_segment_hash("foo")
727 h2 = hashutil.plaintext_segment_hasher()
729 self.failUnlessEqual(h1, h2.digest())
731 def test_timing_safe_compare(self):
732 self.failUnless(hashutil.timing_safe_compare("a", "a"))
733 self.failUnless(hashutil.timing_safe_compare("ab", "ab"))
734 self.failIf(hashutil.timing_safe_compare("a", "b"))
735 self.failIf(hashutil.timing_safe_compare("a", "aa"))
737 def _testknown(self, hashf, expected_a, *args):
739 got_a = base32.b2a(got)
740 self.failUnlessEqual(got_a, expected_a)
742 def test_known_answers(self):
743 # assert backwards compatibility
744 self._testknown(hashutil.storage_index_hash, "qb5igbhcc5esa6lwqorsy7e6am", "")
745 self._testknown(hashutil.block_hash, "msjr5bh4evuh7fa3zw7uovixfbvlnstr5b65mrerwfnvjxig2jvq", "")
746 self._testknown(hashutil.uri_extension_hash, "wthsu45q7zewac2mnivoaa4ulh5xvbzdmsbuyztq2a5fzxdrnkka", "")
747 self._testknown(hashutil.plaintext_hash, "5lz5hwz3qj3af7n6e3arblw7xzutvnd3p3fjsngqjcb7utf3x3da", "")
748 self._testknown(hashutil.crypttext_hash, "itdj6e4njtkoiavlrmxkvpreosscssklunhwtvxn6ggho4rkqwga", "")
749 self._testknown(hashutil.crypttext_segment_hash, "aovy5aa7jej6ym5ikgwyoi4pxawnoj3wtaludjz7e2nb5xijb7aa", "")
750 self._testknown(hashutil.plaintext_segment_hash, "4fdgf6qruaisyukhqcmoth4t3li6bkolbxvjy4awwcpprdtva7za", "")
751 self._testknown(hashutil.convergence_hash, "3mo6ni7xweplycin6nowynw2we", 3, 10, 100, "", "converge")
752 self._testknown(hashutil.my_renewal_secret_hash, "ujhr5k5f7ypkp67jkpx6jl4p47pyta7hu5m527cpcgvkafsefm6q", "")
753 self._testknown(hashutil.my_cancel_secret_hash, "rjwzmafe2duixvqy6h47f5wfrokdziry6zhx4smew4cj6iocsfaa", "")
754 self._testknown(hashutil.file_renewal_secret_hash, "hzshk2kf33gzbd5n3a6eszkf6q6o6kixmnag25pniusyaulqjnia", "", "si")
755 self._testknown(hashutil.file_cancel_secret_hash, "bfciwvr6w7wcavsngxzxsxxaszj72dej54n4tu2idzp6b74g255q", "", "si")
756 self._testknown(hashutil.bucket_renewal_secret_hash, "e7imrzgzaoashsncacvy3oysdd2m5yvtooo4gmj4mjlopsazmvuq", "", "\x00"*20)
757 self._testknown(hashutil.bucket_cancel_secret_hash, "dvdujeyxeirj6uux6g7xcf4lvesk632aulwkzjar7srildvtqwma", "", "\x00"*20)
758 self._testknown(hashutil.hmac, "c54ypfi6pevb3nvo6ba42jtglpkry2kbdopqsi7dgrm4r7tw5sra", "tag", "")
759 self._testknown(hashutil.mutable_rwcap_key_hash, "6rvn2iqrghii5n4jbbwwqqsnqu", "iv", "wk")
760 self._testknown(hashutil.ssk_writekey_hash, "ykpgmdbpgbb6yqz5oluw2q26ye", "")
761 self._testknown(hashutil.ssk_write_enabler_master_hash, "izbfbfkoait4dummruol3gy2bnixrrrslgye6ycmkuyujnenzpia", "")
762 self._testknown(hashutil.ssk_write_enabler_hash, "fuu2dvx7g6gqu5x22vfhtyed7p4pd47y5hgxbqzgrlyvxoev62tq", "wk", "\x00"*20)
763 self._testknown(hashutil.ssk_pubkey_fingerprint_hash, "3opzw4hhm2sgncjx224qmt5ipqgagn7h5zivnfzqycvgqgmgz35q", "")
764 self._testknown(hashutil.ssk_readkey_hash, "vugid4as6qbqgeq2xczvvcedai", "")
765 self._testknown(hashutil.ssk_readkey_data_hash, "73wsaldnvdzqaf7v4pzbr2ae5a", "iv", "rk")
766 self._testknown(hashutil.ssk_storage_index_hash, "j7icz6kigb6hxrej3tv4z7ayym", "")
769 class Abbreviate(unittest.TestCase):
771 a = abbreviate.abbreviate_time
772 self.failUnlessEqual(a(None), "unknown")
773 self.failUnlessEqual(a(0), "0 seconds")
774 self.failUnlessEqual(a(1), "1 second")
775 self.failUnlessEqual(a(2), "2 seconds")
776 self.failUnlessEqual(a(119), "119 seconds")
778 self.failUnlessEqual(a(2*MIN), "2 minutes")
779 self.failUnlessEqual(a(60*MIN), "60 minutes")
780 self.failUnlessEqual(a(179*MIN), "179 minutes")
782 self.failUnlessEqual(a(180*MIN), "3 hours")
783 self.failUnlessEqual(a(4*HOUR), "4 hours")
786 self.failUnlessEqual(a(2*DAY), "2 days")
787 self.failUnlessEqual(a(2*MONTH), "2 months")
789 self.failUnlessEqual(a(5*YEAR), "5 years")
791 def test_space(self):
792 tests_si = [(None, "unknown"),
799 (20*1000, "20.00 kB"),
800 (1024*1024, "1.05 MB"),
801 (1000*1000, "1.00 MB"),
802 (1000*1000*1000, "1.00 GB"),
803 (1000*1000*1000*1000, "1.00 TB"),
804 (1000*1000*1000*1000*1000, "1.00 PB"),
805 (1000*1000*1000*1000*1000*1000, "1.00 EB"),
806 (1234567890123456789, "1.23 EB"),
808 for (x, expected) in tests_si:
809 got = abbreviate.abbreviate_space(x, SI=True)
810 self.failUnlessEqual(got, expected)
812 tests_base1024 = [(None, "unknown"),
819 (20*1024, "20.00 kiB"),
820 (1000*1000, "976.56 kiB"),
821 (1024*1024, "1.00 MiB"),
822 (1024*1024*1024, "1.00 GiB"),
823 (1024*1024*1024*1024, "1.00 TiB"),
824 (1000*1000*1000*1000*1000, "909.49 TiB"),
825 (1024*1024*1024*1024*1024, "1.00 PiB"),
826 (1024*1024*1024*1024*1024*1024, "1.00 EiB"),
827 (1234567890123456789, "1.07 EiB"),
829 for (x, expected) in tests_base1024:
830 got = abbreviate.abbreviate_space(x, SI=False)
831 self.failUnlessEqual(got, expected)
833 self.failUnlessEqual(abbreviate.abbreviate_space_both(1234567),
834 "(1.23 MB, 1.18 MiB)")
836 def test_parse_space(self):
837 p = abbreviate.parse_abbreviated_size
838 self.failUnlessEqual(p(""), None)
839 self.failUnlessEqual(p(None), None)
840 self.failUnlessEqual(p("123"), 123)
841 self.failUnlessEqual(p("123B"), 123)
842 self.failUnlessEqual(p("2K"), 2000)
843 self.failUnlessEqual(p("2kb"), 2000)
844 self.failUnlessEqual(p("2KiB"), 2048)
845 self.failUnlessEqual(p("10MB"), 10*1000*1000)
846 self.failUnlessEqual(p("10MiB"), 10*1024*1024)
847 self.failUnlessEqual(p("5G"), 5*1000*1000*1000)
848 self.failUnlessEqual(p("4GiB"), 4*1024*1024*1024)
849 self.failUnlessEqual(p("3TB"), 3*1000*1000*1000*1000)
850 self.failUnlessEqual(p("3TiB"), 3*1024*1024*1024*1024)
851 self.failUnlessEqual(p("6PB"), 6*1000*1000*1000*1000*1000)
852 self.failUnlessEqual(p("6PiB"), 6*1024*1024*1024*1024*1024)
853 self.failUnlessEqual(p("9EB"), 9*1000*1000*1000*1000*1000*1000)
854 self.failUnlessEqual(p("9EiB"), 9*1024*1024*1024*1024*1024*1024)
856 e = self.failUnlessRaises(ValueError, p, "12 cubits")
857 self.failUnlessIn("12 cubits", str(e))
858 e = self.failUnlessRaises(ValueError, p, "1 BB")
859 self.failUnlessIn("1 BB", str(e))
860 e = self.failUnlessRaises(ValueError, p, "fhtagn")
861 self.failUnlessIn("fhtagn", str(e))
863 class Limiter(unittest.TestCase):
864 timeout = 480 # This takes longer than 240 seconds on Francois's arm box.
866 def job(self, i, foo):
867 self.calls.append( (i, foo) )
868 self.simultaneous += 1
869 self.peak_simultaneous = max(self.simultaneous, self.peak_simultaneous)
872 self.simultaneous -= 1
873 d.callback("done %d" % i)
874 reactor.callLater(1.0, _done)
877 def bad_job(self, i, foo):
878 raise ValueError("bad_job %d" % i)
880 def test_limiter(self):
882 self.simultaneous = 0
883 self.peak_simultaneous = 0
884 l = limiter.ConcurrencyLimiter()
887 dl.append(l.add(self.job, i, foo=str(i)))
888 d = defer.DeferredList(dl, fireOnOneErrback=True)
890 self.failUnlessEqual(self.simultaneous, 0)
891 self.failUnless(self.peak_simultaneous <= 10)
892 self.failUnlessEqual(len(self.calls), 20)
894 self.failUnless( (i, str(i)) in self.calls)
898 def test_errors(self):
900 self.simultaneous = 0
901 self.peak_simultaneous = 0
902 l = limiter.ConcurrencyLimiter()
905 dl.append(l.add(self.job, i, foo=str(i)))
906 d2 = l.add(self.bad_job, 21, "21")
907 d = defer.DeferredList(dl, fireOnOneErrback=True)
910 for (success, result) in res:
911 self.failUnlessEqual(success, True)
912 results.append(result)
914 expected_results = ["done %d" % i for i in range(20)]
915 expected_results.sort()
916 self.failUnlessEqual(results, expected_results)
917 self.failUnless(self.peak_simultaneous <= 10)
918 self.failUnlessEqual(len(self.calls), 20)
920 self.failUnless( (i, str(i)) in self.calls)
922 self.fail("should have failed, not got %s" % (res,))
925 self.failUnless("bad_job 21" in str(f))
926 d2.addCallbacks(_good, _err)
928 d.addCallback(_most_done)
930 self.failUnlessEqual(self.simultaneous, 0)
931 self.failUnless(self.peak_simultaneous <= 10)
932 self.failUnlessEqual(len(self.calls), 20)
934 self.failUnless( (i, str(i)) in self.calls)
935 d.addCallback(_all_done)
938 class TimeFormat(unittest.TestCase):
939 def test_epoch(self):
940 return self._help_test_epoch()
942 def test_epoch_in_London(self):
943 # Europe/London is a particularly troublesome timezone. Nowadays, its
944 # offset from GMT is 0. But in 1970, its offset from GMT was 1.
945 # (Apparently in 1970 Britain had redefined standard time to be GMT+1
946 # and stayed in standard time all year round, whereas today
947 # Europe/London standard time is GMT and Europe/London Daylight
948 # Savings Time is GMT+1.) The current implementation of
949 # time_format.iso_utc_time_to_localseconds() breaks if the timezone is
950 # Europe/London. (As soon as this unit test is done then I'll change
951 # that implementation to something that works even in this case...)
952 origtz = os.environ.get('TZ')
953 os.environ['TZ'] = "Europe/London"
954 if hasattr(time, 'tzset'):
957 return self._help_test_epoch()
962 os.environ['TZ'] = origtz
963 if hasattr(time, 'tzset'):
966 def _help_test_epoch(self):
967 origtzname = time.tzname
968 s = time_format.iso_utc_time_to_seconds("1970-01-01T00:00:01")
969 self.failUnlessEqual(s, 1.0)
970 s = time_format.iso_utc_time_to_seconds("1970-01-01_00:00:01")
971 self.failUnlessEqual(s, 1.0)
972 s = time_format.iso_utc_time_to_seconds("1970-01-01 00:00:01")
973 self.failUnlessEqual(s, 1.0)
975 self.failUnlessEqual(time_format.iso_utc(1.0), "1970-01-01_00:00:01")
976 self.failUnlessEqual(time_format.iso_utc(1.0, sep=" "),
977 "1970-01-01 00:00:01")
980 isostr = time_format.iso_utc(now)
981 timestamp = time_format.iso_utc_time_to_seconds(isostr)
982 self.failUnlessEqual(int(timestamp), int(now))
986 self.failUnlessEqual(time_format.iso_utc(t=my_time),
987 "1970-01-01_00:00:01")
988 e = self.failUnlessRaises(ValueError,
989 time_format.iso_utc_time_to_seconds,
990 "invalid timestring")
991 self.failUnless("not a complete ISO8601 timestamp" in str(e))
992 s = time_format.iso_utc_time_to_seconds("1970-01-01_00:00:01.500")
993 self.failUnlessEqual(s, 1.5)
995 # Look for daylight-savings-related errors.
996 thatmomentinmarch = time_format.iso_utc_time_to_seconds("2009-03-20 21:49:02.226536")
997 self.failUnlessEqual(thatmomentinmarch, 1237585742.226536)
998 self.failUnlessEqual(origtzname, time.tzname)
1000 def test_iso_utc(self):
1001 when = 1266760143.7841301
1002 out = time_format.iso_utc_date(when)
1003 self.failUnlessEqual(out, "2010-02-21")
1004 out = time_format.iso_utc_date(t=lambda: when)
1005 self.failUnlessEqual(out, "2010-02-21")
1006 out = time_format.iso_utc(when)
1007 self.failUnlessEqual(out, "2010-02-21_13:49:03.784130")
1008 out = time_format.iso_utc(when, sep="-")
1009 self.failUnlessEqual(out, "2010-02-21-13:49:03.784130")
1011 def test_parse_duration(self):
1012 p = time_format.parse_duration
1014 self.failUnlessEqual(p("1 day"), DAY)
1015 self.failUnlessEqual(p("2 days"), 2*DAY)
1016 self.failUnlessEqual(p("3 months"), 3*31*DAY)
1017 self.failUnlessEqual(p("4 mo"), 4*31*DAY)
1018 self.failUnlessEqual(p("5 years"), 5*365*DAY)
1019 e = self.failUnlessRaises(ValueError, p, "123")
1020 self.failUnlessIn("no unit (like day, month, or year) in '123'",
1023 def test_parse_date(self):
1024 self.failUnlessEqual(time_format.parse_date("2010-02-21"), 1266710400)
1026 class CacheDir(unittest.TestCase):
1027 def test_basic(self):
1028 basedir = "test_util/CacheDir/test_basic"
1030 def _failIfExists(name):
1031 absfn = os.path.join(basedir, name)
1032 self.failIf(os.path.exists(absfn),
1033 "%s exists but it shouldn't" % absfn)
1035 def _failUnlessExists(name):
1036 absfn = os.path.join(basedir, name)
1037 self.failUnless(os.path.exists(absfn),
1038 "%s doesn't exist but it should" % absfn)
1040 cdm = cachedir.CacheDirectoryManager(basedir)
1041 a = cdm.get_file("a")
1042 b = cdm.get_file("b")
1043 c = cdm.get_file("c")
1044 f = open(a.get_filename(), "wb"); f.write("hi"); f.close(); del f
1045 f = open(b.get_filename(), "wb"); f.write("hi"); f.close(); del f
1046 f = open(c.get_filename(), "wb"); f.write("hi"); f.close(); del f
1048 _failUnlessExists("a")
1049 _failUnlessExists("b")
1050 _failUnlessExists("c")
1054 _failUnlessExists("a")
1055 _failUnlessExists("b")
1056 _failUnlessExists("c")
1059 # this file won't be deleted yet, because it isn't old enough
1061 _failUnlessExists("a")
1062 _failUnlessExists("b")
1063 _failUnlessExists("c")
1065 # we change the definition of "old" to make everything old
1070 _failUnlessExists("b")
1071 _failUnlessExists("c")
1079 _failUnlessExists("b")
1080 _failUnlessExists("c")
1082 b2 = cdm.get_file("b")
1086 _failUnlessExists("b")
1087 _failUnlessExists("c")
1092 def __init__(self, x):
1097 return "<%s %s>" % (self.__class__.__name__, self.x,)
1100 def __le__(self, other):
1101 return self.x <= other
1102 def __lt__(self, other):
1103 return self.x < other
1104 def __ge__(self, other):
1105 return self.x >= other
1106 def __gt__(self, other):
1107 return self.x > other
1108 def __ne__(self, other):
1109 return self.x != other
1110 def __eq__(self, other):
1111 return self.x == other
1113 class DictUtil(unittest.TestCase):
1114 def _help_test_empty_dict(self, klass):
1118 self.failUnless(d1 == d2, "d1: %r, d2: %r" % (d1, d2,))
1119 self.failUnless(len(d1) == 0)
1120 self.failUnless(len(d2) == 0)
1122 def _help_test_nonempty_dict(self, klass):
1123 d1 = klass({'a': 1, 'b': "eggs", 3: "spam",})
1124 d2 = klass({'a': 1, 'b': "eggs", 3: "spam",})
1126 self.failUnless(d1 == d2)
1127 self.failUnless(len(d1) == 3, "%s, %s" % (len(d1), d1,))
1128 self.failUnless(len(d2) == 3)
1130 def _help_test_eq_but_notis(self, klass):
1131 d = klass({'a': 3, 'b': EqButNotIs(3), 'c': 3})
1136 d['b'] = EqButNotIs(3)
1141 d['b'] = EqButNotIs(3)
1147 d['a'] = EqButNotIs(3)
1152 fake3 = EqButNotIs(3)
1153 fake7 = EqButNotIs(7)
1157 self.failUnless(filter(lambda x: x is 8, d.itervalues()))
1158 self.failUnless(filter(lambda x: x is fake7, d.itervalues()))
1159 # The real 7 should have been ejected by the d[3] = 8.
1160 self.failUnless(not filter(lambda x: x is 7, d.itervalues()))
1161 self.failUnless(filter(lambda x: x is fake3, d.iterkeys()))
1162 self.failUnless(filter(lambda x: x is 3, d.iterkeys()))
1167 fake3 = EqButNotIs(3)
1168 fake7 = EqButNotIs(7)
1171 self.failUnless(filter(lambda x: x is 8, d.itervalues()))
1172 self.failUnless(filter(lambda x: x is fake7, d.itervalues()))
1173 # The real 7 should have been ejected by the d[3] = 8.
1174 self.failUnless(not filter(lambda x: x is 7, d.itervalues()))
1175 self.failUnless(filter(lambda x: x is fake3, d.iterkeys()))
1176 self.failUnless(filter(lambda x: x is 3, d.iterkeys()))
1180 self._help_test_eq_but_notis(dictutil.UtilDict)
1181 self._help_test_eq_but_notis(dictutil.NumDict)
1182 self._help_test_eq_but_notis(dictutil.ValueOrderedDict)
1183 self._help_test_nonempty_dict(dictutil.UtilDict)
1184 self._help_test_nonempty_dict(dictutil.NumDict)
1185 self._help_test_nonempty_dict(dictutil.ValueOrderedDict)
1186 self._help_test_eq_but_notis(dictutil.UtilDict)
1187 self._help_test_eq_but_notis(dictutil.NumDict)
1188 self._help_test_eq_but_notis(dictutil.ValueOrderedDict)
1190 def test_dict_of_sets(self):
1191 ds = dictutil.DictOfSets()
1196 self.failUnlessEqual(ds[1], set(["a"]))
1197 self.failUnlessEqual(ds[2], set(["b", "c"]))
1198 ds.discard(3, "d") # should not raise an exception
1200 self.failUnlessEqual(ds[2], set(["c"]))
1202 self.failIf(2 in ds)
1205 ds2 = dictutil.DictOfSets()
1210 self.failUnlessEqual(ds[1], set(["a"]))
1211 self.failUnlessEqual(ds[3], set(["f", "g"]))
1212 self.failUnlessEqual(ds[4], set(["h"]))
1214 def test_move(self):
1215 d1 = {1: "a", 2: "b"}
1216 d2 = {2: "c", 3: "d"}
1217 dictutil.move(1, d1, d2)
1218 self.failUnlessEqual(d1, {2: "b"})
1219 self.failUnlessEqual(d2, {1: "a", 2: "c", 3: "d"})
1221 d1 = {1: "a", 2: "b"}
1222 d2 = {2: "c", 3: "d"}
1223 dictutil.move(2, d1, d2)
1224 self.failUnlessEqual(d1, {1: "a"})
1225 self.failUnlessEqual(d2, {2: "b", 3: "d"})
1227 d1 = {1: "a", 2: "b"}
1228 d2 = {2: "c", 3: "d"}
1229 self.failUnlessRaises(KeyError, dictutil.move, 5, d1, d2, strict=True)
1231 def test_subtract(self):
1232 d1 = {1: "a", 2: "b"}
1233 d2 = {2: "c", 3: "d"}
1234 d3 = dictutil.subtract(d1, d2)
1235 self.failUnlessEqual(d3, {1: "a"})
1237 d1 = {1: "a", 2: "b"}
1239 d3 = dictutil.subtract(d1, d2)
1240 self.failUnlessEqual(d3, {1: "a"})
1242 def test_utildict(self):
1243 d = dictutil.UtilDict({1: "a", 2: "b"})
1246 self.failUnlessEqual(d, {2: "b"})
1249 self.failUnlessRaises(TypeError, eq, d, "not a dict")
1251 d = dictutil.UtilDict({1: "b", 2: "a"})
1252 self.failUnlessEqual(d.items_sorted_by_value(),
1253 [(2, "a"), (1, "b")])
1254 self.failUnlessEqual(d.items_sorted_by_key(),
1255 [(1, "b"), (2, "a")])
1256 self.failUnlessEqual(repr(d), "{1: 'b', 2: 'a'}")
1257 self.failUnless(1 in d)
1259 d2 = dictutil.UtilDict({3: "c", 4: "d"})
1260 self.failUnless(d != d2)
1261 self.failUnless(d2 > d)
1262 self.failUnless(d2 >= d)
1263 self.failUnless(d <= d2)
1264 self.failUnless(d < d2)
1265 self.failUnlessEqual(d[1], "b")
1266 self.failUnlessEqual(sorted(list([k for k in d])), [1,2])
1269 self.failUnlessEqual(d, d3)
1270 self.failUnless(isinstance(d3, dictutil.UtilDict))
1272 d4 = d.fromkeys([3,4], "e")
1273 self.failUnlessEqual(d4, {3: "e", 4: "e"})
1275 self.failUnlessEqual(d.get(1), "b")
1276 self.failUnlessEqual(d.get(3), None)
1277 self.failUnlessEqual(d.get(3, "default"), "default")
1278 self.failUnlessEqual(sorted(list(d.items())),
1279 [(1, "b"), (2, "a")])
1280 self.failUnlessEqual(sorted(list(d.iteritems())),
1281 [(1, "b"), (2, "a")])
1282 self.failUnlessEqual(sorted(d.keys()), [1, 2])
1283 self.failUnlessEqual(sorted(d.values()), ["a", "b"])
1284 x = d.setdefault(1, "new")
1285 self.failUnlessEqual(x, "b")
1286 self.failUnlessEqual(d[1], "b")
1287 x = d.setdefault(3, "new")
1288 self.failUnlessEqual(x, "new")
1289 self.failUnlessEqual(d[3], "new")
1293 self.failUnless(x in [(1, "b"), (2, "a")])
1295 self.failUnless(x in [(1, "b"), (2, "a")])
1296 self.failUnlessRaises(KeyError, d.popitem)
1298 def test_numdict(self):
1299 d = dictutil.NumDict({"a": 1, "b": 2})
1301 d.add_num("a", 10, 5)
1302 d.add_num("c", 20, 5)
1304 self.failUnlessEqual(d, {"a": 11, "b": 2, "c": 25, "d": 30})
1306 d.subtract_num("a", 10)
1307 d.subtract_num("e", 10)
1308 d.subtract_num("f", 10, 15)
1309 self.failUnlessEqual(d, {"a": 1, "b": 2, "c": 25, "d": 30,
1312 self.failUnlessEqual(d.sum(), sum([1, 2, 25, 30, -10, 5]))
1314 d = dictutil.NumDict()
1318 self.failUnlessEqual(d, {"a": 2, "b": 6})
1322 self.failUnlessEqual(d, {"a": 1, "b": 6, "c": -1, "d": 4})
1323 self.failUnlessEqual(d.items_sorted_by_key(),
1324 [("a", 1), ("b", 6), ("c", -1), ("d", 4)])
1325 self.failUnlessEqual(d.items_sorted_by_value(),
1326 [("c", -1), ("a", 1), ("d", 4), ("b", 6)])
1327 self.failUnlessEqual(d.item_with_largest_value(), ("b", 6))
1329 d = dictutil.NumDict({"a": 1, "b": 2})
1330 self.failUnlessEqual(repr(d), "{'a': 1, 'b': 2}")
1331 self.failUnless("a" in d)
1333 d2 = dictutil.NumDict({"c": 3, "d": 4})
1334 self.failUnless(d != d2)
1335 self.failUnless(d2 > d)
1336 self.failUnless(d2 >= d)
1337 self.failUnless(d <= d2)
1338 self.failUnless(d < d2)
1339 self.failUnlessEqual(d["a"], 1)
1340 self.failUnlessEqual(sorted(list([k for k in d])), ["a","b"])
1343 self.failUnlessRaises(TypeError, eq, d, "not a dict")
1346 self.failUnlessEqual(d, d3)
1347 self.failUnless(isinstance(d3, dictutil.NumDict))
1349 d4 = d.fromkeys(["a","b"], 5)
1350 self.failUnlessEqual(d4, {"a": 5, "b": 5})
1352 self.failUnlessEqual(d.get("a"), 1)
1353 self.failUnlessEqual(d.get("c"), 0)
1354 self.failUnlessEqual(d.get("c", 5), 5)
1355 self.failUnlessEqual(sorted(list(d.items())),
1356 [("a", 1), ("b", 2)])
1357 self.failUnlessEqual(sorted(list(d.iteritems())),
1358 [("a", 1), ("b", 2)])
1359 self.failUnlessEqual(sorted(d.keys()), ["a", "b"])
1360 self.failUnlessEqual(sorted(d.values()), [1, 2])
1361 self.failUnless(d.has_key("a"))
1362 self.failIf(d.has_key("c"))
1364 x = d.setdefault("c", 3)
1365 self.failUnlessEqual(x, 3)
1366 self.failUnlessEqual(d["c"], 3)
1367 x = d.setdefault("c", 5)
1368 self.failUnlessEqual(x, 3)
1369 self.failUnlessEqual(d["c"], 3)
1373 self.failUnless(x in [("a", 1), ("b", 2)])
1375 self.failUnless(x in [("a", 1), ("b", 2)])
1376 self.failUnlessRaises(KeyError, d.popitem)
1379 d.update({"c": 4, "d": 5})
1380 self.failUnlessEqual(d, {"c": 4, "d": 5})
1382 def test_del_if_present(self):
1383 d = {1: "a", 2: "b"}
1384 dictutil.del_if_present(d, 1)
1385 dictutil.del_if_present(d, 3)
1386 self.failUnlessEqual(d, {2: "b"})
1388 def test_valueordereddict(self):
1389 d = dictutil.ValueOrderedDict()
1394 self.failUnlessEqual(d, {"a": 3, "b": 2, "c": 1})
1395 self.failUnlessEqual(d.items(), [("c", 1), ("b", 2), ("a", 3)])
1396 self.failUnlessEqual(d.values(), [1, 2, 3])
1397 self.failUnlessEqual(d.keys(), ["c", "b", "a"])
1398 self.failUnlessEqual(repr(d), "<ValueOrderedDict {c: 1, b: 2, a: 3}>")
1401 self.failIf(d == {"a": 4})
1402 self.failUnless(d != {"a": 4})
1404 x = d.setdefault("d", 0)
1405 self.failUnlessEqual(x, 0)
1406 self.failUnlessEqual(d["d"], 0)
1407 x = d.setdefault("d", -1)
1408 self.failUnlessEqual(x, 0)
1409 self.failUnlessEqual(d["d"], 0)
1411 x = d.remove("e", "default", False)
1412 self.failUnlessEqual(x, "default")
1413 self.failUnlessRaises(KeyError, d.remove, "e", "default", True)
1414 x = d.remove("d", 5)
1415 self.failUnlessEqual(x, 0)
1417 x = d.__getitem__("c")
1418 self.failUnlessEqual(x, 1)
1419 x = d.__getitem__("e", "default", False)
1420 self.failUnlessEqual(x, "default")
1421 self.failUnlessRaises(KeyError, d.__getitem__, "e", "default", True)
1423 self.failUnlessEqual(d.popitem(), ("c", 1))
1424 self.failUnlessEqual(d.popitem(), ("b", 2))
1425 self.failUnlessEqual(d.popitem(), ("a", 3))
1426 self.failUnlessRaises(KeyError, d.popitem)
1428 d = dictutil.ValueOrderedDict({"a": 3, "b": 2, "c": 1})
1429 x = d.pop("d", "default", False)
1430 self.failUnlessEqual(x, "default")
1431 self.failUnlessRaises(KeyError, d.pop, "d", "default", True)
1433 self.failUnlessEqual(x, 2)
1434 self.failUnlessEqual(d.items(), [("c", 1), ("a", 3)])
1436 d = dictutil.ValueOrderedDict({"a": 3, "b": 2, "c": 1})
1437 x = d.pop_from_list(1) # pop the second item, b/2
1438 self.failUnlessEqual(x, "b")
1439 self.failUnlessEqual(d.items(), [("c", 1), ("a", 3)])
1441 def test_auxdict(self):
1442 d = dictutil.AuxValueDict()
1443 # we put the serialized form in the auxdata
1444 d.set_with_aux("key", ("filecap", "metadata"), "serialized")
1446 self.failUnlessEqual(d.keys(), ["key"])
1447 self.failUnlessEqual(d["key"], ("filecap", "metadata"))
1448 self.failUnlessEqual(d.get_aux("key"), "serialized")
1449 def _get_missing(key):
1451 self.failUnlessRaises(KeyError, _get_missing, "nonkey")
1452 self.failUnlessEqual(d.get("nonkey"), None)
1453 self.failUnlessEqual(d.get("nonkey", "nonvalue"), "nonvalue")
1454 self.failUnlessEqual(d.get_aux("nonkey"), None)
1455 self.failUnlessEqual(d.get_aux("nonkey", "nonvalue"), "nonvalue")
1457 d["key"] = ("filecap2", "metadata2")
1458 self.failUnlessEqual(d["key"], ("filecap2", "metadata2"))
1459 self.failUnlessEqual(d.get_aux("key"), None)
1461 d.set_with_aux("key2", "value2", "aux2")
1462 self.failUnlessEqual(sorted(d.keys()), ["key", "key2"])
1464 self.failUnlessEqual(d.keys(), ["key"])
1465 self.failIf("key2" in d)
1466 self.failUnlessRaises(KeyError, _get_missing, "key2")
1467 self.failUnlessEqual(d.get("key2"), None)
1468 self.failUnlessEqual(d.get_aux("key2"), None)
1469 d["key2"] = "newvalue2"
1470 self.failUnlessEqual(d.get("key2"), "newvalue2")
1471 self.failUnlessEqual(d.get_aux("key2"), None)
1473 d = dictutil.AuxValueDict({1:2,3:4})
1474 self.failUnlessEqual(sorted(d.keys()), [1,3])
1475 self.failUnlessEqual(d[1], 2)
1476 self.failUnlessEqual(d.get_aux(1), None)
1478 d = dictutil.AuxValueDict([ (1,2), (3,4) ])
1479 self.failUnlessEqual(sorted(d.keys()), [1,3])
1480 self.failUnlessEqual(d[1], 2)
1481 self.failUnlessEqual(d.get_aux(1), None)
1483 d = dictutil.AuxValueDict(one=1, two=2)
1484 self.failUnlessEqual(sorted(d.keys()), ["one","two"])
1485 self.failUnlessEqual(d["one"], 1)
1486 self.failUnlessEqual(d.get_aux("one"), None)
1488 class Pipeline(unittest.TestCase):
1489 def pause(self, *args, **kwargs):
1490 d = defer.Deferred()
1491 self.calls.append( (d, args, kwargs) )
1494 def failUnlessCallsAre(self, expected):
1497 self.failUnlessEqual(len(self.calls), len(expected), self.calls)
1498 for i,c in enumerate(self.calls):
1499 self.failUnlessEqual(c[1:], expected[i], str(i))
1501 def test_basic(self):
1504 p = pipeline.Pipeline(100)
1506 d = p.flush() # fires immediately
1507 d.addCallbacks(finished.append, log.err)
1508 self.failUnlessEqual(len(finished), 1)
1511 d = p.add(10, self.pause, "one")
1512 # the call should start right away, and our return Deferred should
1514 d.addCallbacks(finished.append, log.err)
1515 self.failUnlessEqual(len(finished), 1)
1516 self.failUnlessEqual(finished[0], None)
1517 self.failUnlessCallsAre([ ( ("one",) , {} ) ])
1518 self.failUnlessEqual(p.gauge, 10)
1523 d = p.add(20, self.pause, "two", kw=2)
1524 # pipeline: [one, two]
1526 # the call and the Deferred should fire right away
1527 d.addCallbacks(finished.append, log.err)
1528 self.failUnlessEqual(len(finished), 1)
1529 self.failUnlessEqual(finished[0], None)
1530 self.failUnlessCallsAre([ ( ("one",) , {} ),
1531 ( ("two",) , {"kw": 2} ),
1533 self.failUnlessEqual(p.gauge, 30)
1535 self.calls[0][0].callback("one-result")
1537 self.failUnlessEqual(p.gauge, 20)
1540 d = p.add(90, self.pause, "three", "posarg1")
1541 # pipeline: [two, three]
1544 fd.addCallbacks(flushed.append, log.err)
1545 self.failUnlessEqual(flushed, [])
1547 # the call will be made right away, but the return Deferred will not,
1548 # because the pipeline is now full.
1549 d.addCallbacks(finished.append, log.err)
1550 self.failUnlessEqual(len(finished), 0)
1551 self.failUnlessCallsAre([ ( ("one",) , {} ),
1552 ( ("two",) , {"kw": 2} ),
1553 ( ("three", "posarg1"), {} ),
1555 self.failUnlessEqual(p.gauge, 110)
1557 self.failUnlessRaises(pipeline.SingleFileError, p.add, 10, self.pause)
1559 # retiring either call will unblock the pipeline, causing the #3
1561 self.calls[2][0].callback("three-result")
1564 self.failUnlessEqual(len(finished), 1)
1565 self.failUnlessEqual(finished[0], None)
1566 self.failUnlessEqual(flushed, [])
1568 # retiring call#2 will finally allow the flush() Deferred to fire
1569 self.calls[1][0].callback("two-result")
1570 self.failUnlessEqual(len(flushed), 1)
1572 def test_errors(self):
1574 p = pipeline.Pipeline(100)
1576 d1 = p.add(200, self.pause, "one")
1580 d1.addBoth(finished.append)
1581 self.failUnlessEqual(finished, [])
1584 d2.addBoth(flushed.append)
1585 self.failUnlessEqual(flushed, [])
1587 self.calls[0][0].errback(ValueError("oops"))
1589 self.failUnlessEqual(len(finished), 1)
1591 self.failUnless(isinstance(f, Failure))
1592 self.failUnless(f.check(pipeline.PipelineError))
1593 self.failUnlessIn("PipelineError", str(f.value))
1594 self.failUnlessIn("ValueError", str(f.value))
1596 self.failUnless("ValueError" in r, r)
1598 self.failUnless(f2.check(ValueError))
1600 self.failUnlessEqual(len(flushed), 1)
1602 self.failUnless(isinstance(f, Failure))
1603 self.failUnless(f.check(pipeline.PipelineError))
1605 self.failUnless(f2.check(ValueError))
1607 # now that the pipeline is in the failed state, any new calls will
1610 d3 = p.add(20, self.pause, "two")
1613 d3.addBoth(finished.append)
1614 self.failUnlessEqual(len(finished), 1)
1616 self.failUnless(isinstance(f, Failure))
1617 self.failUnless(f.check(pipeline.PipelineError))
1619 self.failUnless("ValueError" in r, r)
1621 self.failUnless(f2.check(ValueError))
1625 d4.addBoth(flushed.append)
1626 self.failUnlessEqual(len(flushed), 1)
1628 self.failUnless(isinstance(f, Failure))
1629 self.failUnless(f.check(pipeline.PipelineError))
1631 self.failUnless(f2.check(ValueError))
1633 def test_errors2(self):
1635 p = pipeline.Pipeline(100)
1637 d1 = p.add(10, self.pause, "one")
1638 d2 = p.add(20, self.pause, "two")
1639 d3 = p.add(30, self.pause, "three")
1642 # one call fails, then the second one succeeds: make sure
1643 # ExpandableDeferredList tolerates the second one
1646 d4.addBoth(flushed.append)
1647 self.failUnlessEqual(flushed, [])
1649 self.calls[0][0].errback(ValueError("oops"))
1650 self.failUnlessEqual(len(flushed), 1)
1652 self.failUnless(isinstance(f, Failure))
1653 self.failUnless(f.check(pipeline.PipelineError))
1655 self.failUnless(f2.check(ValueError))
1657 self.calls[1][0].callback("two-result")
1658 self.calls[2][0].errback(ValueError("three-error"))
1662 class SampleError(Exception):
1665 class Log(unittest.TestCase):
1667 if not hasattr(self, "flushLoggedErrors"):
1668 # without flushLoggedErrors, we can't get rid of the
1669 # twisted.log.err that tahoe_log records, so we can't keep this
1670 # test from [ERROR]ing
1671 raise unittest.SkipTest("needs flushLoggedErrors from Twisted-2.5.0")
1673 raise SampleError("simple sample")
1676 tahoe_log.err(format="intentional sample error",
1677 failure=f, level=tahoe_log.OPERATIONAL, umid="wO9UoQ")
1678 self.flushLoggedErrors(SampleError)
1682 # this is a simple+inefficient form of util.spans.Spans . We compare the
1683 # behavior of this reference model against the real (efficient) form.
1685 def __init__(self, _span_or_start=None, length=None):
1687 if length is not None:
1688 for i in range(_span_or_start, _span_or_start+length):
1690 elif _span_or_start:
1691 for (start,length) in _span_or_start:
1692 self.add(start, length)
1694 def add(self, start, length):
1695 for i in range(start, start+length):
1699 def remove(self, start, length):
1700 for i in range(start, start+length):
1701 self._have.discard(i)
1705 return sorted(self._have)
1708 items = sorted(self._have)
1712 if prevstart is None:
1713 prevstart = prevend = i
1718 yield (prevstart, prevend-prevstart+1)
1719 prevstart = prevend = i
1720 if prevstart is not None:
1721 yield (prevstart, prevend-prevstart+1)
1723 def __nonzero__(self): # this gets us bool()
1727 return len(self._have)
1729 def __add__(self, other):
1730 s = self.__class__(self)
1731 for (start, length) in other:
1732 s.add(start, length)
1735 def __sub__(self, other):
1736 s = self.__class__(self)
1737 for (start, length) in other:
1738 s.remove(start, length)
1741 def __iadd__(self, other):
1742 for (start, length) in other:
1743 self.add(start, length)
1746 def __isub__(self, other):
1747 for (start, length) in other:
1748 self.remove(start, length)
1751 def __and__(self, other):
1752 s = self.__class__()
1753 for i in other.each():
1758 def __contains__(self, (start,length)):
1759 for i in range(start, start+length):
1760 if i not in self._have:
1764 class ByteSpans(unittest.TestCase):
1765 def test_basic(self):
1767 self.failUnlessEqual(list(s), [])
1769 self.failIf((0,1) in s)
1770 self.failUnlessEqual(s.len(), 0)
1772 s1 = Spans(3, 4) # 3,4,5,6
1775 s1 = Spans(3L, 4L) # 3,4,5,6
1781 s2.add(10,2) # 10,11
1783 self.failUnless((10,1) in s2)
1784 self.failIf((10,1) in s1)
1785 self.failUnlessEqual(list(s2.each()), [3,4,5,6,10,11])
1786 self.failUnlessEqual(s2.len(), 6)
1788 s2.add(15,2).add(20,2)
1789 self.failUnlessEqual(list(s2.each()), [3,4,5,6,10,11,15,16,20,21])
1790 self.failUnlessEqual(s2.len(), 10)
1792 s2.remove(4,3).remove(15,1)
1793 self.failUnlessEqual(list(s2.each()), [3,10,11,16,20,21])
1794 self.failUnlessEqual(s2.len(), 6)
1796 s1 = SimpleSpans(3, 4) # 3 4 5 6
1797 s2 = SimpleSpans(5, 4) # 5 6 7 8
1799 self.failUnlessEqual(list(i.each()), [5, 6])
1801 def _check1(self, s):
1802 self.failUnlessEqual(list(s), [(3,4)])
1804 self.failUnlessEqual(s.len(), 4)
1805 self.failIf((0,1) in s)
1806 self.failUnless((3,4) in s)
1807 self.failUnless((3,1) in s)
1808 self.failUnless((5,2) in s)
1809 self.failUnless((6,1) in s)
1810 self.failIf((6,2) in s)
1811 self.failIf((7,1) in s)
1812 self.failUnlessEqual(list(s.each()), [3,4,5,6])
1814 def test_large(self):
1815 s = Spans(4, 2**65) # don't do this with a SimpleSpans
1816 self.failUnlessEqual(list(s), [(4, 2**65)])
1818 self.failUnlessEqual(s.len(), 2**65)
1819 self.failIf((0,1) in s)
1820 self.failUnless((4,2) in s)
1821 self.failUnless((2**65,2) in s)
1823 def test_math(self):
1824 s1 = Spans(0, 10) # 0,1,2,3,4,5,6,7,8,9
1825 s2 = Spans(5, 3) # 5,6,7
1826 s3 = Spans(8, 4) # 8,9,10,11
1829 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,8,9])
1831 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7])
1833 self.failUnlessEqual(list(s.each()), [5,6,7])
1835 self.failUnlessEqual(list(s.each()), [5,6,7])
1837 self.failUnlessEqual(list(s.each()), [5,6,7])
1839 self.failUnlessEqual(list(s.each()), [8,9])
1841 self.failUnlessEqual(list(s.each()), [8,9])
1843 self.failUnlessEqual(list(s.each()), [])
1845 self.failUnlessEqual(list(s.each()), [])
1847 self.failUnlessEqual(list(s.each()), [])
1849 self.failUnlessEqual(list(s.each()), [])
1852 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7,8,9])
1854 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7,8,9,10,11])
1856 self.failUnlessEqual(list(s.each()), [5,6,7,8,9,10,11])
1860 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,8,9])
1863 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7])
1866 self.failUnlessEqual(list(s.each()), [5,6,7])
1870 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7,8,9])
1873 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7,8,9,10,11])
1876 self.failUnlessEqual(list(s.each()), [5,6,7,8,9,10,11])
1878 def test_random(self):
1879 # attempt to increase coverage of corner cases by comparing behavior
1880 # of a simple-but-slow model implementation against the
1881 # complex-but-fast actual implementation, in a large number of random
1885 s1 = S1(); s2 = S2()
1887 def _create(subseed):
1888 ns1 = S1(); ns2 = S2()
1890 what = _hash(subseed+str(i)).hexdigest()
1891 start = int(what[2:4], 16)
1892 length = max(1,int(what[5:6], 16))
1893 ns1.add(start, length); ns2.add(start, length)
1897 for i in range(1000):
1898 what = _hash(seed+str(i)).hexdigest()
1901 start = int(what[2:4], 16)
1902 length = max(1,int(what[5:6], 16))
1905 if subop in "01234":
1906 s1 = S1(); s2 = S2()
1907 elif subop in "5678":
1908 s1 = S1(start, length); s2 = S2(start, length)
1910 s1 = S1(s1); s2 = S2(s2)
1911 #print "s2 = %s" % s2.dump()
1913 #print "s2.add(%d,%d)" % (start, length)
1914 s1.add(start, length); s2.add(start, length)
1916 #print "s2.remove(%d,%d)" % (start, length)
1917 s1.remove(start, length); s2.remove(start, length)
1919 ns1, ns2 = _create(what[7:11])
1920 #print "s2 + %s" % ns2.dump()
1921 s1 = s1 + ns1; s2 = s2 + ns2
1923 ns1, ns2 = _create(what[7:11])
1924 #print "%s - %s" % (s2.dump(), ns2.dump())
1925 s1 = s1 - ns1; s2 = s2 - ns2
1927 ns1, ns2 = _create(what[7:11])
1928 #print "s2 += %s" % ns2.dump()
1929 s1 += ns1; s2 += ns2
1931 ns1, ns2 = _create(what[7:11])
1932 #print "%s -= %s" % (s2.dump(), ns2.dump())
1933 s1 -= ns1; s2 -= ns2
1935 ns1, ns2 = _create(what[7:11])
1936 #print "%s &= %s" % (s2.dump(), ns2.dump())
1937 s1 = s1 & ns1; s2 = s2 & ns2
1938 #print "s2 now %s" % s2.dump()
1939 self.failUnlessEqual(list(s1.each()), list(s2.each()))
1940 self.failUnlessEqual(s1.len(), s2.len())
1941 self.failUnlessEqual(bool(s1), bool(s2))
1942 self.failUnlessEqual(list(s1), list(s2))
1944 what = _hash(what[12:14]+str(j)).hexdigest()
1945 start = int(what[2:4], 16)
1946 length = max(1, int(what[5:6], 16))
1947 span = (start, length)
1948 self.failUnlessEqual(bool(span in s1), bool(span in s2))
1954 # s.add(start,length) : returns s
1955 # s.remove(start,length)
1956 # s.each() -> list of byte offsets, mostly for testing
1957 # list(s) -> list of (start,length) tuples, one per span
1958 # (start,length) in s -> True if (start..start+length-1) are all members
1959 # NOT equivalent to x in list(s)
1960 # s.len() -> number of bytes, for testing, bool(), and accounting/limiting
1961 # bool(s) (__nonzeron__)
1962 # s = s1+s2, s1-s2, +=s1, -=s1
1964 def test_overlap(self):
1969 self._test_overlap(a,b,c,d)
1971 def _test_overlap(self, a, b, c, d):
1972 s1 = set(range(a,a+b))
1973 s2 = set(range(c,c+d))
1975 #self._show_overlap(s1, "1")
1976 #self._show_overlap(s2, "2")
1977 o = overlap(a,b,c,d)
1978 expected = s1.intersection(s2)
1980 self.failUnlessEqual(o, None)
1983 so = set(range(start,start+length))
1984 #self._show(so, "o")
1985 self.failUnlessEqual(so, expected)
1987 def _show_overlap(self, s, c):
1991 for i in range(max(s)):
1998 def extend(s, start, length, fill):
1999 if len(s) >= start+length:
2001 assert len(fill) == 1
2002 return s + fill*(start+length-len(s))
2004 def replace(s, start, data):
2005 assert len(s) >= start+len(data)
2006 return s[:start] + data + s[start+len(data):]
2008 class SimpleDataSpans:
2009 def __init__(self, other=None):
2010 self.missing = "" # "1" where missing, "0" where found
2013 for (start, data) in other.get_chunks():
2014 self.add(start, data)
2016 def __nonzero__(self): # this gets us bool()
2019 return len(self.missing.replace("1", ""))
2021 return [i for (i,c) in enumerate(self.missing) if c == "0"]
2022 def _have(self, start, length):
2023 m = self.missing[start:start+length]
2024 if not m or len(m)<length or int(m):
2027 def get_chunks(self):
2028 for i in self._dump():
2029 yield (i, self.data[i])
2030 def get_spans(self):
2031 return SimpleSpans([(start,len(data))
2032 for (start,data) in self.get_chunks()])
2033 def get(self, start, length):
2034 if self._have(start, length):
2035 return self.data[start:start+length]
2037 def pop(self, start, length):
2038 data = self.get(start, length)
2040 self.remove(start, length)
2042 def remove(self, start, length):
2043 self.missing = replace(extend(self.missing, start, length, "1"),
2045 def add(self, start, data):
2046 self.missing = replace(extend(self.missing, start, len(data), "1"),
2047 start, "0"*len(data))
2048 self.data = replace(extend(self.data, start, len(data), " "),
2052 class StringSpans(unittest.TestCase):
2053 def do_basic(self, klass):
2055 self.failUnlessEqual(ds.len(), 0)
2056 self.failUnlessEqual(list(ds._dump()), [])
2057 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 0)
2059 self.failUnlessEqual(ds.get(0, 4), None)
2060 self.failUnlessEqual(ds.pop(0, 4), None)
2064 self.failUnlessEqual(ds.len(), 4)
2065 self.failUnlessEqual(list(ds._dump()), [2,3,4,5])
2066 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 4)
2068 self.failUnless((2,2) in s1)
2069 self.failUnlessEqual(ds.get(0, 4), None)
2070 self.failUnlessEqual(ds.pop(0, 4), None)
2071 self.failUnlessEqual(ds.get(4, 4), None)
2074 self.failUnlessEqual(ds2.len(), 4)
2075 self.failUnlessEqual(list(ds2._dump()), [2,3,4,5])
2076 self.failUnlessEqual(sum([len(d) for (s,d) in ds2.get_chunks()]), 4)
2077 self.failUnlessEqual(ds2.get(0, 4), None)
2078 self.failUnlessEqual(ds2.pop(0, 4), None)
2079 self.failUnlessEqual(ds2.pop(2, 3), "fou")
2080 self.failUnlessEqual(sum([len(d) for (s,d) in ds2.get_chunks()]), 1)
2081 self.failUnlessEqual(ds2.get(2, 3), None)
2082 self.failUnlessEqual(ds2.get(5, 1), "r")
2083 self.failUnlessEqual(ds.get(2, 3), "fou")
2084 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 4)
2087 self.failUnlessEqual(ds.len(), 6)
2088 self.failUnlessEqual(list(ds._dump()), [0,1,2,3,4,5])
2089 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 6)
2090 self.failUnlessEqual(ds.get(0, 4), "23fo")
2091 self.failUnlessEqual(ds.pop(0, 4), "23fo")
2092 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 2)
2093 self.failUnlessEqual(ds.get(0, 4), None)
2094 self.failUnlessEqual(ds.pop(0, 4), None)
2099 self.failUnlessEqual(ds.get(2, 4), "fear")
2104 self.failUnlessEqual(ds.get(2L, 4L), "fear")
2107 def do_scan(self, klass):
2108 # do a test with gaps and spans of size 1 and 2
2109 # left=(1,11) * right=(1,11) * gapsize=(1,2)
2110 # 111, 112, 121, 122, 211, 212, 221, 222
2119 # 11 1 1 11 11 11 1 1 111
2120 # 0123456789012345678901234567
2121 # abcdefghijklmnopqrstuvwxyz-=
2122 pieces = [(1, "bc"),
2132 p_elements = set([1,2,4,7,9,10,12,13,16,17,20,22,25,26,27])
2133 S = "abcdefghijklmnopqrstuvwxyz-="
2134 # TODO: when adding data, add capital letters, to make sure we aren't
2135 # just leaving the old data in place
2139 for start, data in pieces:
2144 d = "".join([((i not in p) and " " or S[i]) for i in range(l)])
2148 for start in range(0, l):
2149 for end in range(start+1, l):
2150 # add [start-end) to the baseline
2151 which = "%d-%d" % (start, end-1)
2152 p_added = set(range(start, end))
2156 print dump(b), which
2157 add = klass(); add.add(start, S[start:end])
2159 b.add(start, S[start:end])
2162 # check that the new span is there
2163 d = b.get(start, end-start)
2164 self.failUnlessEqual(d, S[start:end], which)
2165 # check that all the original pieces are still there
2166 for t_start, t_data in pieces:
2168 self.failUnlessEqual(b.get(t_start, t_len),
2169 S[t_start:t_start+t_len],
2170 "%s %d+%d" % (which, t_start, t_len))
2171 # check that a lot of subspans are mostly correct
2172 for t_start in range(l):
2173 for t_len in range(1,4):
2174 d = b.get(t_start, t_len)
2176 which2 = "%s+(%d-%d)" % (which, t_start,
2178 self.failUnlessEqual(d, S[t_start:t_start+t_len],
2180 # check that removing a subspan gives the right value
2182 b2.remove(t_start, t_len)
2183 removed = set(range(t_start, t_start+t_len))
2185 exp = (((i in p_elements) or (i in p_added))
2186 and (i not in removed))
2187 which2 = "%s-(%d-%d)" % (which, t_start,
2189 self.failUnlessEqual(bool(b2.get(i, 1)), exp,
2192 def test_test(self):
2193 self.do_basic(SimpleDataSpans)
2194 self.do_scan(SimpleDataSpans)
2196 def test_basic(self):
2197 self.do_basic(DataSpans)
2198 self.do_scan(DataSpans)
2200 def test_random(self):
2201 # attempt to increase coverage of corner cases by comparing behavior
2202 # of a simple-but-slow model implementation against the
2203 # complex-but-fast actual implementation, in a large number of random
2205 S1 = SimpleDataSpans
2207 s1 = S1(); s2 = S2()
2209 def _randstr(length, seed):
2212 while created < length:
2213 piece = _hash(seed + str(created)).hexdigest()
2214 pieces.append(piece)
2215 created += len(piece)
2216 return "".join(pieces)[:length]
2217 def _create(subseed):
2218 ns1 = S1(); ns2 = S2()
2220 what = _hash(subseed+str(i)).hexdigest()
2221 start = int(what[2:4], 16)
2222 length = max(1,int(what[5:6], 16))
2223 ns1.add(start, _randstr(length, what[7:9]));
2224 ns2.add(start, _randstr(length, what[7:9]))
2228 for i in range(1000):
2229 what = _hash(seed+str(i)).hexdigest()
2232 start = int(what[2:4], 16)
2233 length = max(1,int(what[5:6], 16))
2236 if subop in "0123456":
2237 s1 = S1(); s2 = S2()
2239 s1, s2 = _create(what[7:11])
2240 #print "s2 = %s" % list(s2._dump())
2241 elif op in "123456":
2242 #print "s2.add(%d,%d)" % (start, length)
2243 s1.add(start, _randstr(length, what[7:9]));
2244 s2.add(start, _randstr(length, what[7:9]))
2245 elif op in "789abc":
2246 #print "s2.remove(%d,%d)" % (start, length)
2247 s1.remove(start, length); s2.remove(start, length)
2249 #print "s2.pop(%d,%d)" % (start, length)
2250 d1 = s1.pop(start, length); d2 = s2.pop(start, length)
2251 self.failUnlessEqual(d1, d2)
2252 #print "s1 now %s" % list(s1._dump())
2253 #print "s2 now %s" % list(s2._dump())
2254 self.failUnlessEqual(s1.len(), s2.len())
2255 self.failUnlessEqual(list(s1._dump()), list(s2._dump()))
2256 for j in range(100):
2257 what = _hash(what[12:14]+str(j)).hexdigest()
2258 start = int(what[2:4], 16)
2259 length = max(1, int(what[5:6], 16))
2260 d1 = s1.get(start, length); d2 = s2.get(start, length)
2261 self.failUnlessEqual(d1, d2, "%d+%d" % (start, length))