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, TimezoneMixin
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))
444 def test_rename_no_overwrite(self):
445 workdir = fileutil.abspath_expanduser_unicode(u"test_rename_no_overwrite")
446 fileutil.make_dirs(workdir)
448 source_path = os.path.join(workdir, "source")
449 dest_path = os.path.join(workdir, "dest")
451 # when neither file exists
452 self.failUnlessRaises(OSError, fileutil.rename_no_overwrite, source_path, dest_path)
454 # when only dest exists
455 fileutil.write(dest_path, "dest")
456 self.failUnlessRaises(OSError, fileutil.rename_no_overwrite, source_path, dest_path)
457 self.failUnlessEqual(fileutil.read(dest_path), "dest")
460 fileutil.write(source_path, "source")
461 self.failUnlessRaises(OSError, fileutil.rename_no_overwrite, source_path, dest_path)
462 self.failUnlessEqual(fileutil.read(source_path), "source")
463 self.failUnlessEqual(fileutil.read(dest_path), "dest")
465 # when only source exists
467 fileutil.rename_no_overwrite(source_path, dest_path)
468 self.failUnlessEqual(fileutil.read(dest_path), "source")
469 self.failIf(os.path.exists(source_path))
471 def test_replace_file(self):
472 workdir = fileutil.abspath_expanduser_unicode(u"test_replace_file")
473 fileutil.make_dirs(workdir)
475 backup_path = os.path.join(workdir, "backup")
476 replaced_path = os.path.join(workdir, "replaced")
477 replacement_path = os.path.join(workdir, "replacement")
479 # when none of the files exist
480 self.failUnlessRaises(fileutil.ConflictError, fileutil.replace_file, replaced_path, replacement_path, backup_path)
482 # when only replaced exists
483 fileutil.write(replaced_path, "foo")
484 self.failUnlessRaises(fileutil.ConflictError, fileutil.replace_file, replaced_path, replacement_path, backup_path)
485 self.failUnlessEqual(fileutil.read(replaced_path), "foo")
487 # when both replaced and replacement exist, but not backup
488 fileutil.write(replacement_path, "bar")
489 fileutil.replace_file(replaced_path, replacement_path, backup_path)
490 self.failUnlessEqual(fileutil.read(backup_path), "foo")
491 self.failUnlessEqual(fileutil.read(replaced_path), "bar")
492 self.failIf(os.path.exists(replacement_path))
494 # when only replacement exists
495 os.remove(backup_path)
496 os.remove(replaced_path)
497 fileutil.write(replacement_path, "bar")
498 fileutil.replace_file(replaced_path, replacement_path, backup_path)
499 self.failUnlessEqual(fileutil.read(replaced_path), "bar")
500 self.failIf(os.path.exists(replacement_path))
501 self.failIf(os.path.exists(backup_path))
503 # when replaced, replacement and backup all exist
504 fileutil.write(replaced_path, "foo")
505 fileutil.write(replacement_path, "bar")
506 fileutil.write(backup_path, "bak")
507 fileutil.replace_file(replaced_path, replacement_path, backup_path)
508 self.failUnlessEqual(fileutil.read(backup_path), "foo")
509 self.failUnlessEqual(fileutil.read(replaced_path), "bar")
510 self.failIf(os.path.exists(replacement_path))
513 basedir = "util/FileUtil/test_du"
514 fileutil.make_dirs(basedir)
515 d = os.path.join(basedir, "space-consuming")
517 self.touch(d, "a/b/1.txt", data="a"*10)
518 self.touch(d, "a/b/2.txt", data="b"*11)
520 self.touch(d, "a/c/1.txt", data="c"*12)
521 self.touch(d, "a/c/2.txt", data="d"*13)
523 used = fileutil.du(basedir)
524 self.failUnlessEqual(10+11+12+13, used)
526 def test_abspath_expanduser_unicode(self):
527 self.failUnlessRaises(AssertionError, fileutil.abspath_expanduser_unicode, "bytestring")
529 saved_cwd = os.path.normpath(os.getcwdu())
530 abspath_cwd = fileutil.abspath_expanduser_unicode(u".")
531 self.failUnless(isinstance(saved_cwd, unicode), saved_cwd)
532 self.failUnless(isinstance(abspath_cwd, unicode), abspath_cwd)
533 if sys.platform == "win32":
534 self.failUnlessReallyEqual(abspath_cwd, fileutil.to_windows_long_path(saved_cwd))
536 self.failUnlessReallyEqual(abspath_cwd, saved_cwd)
538 self.failUnlessReallyEqual(fileutil.to_windows_long_path(u"\\\\?\\foo"), u"\\\\?\\foo")
539 self.failUnlessReallyEqual(fileutil.to_windows_long_path(u"\\\\.\\foo"), u"\\\\.\\foo")
540 self.failUnlessReallyEqual(fileutil.to_windows_long_path(u"\\\\server\\foo"), u"\\\\?\\UNC\\server\\foo")
541 self.failUnlessReallyEqual(fileutil.to_windows_long_path(u"C:\\foo"), u"\\\\?\\C:\\foo")
542 self.failUnlessReallyEqual(fileutil.to_windows_long_path(u"C:\\foo/bar"), u"\\\\?\\C:\\foo\\bar")
544 # adapted from <http://svn.python.org/view/python/branches/release26-maint/Lib/test/test_posixpath.py?view=markup&pathrev=78279#test_abspath>
546 foo = fileutil.abspath_expanduser_unicode(u"foo")
547 self.failUnless(foo.endswith(u"%sfoo" % (os.path.sep,)), foo)
549 foobar = fileutil.abspath_expanduser_unicode(u"bar", base=foo)
550 self.failUnless(foobar.endswith(u"%sfoo%sbar" % (os.path.sep, os.path.sep)), foobar)
552 if sys.platform == "win32":
553 # This is checking that a drive letter is added for a path without one.
554 baz = fileutil.abspath_expanduser_unicode(u"\\baz")
555 self.failUnless(baz.startswith(u"\\\\?\\"), baz)
556 self.failUnlessReallyEqual(baz[5 :], u":\\baz")
558 bar = fileutil.abspath_expanduser_unicode(u"\\bar", base=baz)
559 self.failUnless(bar.startswith(u"\\\\?\\"), bar)
560 self.failUnlessReallyEqual(bar[5 :], u":\\bar")
561 # not u":\\baz\\bar", because \bar is absolute on the current drive.
563 self.failUnlessReallyEqual(baz[4], bar[4]) # same drive
565 self.failIfIn(u"~", fileutil.abspath_expanduser_unicode(u"~"))
569 cwds.append(u'\xe7w\xf0'.encode(sys.getfilesystemencoding()
571 except UnicodeEncodeError:
572 pass # the cwd can't be encoded -- test with ascii cwd only
578 for upath in (u'', u'fuu', u'f\xf9\xf9', u'/fuu', u'U:\\', u'~'):
579 uabspath = fileutil.abspath_expanduser_unicode(upath)
580 self.failUnless(isinstance(uabspath, unicode), uabspath)
584 def test_create_long_path(self):
585 workdir = u"test_create_long_path"
586 fileutil.make_dirs(workdir)
587 long_path = fileutil.abspath_expanduser_unicode(os.path.join(workdir, u'x'*255))
589 fileutil.remove(long_path)
590 self.addCleanup(_cleanup)
592 fileutil.write(long_path, "test")
593 self.failUnless(os.path.exists(long_path))
594 self.failUnlessEqual(fileutil.read(long_path), "test")
596 self.failIf(os.path.exists(long_path))
598 def _test_windows_expanduser(self, userprofile=None, homedrive=None, homepath=None):
599 def call_windows_getenv(name):
600 if name == u"USERPROFILE": return userprofile
601 if name == u"HOMEDRIVE": return homedrive
602 if name == u"HOMEPATH": return homepath
603 self.fail("unexpected argument to call_windows_getenv")
604 self.patch(fileutil, 'windows_getenv', call_windows_getenv)
606 self.failUnlessReallyEqual(fileutil.windows_expanduser(u"~"), os.path.join(u"C:", u"\\Documents and Settings\\\u0100"))
607 self.failUnlessReallyEqual(fileutil.windows_expanduser(u"~\\foo"), os.path.join(u"C:", u"\\Documents and Settings\\\u0100", u"foo"))
608 self.failUnlessReallyEqual(fileutil.windows_expanduser(u"~/foo"), os.path.join(u"C:", u"\\Documents and Settings\\\u0100", u"foo"))
609 self.failUnlessReallyEqual(fileutil.windows_expanduser(u"a"), u"a")
610 self.failUnlessReallyEqual(fileutil.windows_expanduser(u"a~"), u"a~")
611 self.failUnlessReallyEqual(fileutil.windows_expanduser(u"a\\~\\foo"), u"a\\~\\foo")
613 def test_windows_expanduser_xp(self):
614 return self._test_windows_expanduser(homedrive=u"C:", homepath=u"\\Documents and Settings\\\u0100")
616 def test_windows_expanduser_win7(self):
617 return self._test_windows_expanduser(userprofile=os.path.join(u"C:", u"\\Documents and Settings\\\u0100"))
619 def test_disk_stats(self):
620 avail = fileutil.get_available_space('.', 2**14)
622 raise unittest.SkipTest("This test will spuriously fail there is no disk space left.")
624 disk = fileutil.get_disk_stats('.', 2**13)
625 self.failUnless(disk['total'] > 0, disk['total'])
626 # we tolerate used==0 for a Travis-CI bug, see #2290
627 self.failUnless(disk['used'] >= 0, disk['used'])
628 self.failUnless(disk['free_for_root'] > 0, disk['free_for_root'])
629 self.failUnless(disk['free_for_nonroot'] > 0, disk['free_for_nonroot'])
630 self.failUnless(disk['avail'] > 0, disk['avail'])
632 def test_disk_stats_avail_nonnegative(self):
633 # This test will spuriously fail if you have more than 2^128
634 # bytes of available space on your filesystem.
635 disk = fileutil.get_disk_stats('.', 2**128)
636 self.failUnlessEqual(disk['avail'], 0)
638 def test_get_pathinfo(self):
639 basedir = "util/FileUtil/test_get_pathinfo"
640 fileutil.make_dirs(basedir)
643 self.mkdir(basedir, "a")
644 dirinfo = fileutil.get_pathinfo(basedir)
645 self.failUnlessTrue(dirinfo.isdir)
646 self.failUnlessTrue(dirinfo.exists)
647 self.failUnlessFalse(dirinfo.isfile)
648 self.failUnlessFalse(dirinfo.islink)
650 # create a file under the directory
651 f = os.path.join(basedir, "a", "1.txt")
652 self.touch(basedir, "a/1.txt", data="a"*10)
653 fileinfo = fileutil.get_pathinfo(f)
654 self.failUnlessTrue(fileinfo.isfile)
655 self.failUnlessTrue(fileinfo.exists)
656 self.failUnlessFalse(fileinfo.isdir)
657 self.failUnlessFalse(fileinfo.islink)
658 self.failUnlessEqual(fileinfo.size, 10)
660 # create a symlink under the directory a pointing to 1.txt
661 slname = os.path.join(basedir, "a", "linkto1.txt")
662 os.symlink(f, slname)
663 symlinkinfo = fileutil.get_pathinfo(slname)
664 self.failUnlessTrue(symlinkinfo.islink)
665 self.failUnlessTrue(symlinkinfo.exists)
666 self.failUnlessFalse(symlinkinfo.isfile)
667 self.failUnlessFalse(symlinkinfo.isdir)
669 # path at which nothing exists
670 dnename = os.path.join(basedir, "a", "doesnotexist")
672 dneinfo = fileutil.get_pathinfo(dnename, now=now)
673 self.failUnlessFalse(dneinfo.exists)
674 self.failUnlessFalse(dneinfo.isfile)
675 self.failUnlessFalse(dneinfo.isdir)
676 self.failUnlessFalse(dneinfo.islink)
677 self.failUnlessEqual(dneinfo.size, None)
678 self.failUnlessEqual(dneinfo.mtime, now)
679 self.failUnlessEqual(dneinfo.ctime, now)
682 class PollMixinTests(unittest.TestCase):
684 self.pm = pollmixin.PollMixin()
686 def test_PollMixin_True(self):
687 d = self.pm.poll(check_f=lambda : True,
691 def test_PollMixin_False_then_True(self):
692 i = iter([False, True])
693 d = self.pm.poll(check_f=i.next,
697 def test_timeout(self):
698 d = self.pm.poll(check_f=lambda: False,
702 self.fail("poll should have failed, not returned %s" % (res,))
704 f.trap(pollmixin.TimeoutError)
705 return None # success
706 d.addCallbacks(_suc, _err)
709 class DeferredUtilTests(unittest.TestCase, deferredutil.WaitForDelayedCallsMixin):
710 def test_gather_results(self):
711 d1 = defer.Deferred()
712 d2 = defer.Deferred()
713 res = deferredutil.gatherResults([d1, d2])
714 d1.errback(ValueError("BAD"))
716 self.fail("Should have errbacked, not resulted in %s" % (res,))
718 thef.trap(ValueError)
719 res.addCallbacks(_callb, _errb)
722 def test_success(self):
723 d1, d2 = defer.Deferred(), defer.Deferred()
726 dlss = deferredutil.DeferredListShouldSucceed([d1,d2])
727 dlss.addCallbacks(good.append, bad.append)
730 self.failUnlessEqual(good, [[1,2]])
731 self.failUnlessEqual(bad, [])
733 def test_failure(self):
734 d1, d2 = defer.Deferred(), defer.Deferred()
737 dlss = deferredutil.DeferredListShouldSucceed([d1,d2])
738 dlss.addCallbacks(good.append, bad.append)
739 d1.addErrback(lambda _ignore: None)
740 d2.addErrback(lambda _ignore: None)
742 d2.errback(ValueError())
743 self.failUnlessEqual(good, [])
744 self.failUnlessEqual(len(bad), 1)
746 self.failUnless(isinstance(f, Failure))
747 self.failUnless(f.check(ValueError))
749 def test_wait_for_delayed_calls(self):
751 This tests that 'wait_for_delayed_calls' does in fact wait for a
752 delayed call that is active when the test returns. If it didn't,
753 Trial would report an unclean reactor error for this test.
758 reactor.callLater(0.1, _trigger)
760 d = defer.succeed(None)
761 d.addBoth(self.wait_for_delayed_calls)
764 class HashUtilTests(unittest.TestCase):
766 def test_random_key(self):
767 k = hashutil.random_key()
768 self.failUnlessEqual(len(k), hashutil.KEYLEN)
770 def test_sha256d(self):
771 h1 = hashutil.tagged_hash("tag1", "value")
772 h2 = hashutil.tagged_hasher("tag1")
776 self.failUnlessEqual(h1, h2a)
777 self.failUnlessEqual(h2a, h2b)
779 def test_sha256d_truncated(self):
780 h1 = hashutil.tagged_hash("tag1", "value", 16)
781 h2 = hashutil.tagged_hasher("tag1", 16)
784 self.failUnlessEqual(len(h1), 16)
785 self.failUnlessEqual(len(h2), 16)
786 self.failUnlessEqual(h1, h2)
789 h1 = hashutil.convergence_hash(3, 10, 1000, "data", "secret")
790 h2 = hashutil.convergence_hasher(3, 10, 1000, "secret")
793 self.failUnlessEqual(h1, h2)
795 def test_hashers(self):
796 h1 = hashutil.block_hash("foo")
797 h2 = hashutil.block_hasher()
799 self.failUnlessEqual(h1, h2.digest())
801 h1 = hashutil.uri_extension_hash("foo")
802 h2 = hashutil.uri_extension_hasher()
804 self.failUnlessEqual(h1, h2.digest())
806 h1 = hashutil.plaintext_hash("foo")
807 h2 = hashutil.plaintext_hasher()
809 self.failUnlessEqual(h1, h2.digest())
811 h1 = hashutil.crypttext_hash("foo")
812 h2 = hashutil.crypttext_hasher()
814 self.failUnlessEqual(h1, h2.digest())
816 h1 = hashutil.crypttext_segment_hash("foo")
817 h2 = hashutil.crypttext_segment_hasher()
819 self.failUnlessEqual(h1, h2.digest())
821 h1 = hashutil.plaintext_segment_hash("foo")
822 h2 = hashutil.plaintext_segment_hasher()
824 self.failUnlessEqual(h1, h2.digest())
826 def test_timing_safe_compare(self):
827 self.failUnless(hashutil.timing_safe_compare("a", "a"))
828 self.failUnless(hashutil.timing_safe_compare("ab", "ab"))
829 self.failIf(hashutil.timing_safe_compare("a", "b"))
830 self.failIf(hashutil.timing_safe_compare("a", "aa"))
832 def _testknown(self, hashf, expected_a, *args):
834 got_a = base32.b2a(got)
835 self.failUnlessEqual(got_a, expected_a)
837 def test_known_answers(self):
838 # assert backwards compatibility
839 self._testknown(hashutil.storage_index_hash, "qb5igbhcc5esa6lwqorsy7e6am", "")
840 self._testknown(hashutil.block_hash, "msjr5bh4evuh7fa3zw7uovixfbvlnstr5b65mrerwfnvjxig2jvq", "")
841 self._testknown(hashutil.uri_extension_hash, "wthsu45q7zewac2mnivoaa4ulh5xvbzdmsbuyztq2a5fzxdrnkka", "")
842 self._testknown(hashutil.plaintext_hash, "5lz5hwz3qj3af7n6e3arblw7xzutvnd3p3fjsngqjcb7utf3x3da", "")
843 self._testknown(hashutil.crypttext_hash, "itdj6e4njtkoiavlrmxkvpreosscssklunhwtvxn6ggho4rkqwga", "")
844 self._testknown(hashutil.crypttext_segment_hash, "aovy5aa7jej6ym5ikgwyoi4pxawnoj3wtaludjz7e2nb5xijb7aa", "")
845 self._testknown(hashutil.plaintext_segment_hash, "4fdgf6qruaisyukhqcmoth4t3li6bkolbxvjy4awwcpprdtva7za", "")
846 self._testknown(hashutil.convergence_hash, "3mo6ni7xweplycin6nowynw2we", 3, 10, 100, "", "converge")
847 self._testknown(hashutil.my_renewal_secret_hash, "ujhr5k5f7ypkp67jkpx6jl4p47pyta7hu5m527cpcgvkafsefm6q", "")
848 self._testknown(hashutil.my_cancel_secret_hash, "rjwzmafe2duixvqy6h47f5wfrokdziry6zhx4smew4cj6iocsfaa", "")
849 self._testknown(hashutil.file_renewal_secret_hash, "hzshk2kf33gzbd5n3a6eszkf6q6o6kixmnag25pniusyaulqjnia", "", "si")
850 self._testknown(hashutil.file_cancel_secret_hash, "bfciwvr6w7wcavsngxzxsxxaszj72dej54n4tu2idzp6b74g255q", "", "si")
851 self._testknown(hashutil.bucket_renewal_secret_hash, "e7imrzgzaoashsncacvy3oysdd2m5yvtooo4gmj4mjlopsazmvuq", "", "\x00"*20)
852 self._testknown(hashutil.bucket_cancel_secret_hash, "dvdujeyxeirj6uux6g7xcf4lvesk632aulwkzjar7srildvtqwma", "", "\x00"*20)
853 self._testknown(hashutil.hmac, "c54ypfi6pevb3nvo6ba42jtglpkry2kbdopqsi7dgrm4r7tw5sra", "tag", "")
854 self._testknown(hashutil.mutable_rwcap_key_hash, "6rvn2iqrghii5n4jbbwwqqsnqu", "iv", "wk")
855 self._testknown(hashutil.ssk_writekey_hash, "ykpgmdbpgbb6yqz5oluw2q26ye", "")
856 self._testknown(hashutil.ssk_write_enabler_master_hash, "izbfbfkoait4dummruol3gy2bnixrrrslgye6ycmkuyujnenzpia", "")
857 self._testknown(hashutil.ssk_write_enabler_hash, "fuu2dvx7g6gqu5x22vfhtyed7p4pd47y5hgxbqzgrlyvxoev62tq", "wk", "\x00"*20)
858 self._testknown(hashutil.ssk_pubkey_fingerprint_hash, "3opzw4hhm2sgncjx224qmt5ipqgagn7h5zivnfzqycvgqgmgz35q", "")
859 self._testknown(hashutil.ssk_readkey_hash, "vugid4as6qbqgeq2xczvvcedai", "")
860 self._testknown(hashutil.ssk_readkey_data_hash, "73wsaldnvdzqaf7v4pzbr2ae5a", "iv", "rk")
861 self._testknown(hashutil.ssk_storage_index_hash, "j7icz6kigb6hxrej3tv4z7ayym", "")
864 class Abbreviate(unittest.TestCase):
866 a = abbreviate.abbreviate_time
867 self.failUnlessEqual(a(None), "unknown")
868 self.failUnlessEqual(a(0), "0 seconds")
869 self.failUnlessEqual(a(1), "1 second")
870 self.failUnlessEqual(a(2), "2 seconds")
871 self.failUnlessEqual(a(119), "119 seconds")
873 self.failUnlessEqual(a(2*MIN), "2 minutes")
874 self.failUnlessEqual(a(60*MIN), "60 minutes")
875 self.failUnlessEqual(a(179*MIN), "179 minutes")
877 self.failUnlessEqual(a(180*MIN), "3 hours")
878 self.failUnlessEqual(a(4*HOUR), "4 hours")
881 self.failUnlessEqual(a(2*DAY), "2 days")
882 self.failUnlessEqual(a(2*MONTH), "2 months")
884 self.failUnlessEqual(a(5*YEAR), "5 years")
886 def test_space(self):
887 tests_si = [(None, "unknown"),
894 (20*1000, "20.00 kB"),
895 (1024*1024, "1.05 MB"),
896 (1000*1000, "1.00 MB"),
897 (1000*1000*1000, "1.00 GB"),
898 (1000*1000*1000*1000, "1.00 TB"),
899 (1000*1000*1000*1000*1000, "1.00 PB"),
900 (1000*1000*1000*1000*1000*1000, "1.00 EB"),
901 (1234567890123456789, "1.23 EB"),
903 for (x, expected) in tests_si:
904 got = abbreviate.abbreviate_space(x, SI=True)
905 self.failUnlessEqual(got, expected)
907 tests_base1024 = [(None, "unknown"),
914 (20*1024, "20.00 kiB"),
915 (1000*1000, "976.56 kiB"),
916 (1024*1024, "1.00 MiB"),
917 (1024*1024*1024, "1.00 GiB"),
918 (1024*1024*1024*1024, "1.00 TiB"),
919 (1000*1000*1000*1000*1000, "909.49 TiB"),
920 (1024*1024*1024*1024*1024, "1.00 PiB"),
921 (1024*1024*1024*1024*1024*1024, "1.00 EiB"),
922 (1234567890123456789, "1.07 EiB"),
924 for (x, expected) in tests_base1024:
925 got = abbreviate.abbreviate_space(x, SI=False)
926 self.failUnlessEqual(got, expected)
928 self.failUnlessEqual(abbreviate.abbreviate_space_both(1234567),
929 "(1.23 MB, 1.18 MiB)")
931 def test_parse_space(self):
932 p = abbreviate.parse_abbreviated_size
933 self.failUnlessEqual(p(""), None)
934 self.failUnlessEqual(p(None), None)
935 self.failUnlessEqual(p("123"), 123)
936 self.failUnlessEqual(p("123B"), 123)
937 self.failUnlessEqual(p("2K"), 2000)
938 self.failUnlessEqual(p("2kb"), 2000)
939 self.failUnlessEqual(p("2KiB"), 2048)
940 self.failUnlessEqual(p("10MB"), 10*1000*1000)
941 self.failUnlessEqual(p("10MiB"), 10*1024*1024)
942 self.failUnlessEqual(p("5G"), 5*1000*1000*1000)
943 self.failUnlessEqual(p("4GiB"), 4*1024*1024*1024)
944 self.failUnlessEqual(p("3TB"), 3*1000*1000*1000*1000)
945 self.failUnlessEqual(p("3TiB"), 3*1024*1024*1024*1024)
946 self.failUnlessEqual(p("6PB"), 6*1000*1000*1000*1000*1000)
947 self.failUnlessEqual(p("6PiB"), 6*1024*1024*1024*1024*1024)
948 self.failUnlessEqual(p("9EB"), 9*1000*1000*1000*1000*1000*1000)
949 self.failUnlessEqual(p("9EiB"), 9*1024*1024*1024*1024*1024*1024)
951 e = self.failUnlessRaises(ValueError, p, "12 cubits")
952 self.failUnlessIn("12 cubits", str(e))
953 e = self.failUnlessRaises(ValueError, p, "1 BB")
954 self.failUnlessIn("1 BB", str(e))
955 e = self.failUnlessRaises(ValueError, p, "fhtagn")
956 self.failUnlessIn("fhtagn", str(e))
958 class Limiter(unittest.TestCase):
959 timeout = 480 # This takes longer than 240 seconds on Francois's arm box.
961 def job(self, i, foo):
962 self.calls.append( (i, foo) )
963 self.simultaneous += 1
964 self.peak_simultaneous = max(self.simultaneous, self.peak_simultaneous)
967 self.simultaneous -= 1
968 d.callback("done %d" % i)
969 reactor.callLater(1.0, _done)
972 def bad_job(self, i, foo):
973 raise ValueError("bad_job %d" % i)
975 def test_limiter(self):
977 self.simultaneous = 0
978 self.peak_simultaneous = 0
979 l = limiter.ConcurrencyLimiter()
982 dl.append(l.add(self.job, i, foo=str(i)))
983 d = defer.DeferredList(dl, fireOnOneErrback=True)
985 self.failUnlessEqual(self.simultaneous, 0)
986 self.failUnless(self.peak_simultaneous <= 10)
987 self.failUnlessEqual(len(self.calls), 20)
989 self.failUnless( (i, str(i)) in self.calls)
993 def test_errors(self):
995 self.simultaneous = 0
996 self.peak_simultaneous = 0
997 l = limiter.ConcurrencyLimiter()
1000 dl.append(l.add(self.job, i, foo=str(i)))
1001 d2 = l.add(self.bad_job, 21, "21")
1002 d = defer.DeferredList(dl, fireOnOneErrback=True)
1003 def _most_done(res):
1005 for (success, result) in res:
1006 self.failUnlessEqual(success, True)
1007 results.append(result)
1009 expected_results = ["done %d" % i for i in range(20)]
1010 expected_results.sort()
1011 self.failUnlessEqual(results, expected_results)
1012 self.failUnless(self.peak_simultaneous <= 10)
1013 self.failUnlessEqual(len(self.calls), 20)
1015 self.failUnless( (i, str(i)) in self.calls)
1017 self.fail("should have failed, not got %s" % (res,))
1020 self.failUnless("bad_job 21" in str(f))
1021 d2.addCallbacks(_good, _err)
1023 d.addCallback(_most_done)
1025 self.failUnlessEqual(self.simultaneous, 0)
1026 self.failUnless(self.peak_simultaneous <= 10)
1027 self.failUnlessEqual(len(self.calls), 20)
1029 self.failUnless( (i, str(i)) in self.calls)
1030 d.addCallback(_all_done)
1033 class TimeFormat(unittest.TestCase, TimezoneMixin):
1034 def test_epoch(self):
1035 return self._help_test_epoch()
1037 def test_epoch_in_London(self):
1038 # Europe/London is a particularly troublesome timezone. Nowadays, its
1039 # offset from GMT is 0. But in 1970, its offset from GMT was 1.
1040 # (Apparently in 1970 Britain had redefined standard time to be GMT+1
1041 # and stayed in standard time all year round, whereas today
1042 # Europe/London standard time is GMT and Europe/London Daylight
1043 # Savings Time is GMT+1.) The current implementation of
1044 # time_format.iso_utc_time_to_localseconds() breaks if the timezone is
1045 # Europe/London. (As soon as this unit test is done then I'll change
1046 # that implementation to something that works even in this case...)
1047 self.setTimezone("Europe/London")
1048 return self._help_test_epoch()
1050 def _help_test_epoch(self):
1051 origtzname = time.tzname
1052 s = time_format.iso_utc_time_to_seconds("1970-01-01T00:00:01")
1053 self.failUnlessEqual(s, 1.0)
1054 s = time_format.iso_utc_time_to_seconds("1970-01-01_00:00:01")
1055 self.failUnlessEqual(s, 1.0)
1056 s = time_format.iso_utc_time_to_seconds("1970-01-01 00:00:01")
1057 self.failUnlessEqual(s, 1.0)
1059 self.failUnlessEqual(time_format.iso_utc(1.0), "1970-01-01_00:00:01")
1060 self.failUnlessEqual(time_format.iso_utc(1.0, sep=" "),
1061 "1970-01-01 00:00:01")
1064 isostr = time_format.iso_utc(now)
1065 timestamp = time_format.iso_utc_time_to_seconds(isostr)
1066 self.failUnlessEqual(int(timestamp), int(now))
1070 self.failUnlessEqual(time_format.iso_utc(t=my_time),
1071 "1970-01-01_00:00:01")
1072 e = self.failUnlessRaises(ValueError,
1073 time_format.iso_utc_time_to_seconds,
1074 "invalid timestring")
1075 self.failUnless("not a complete ISO8601 timestamp" in str(e))
1076 s = time_format.iso_utc_time_to_seconds("1970-01-01_00:00:01.500")
1077 self.failUnlessEqual(s, 1.5)
1079 # Look for daylight-savings-related errors.
1080 thatmomentinmarch = time_format.iso_utc_time_to_seconds("2009-03-20 21:49:02.226536")
1081 self.failUnlessEqual(thatmomentinmarch, 1237585742.226536)
1082 self.failUnlessEqual(origtzname, time.tzname)
1084 def test_iso_utc(self):
1085 when = 1266760143.7841301
1086 out = time_format.iso_utc_date(when)
1087 self.failUnlessEqual(out, "2010-02-21")
1088 out = time_format.iso_utc_date(t=lambda: when)
1089 self.failUnlessEqual(out, "2010-02-21")
1090 out = time_format.iso_utc(when)
1091 self.failUnlessEqual(out, "2010-02-21_13:49:03.784130")
1092 out = time_format.iso_utc(when, sep="-")
1093 self.failUnlessEqual(out, "2010-02-21-13:49:03.784130")
1095 def test_parse_duration(self):
1096 p = time_format.parse_duration
1098 self.failUnlessEqual(p("1 day"), DAY)
1099 self.failUnlessEqual(p("2 days"), 2*DAY)
1100 self.failUnlessEqual(p("3 months"), 3*31*DAY)
1101 self.failUnlessEqual(p("4 mo"), 4*31*DAY)
1102 self.failUnlessEqual(p("5 years"), 5*365*DAY)
1103 e = self.failUnlessRaises(ValueError, p, "123")
1104 self.failUnlessIn("no unit (like day, month, or year) in '123'",
1107 def test_parse_date(self):
1108 self.failUnlessEqual(time_format.parse_date("2010-02-21"), 1266710400)
1110 def test_format_time(self):
1111 self.failUnlessEqual(time_format.format_time(time.gmtime(0)), '1970-01-01 00:00:00')
1112 self.failUnlessEqual(time_format.format_time(time.gmtime(60)), '1970-01-01 00:01:00')
1113 self.failUnlessEqual(time_format.format_time(time.gmtime(60*60)), '1970-01-01 01:00:00')
1114 seconds_per_day = 60*60*24
1115 leap_years_1970_to_2014_inclusive = ((2012 - 1968) // 4)
1116 self.failUnlessEqual(time_format.format_time(time.gmtime(seconds_per_day*((2015 - 1970)*365+leap_years_1970_to_2014_inclusive))), '2015-01-01 00:00:00')
1118 def test_format_time_y2038(self):
1119 seconds_per_day = 60*60*24
1120 leap_years_1970_to_2047_inclusive = ((2044 - 1968) // 4)
1122 self.failUnlessEqual(time_format.format_time(time.gmtime(seconds_per_day*((2048 - 1970)*365+leap_years_1970_to_2047_inclusive))), '2048-01-01 00:00:00')
1123 except unittest.FailTest:
1124 raise unittest.SkipTest("Note: this system cannot handle dates after 2037.")
1126 def test_format_delta(self):
1128 time_5s_delta = 1389812728
1129 time_28m7s_delta = 1389814410
1130 time_1h_delta = 1389816323
1131 time_1d21h46m49s_delta = 1389977532
1133 self.failUnlessEqual(
1134 time_format.format_delta(time_1, time_1), '0s')
1136 self.failUnlessEqual(
1137 time_format.format_delta(time_1, time_5s_delta), '5s')
1138 self.failUnlessEqual(
1139 time_format.format_delta(time_1, time_28m7s_delta), '28m 7s')
1140 self.failUnlessEqual(
1141 time_format.format_delta(time_1, time_1h_delta), '1h 0m 0s')
1142 self.failUnlessEqual(
1143 time_format.format_delta(time_1, time_1d21h46m49s_delta), '1d 21h 46m 49s')
1145 self.failUnlessEqual(
1146 time_format.format_delta(time_1d21h46m49s_delta, time_1), '-')
1148 # time_1 with a decimal fraction will make the delta 1s less
1149 time_1decimal = 1389812723.383963
1151 self.failUnlessEqual(
1152 time_format.format_delta(time_1decimal, time_5s_delta), '4s')
1153 self.failUnlessEqual(
1154 time_format.format_delta(time_1decimal, time_28m7s_delta), '28m 6s')
1155 self.failUnlessEqual(
1156 time_format.format_delta(time_1decimal, time_1h_delta), '59m 59s')
1157 self.failUnlessEqual(
1158 time_format.format_delta(time_1decimal, time_1d21h46m49s_delta), '1d 21h 46m 48s')
1160 class CacheDir(unittest.TestCase):
1161 def test_basic(self):
1162 basedir = "test_util/CacheDir/test_basic"
1164 def _failIfExists(name):
1165 absfn = os.path.join(basedir, name)
1166 self.failIf(os.path.exists(absfn),
1167 "%s exists but it shouldn't" % absfn)
1169 def _failUnlessExists(name):
1170 absfn = os.path.join(basedir, name)
1171 self.failUnless(os.path.exists(absfn),
1172 "%s doesn't exist but it should" % absfn)
1174 cdm = cachedir.CacheDirectoryManager(basedir)
1175 a = cdm.get_file("a")
1176 b = cdm.get_file("b")
1177 c = cdm.get_file("c")
1178 f = open(a.get_filename(), "wb"); f.write("hi"); f.close(); del f
1179 f = open(b.get_filename(), "wb"); f.write("hi"); f.close(); del f
1180 f = open(c.get_filename(), "wb"); f.write("hi"); f.close(); del f
1182 _failUnlessExists("a")
1183 _failUnlessExists("b")
1184 _failUnlessExists("c")
1188 _failUnlessExists("a")
1189 _failUnlessExists("b")
1190 _failUnlessExists("c")
1193 # this file won't be deleted yet, because it isn't old enough
1195 _failUnlessExists("a")
1196 _failUnlessExists("b")
1197 _failUnlessExists("c")
1199 # we change the definition of "old" to make everything old
1204 _failUnlessExists("b")
1205 _failUnlessExists("c")
1213 _failUnlessExists("b")
1214 _failUnlessExists("c")
1216 b2 = cdm.get_file("b")
1220 _failUnlessExists("b")
1221 _failUnlessExists("c")
1226 def __init__(self, x):
1231 return "<%s %s>" % (self.__class__.__name__, self.x,)
1234 def __le__(self, other):
1235 return self.x <= other
1236 def __lt__(self, other):
1237 return self.x < other
1238 def __ge__(self, other):
1239 return self.x >= other
1240 def __gt__(self, other):
1241 return self.x > other
1242 def __ne__(self, other):
1243 return self.x != other
1244 def __eq__(self, other):
1245 return self.x == other
1247 class DictUtil(unittest.TestCase):
1248 def _help_test_empty_dict(self, klass):
1252 self.failUnless(d1 == d2, "d1: %r, d2: %r" % (d1, d2,))
1253 self.failUnless(len(d1) == 0)
1254 self.failUnless(len(d2) == 0)
1256 def _help_test_nonempty_dict(self, klass):
1257 d1 = klass({'a': 1, 'b': "eggs", 3: "spam",})
1258 d2 = klass({'a': 1, 'b': "eggs", 3: "spam",})
1260 self.failUnless(d1 == d2)
1261 self.failUnless(len(d1) == 3, "%s, %s" % (len(d1), d1,))
1262 self.failUnless(len(d2) == 3)
1264 def _help_test_eq_but_notis(self, klass):
1265 d = klass({'a': 3, 'b': EqButNotIs(3), 'c': 3})
1270 d['b'] = EqButNotIs(3)
1275 d['b'] = EqButNotIs(3)
1281 d['a'] = EqButNotIs(3)
1286 fake3 = EqButNotIs(3)
1287 fake7 = EqButNotIs(7)
1291 self.failUnless(filter(lambda x: x is 8, d.itervalues()))
1292 self.failUnless(filter(lambda x: x is fake7, d.itervalues()))
1293 # The real 7 should have been ejected by the d[3] = 8.
1294 self.failUnless(not filter(lambda x: x is 7, d.itervalues()))
1295 self.failUnless(filter(lambda x: x is fake3, d.iterkeys()))
1296 self.failUnless(filter(lambda x: x is 3, d.iterkeys()))
1301 fake3 = EqButNotIs(3)
1302 fake7 = EqButNotIs(7)
1305 self.failUnless(filter(lambda x: x is 8, d.itervalues()))
1306 self.failUnless(filter(lambda x: x is fake7, d.itervalues()))
1307 # The real 7 should have been ejected by the d[3] = 8.
1308 self.failUnless(not filter(lambda x: x is 7, d.itervalues()))
1309 self.failUnless(filter(lambda x: x is fake3, d.iterkeys()))
1310 self.failUnless(filter(lambda x: x is 3, d.iterkeys()))
1314 self._help_test_eq_but_notis(dictutil.UtilDict)
1315 self._help_test_eq_but_notis(dictutil.NumDict)
1316 self._help_test_eq_but_notis(dictutil.ValueOrderedDict)
1317 self._help_test_nonempty_dict(dictutil.UtilDict)
1318 self._help_test_nonempty_dict(dictutil.NumDict)
1319 self._help_test_nonempty_dict(dictutil.ValueOrderedDict)
1320 self._help_test_eq_but_notis(dictutil.UtilDict)
1321 self._help_test_eq_but_notis(dictutil.NumDict)
1322 self._help_test_eq_but_notis(dictutil.ValueOrderedDict)
1324 def test_dict_of_sets(self):
1325 ds = dictutil.DictOfSets()
1330 self.failUnlessEqual(ds[1], set(["a"]))
1331 self.failUnlessEqual(ds[2], set(["b", "c"]))
1332 ds.discard(3, "d") # should not raise an exception
1334 self.failUnlessEqual(ds[2], set(["c"]))
1336 self.failIf(2 in ds)
1339 ds2 = dictutil.DictOfSets()
1344 self.failUnlessEqual(ds[1], set(["a"]))
1345 self.failUnlessEqual(ds[3], set(["f", "g"]))
1346 self.failUnlessEqual(ds[4], set(["h"]))
1348 def test_move(self):
1349 d1 = {1: "a", 2: "b"}
1350 d2 = {2: "c", 3: "d"}
1351 dictutil.move(1, d1, d2)
1352 self.failUnlessEqual(d1, {2: "b"})
1353 self.failUnlessEqual(d2, {1: "a", 2: "c", 3: "d"})
1355 d1 = {1: "a", 2: "b"}
1356 d2 = {2: "c", 3: "d"}
1357 dictutil.move(2, d1, d2)
1358 self.failUnlessEqual(d1, {1: "a"})
1359 self.failUnlessEqual(d2, {2: "b", 3: "d"})
1361 d1 = {1: "a", 2: "b"}
1362 d2 = {2: "c", 3: "d"}
1363 self.failUnlessRaises(KeyError, dictutil.move, 5, d1, d2, strict=True)
1365 def test_subtract(self):
1366 d1 = {1: "a", 2: "b"}
1367 d2 = {2: "c", 3: "d"}
1368 d3 = dictutil.subtract(d1, d2)
1369 self.failUnlessEqual(d3, {1: "a"})
1371 d1 = {1: "a", 2: "b"}
1373 d3 = dictutil.subtract(d1, d2)
1374 self.failUnlessEqual(d3, {1: "a"})
1376 def test_utildict(self):
1377 d = dictutil.UtilDict({1: "a", 2: "b"})
1380 self.failUnlessEqual(d, {2: "b"})
1383 self.failUnlessRaises(TypeError, eq, d, "not a dict")
1385 d = dictutil.UtilDict({1: "b", 2: "a"})
1386 self.failUnlessEqual(d.items_sorted_by_value(),
1387 [(2, "a"), (1, "b")])
1388 self.failUnlessEqual(d.items_sorted_by_key(),
1389 [(1, "b"), (2, "a")])
1390 self.failUnlessEqual(repr(d), "{1: 'b', 2: 'a'}")
1391 self.failUnless(1 in d)
1393 d2 = dictutil.UtilDict({3: "c", 4: "d"})
1394 self.failUnless(d != d2)
1395 self.failUnless(d2 > d)
1396 self.failUnless(d2 >= d)
1397 self.failUnless(d <= d2)
1398 self.failUnless(d < d2)
1399 self.failUnlessEqual(d[1], "b")
1400 self.failUnlessEqual(sorted(list([k for k in d])), [1,2])
1403 self.failUnlessEqual(d, d3)
1404 self.failUnless(isinstance(d3, dictutil.UtilDict))
1406 d4 = d.fromkeys([3,4], "e")
1407 self.failUnlessEqual(d4, {3: "e", 4: "e"})
1409 self.failUnlessEqual(d.get(1), "b")
1410 self.failUnlessEqual(d.get(3), None)
1411 self.failUnlessEqual(d.get(3, "default"), "default")
1412 self.failUnlessEqual(sorted(list(d.items())),
1413 [(1, "b"), (2, "a")])
1414 self.failUnlessEqual(sorted(list(d.iteritems())),
1415 [(1, "b"), (2, "a")])
1416 self.failUnlessEqual(sorted(d.keys()), [1, 2])
1417 self.failUnlessEqual(sorted(d.values()), ["a", "b"])
1418 x = d.setdefault(1, "new")
1419 self.failUnlessEqual(x, "b")
1420 self.failUnlessEqual(d[1], "b")
1421 x = d.setdefault(3, "new")
1422 self.failUnlessEqual(x, "new")
1423 self.failUnlessEqual(d[3], "new")
1427 self.failUnless(x in [(1, "b"), (2, "a")])
1429 self.failUnless(x in [(1, "b"), (2, "a")])
1430 self.failUnlessRaises(KeyError, d.popitem)
1432 def test_numdict(self):
1433 d = dictutil.NumDict({"a": 1, "b": 2})
1435 d.add_num("a", 10, 5)
1436 d.add_num("c", 20, 5)
1438 self.failUnlessEqual(d, {"a": 11, "b": 2, "c": 25, "d": 30})
1440 d.subtract_num("a", 10)
1441 d.subtract_num("e", 10)
1442 d.subtract_num("f", 10, 15)
1443 self.failUnlessEqual(d, {"a": 1, "b": 2, "c": 25, "d": 30,
1446 self.failUnlessEqual(d.sum(), sum([1, 2, 25, 30, -10, 5]))
1448 d = dictutil.NumDict()
1452 self.failUnlessEqual(d, {"a": 2, "b": 6})
1456 self.failUnlessEqual(d, {"a": 1, "b": 6, "c": -1, "d": 4})
1457 self.failUnlessEqual(d.items_sorted_by_key(),
1458 [("a", 1), ("b", 6), ("c", -1), ("d", 4)])
1459 self.failUnlessEqual(d.items_sorted_by_value(),
1460 [("c", -1), ("a", 1), ("d", 4), ("b", 6)])
1461 self.failUnlessEqual(d.item_with_largest_value(), ("b", 6))
1463 d = dictutil.NumDict({"a": 1, "b": 2})
1464 self.failUnlessEqual(repr(d), "{'a': 1, 'b': 2}")
1465 self.failUnless("a" in d)
1467 d2 = dictutil.NumDict({"c": 3, "d": 4})
1468 self.failUnless(d != d2)
1469 self.failUnless(d2 > d)
1470 self.failUnless(d2 >= d)
1471 self.failUnless(d <= d2)
1472 self.failUnless(d < d2)
1473 self.failUnlessEqual(d["a"], 1)
1474 self.failUnlessEqual(sorted(list([k for k in d])), ["a","b"])
1477 self.failUnlessRaises(TypeError, eq, d, "not a dict")
1480 self.failUnlessEqual(d, d3)
1481 self.failUnless(isinstance(d3, dictutil.NumDict))
1483 d4 = d.fromkeys(["a","b"], 5)
1484 self.failUnlessEqual(d4, {"a": 5, "b": 5})
1486 self.failUnlessEqual(d.get("a"), 1)
1487 self.failUnlessEqual(d.get("c"), 0)
1488 self.failUnlessEqual(d.get("c", 5), 5)
1489 self.failUnlessEqual(sorted(list(d.items())),
1490 [("a", 1), ("b", 2)])
1491 self.failUnlessEqual(sorted(list(d.iteritems())),
1492 [("a", 1), ("b", 2)])
1493 self.failUnlessEqual(sorted(d.keys()), ["a", "b"])
1494 self.failUnlessEqual(sorted(d.values()), [1, 2])
1495 self.failUnless(d.has_key("a"))
1496 self.failIf(d.has_key("c"))
1498 x = d.setdefault("c", 3)
1499 self.failUnlessEqual(x, 3)
1500 self.failUnlessEqual(d["c"], 3)
1501 x = d.setdefault("c", 5)
1502 self.failUnlessEqual(x, 3)
1503 self.failUnlessEqual(d["c"], 3)
1507 self.failUnless(x in [("a", 1), ("b", 2)])
1509 self.failUnless(x in [("a", 1), ("b", 2)])
1510 self.failUnlessRaises(KeyError, d.popitem)
1513 d.update({"c": 4, "d": 5})
1514 self.failUnlessEqual(d, {"c": 4, "d": 5})
1516 def test_del_if_present(self):
1517 d = {1: "a", 2: "b"}
1518 dictutil.del_if_present(d, 1)
1519 dictutil.del_if_present(d, 3)
1520 self.failUnlessEqual(d, {2: "b"})
1522 def test_valueordereddict(self):
1523 d = dictutil.ValueOrderedDict()
1528 self.failUnlessEqual(d, {"a": 3, "b": 2, "c": 1})
1529 self.failUnlessEqual(d.items(), [("c", 1), ("b", 2), ("a", 3)])
1530 self.failUnlessEqual(d.values(), [1, 2, 3])
1531 self.failUnlessEqual(d.keys(), ["c", "b", "a"])
1532 self.failUnlessEqual(repr(d), "<ValueOrderedDict {c: 1, b: 2, a: 3}>")
1535 self.failIf(d == {"a": 4})
1536 self.failUnless(d != {"a": 4})
1538 x = d.setdefault("d", 0)
1539 self.failUnlessEqual(x, 0)
1540 self.failUnlessEqual(d["d"], 0)
1541 x = d.setdefault("d", -1)
1542 self.failUnlessEqual(x, 0)
1543 self.failUnlessEqual(d["d"], 0)
1545 x = d.remove("e", "default", False)
1546 self.failUnlessEqual(x, "default")
1547 self.failUnlessRaises(KeyError, d.remove, "e", "default", True)
1548 x = d.remove("d", 5)
1549 self.failUnlessEqual(x, 0)
1551 x = d.__getitem__("c")
1552 self.failUnlessEqual(x, 1)
1553 x = d.__getitem__("e", "default", False)
1554 self.failUnlessEqual(x, "default")
1555 self.failUnlessRaises(KeyError, d.__getitem__, "e", "default", True)
1557 self.failUnlessEqual(d.popitem(), ("c", 1))
1558 self.failUnlessEqual(d.popitem(), ("b", 2))
1559 self.failUnlessEqual(d.popitem(), ("a", 3))
1560 self.failUnlessRaises(KeyError, d.popitem)
1562 d = dictutil.ValueOrderedDict({"a": 3, "b": 2, "c": 1})
1563 x = d.pop("d", "default", False)
1564 self.failUnlessEqual(x, "default")
1565 self.failUnlessRaises(KeyError, d.pop, "d", "default", True)
1567 self.failUnlessEqual(x, 2)
1568 self.failUnlessEqual(d.items(), [("c", 1), ("a", 3)])
1570 d = dictutil.ValueOrderedDict({"a": 3, "b": 2, "c": 1})
1571 x = d.pop_from_list(1) # pop the second item, b/2
1572 self.failUnlessEqual(x, "b")
1573 self.failUnlessEqual(d.items(), [("c", 1), ("a", 3)])
1575 def test_auxdict(self):
1576 d = dictutil.AuxValueDict()
1577 # we put the serialized form in the auxdata
1578 d.set_with_aux("key", ("filecap", "metadata"), "serialized")
1580 self.failUnlessEqual(d.keys(), ["key"])
1581 self.failUnlessEqual(d["key"], ("filecap", "metadata"))
1582 self.failUnlessEqual(d.get_aux("key"), "serialized")
1583 def _get_missing(key):
1585 self.failUnlessRaises(KeyError, _get_missing, "nonkey")
1586 self.failUnlessEqual(d.get("nonkey"), None)
1587 self.failUnlessEqual(d.get("nonkey", "nonvalue"), "nonvalue")
1588 self.failUnlessEqual(d.get_aux("nonkey"), None)
1589 self.failUnlessEqual(d.get_aux("nonkey", "nonvalue"), "nonvalue")
1591 d["key"] = ("filecap2", "metadata2")
1592 self.failUnlessEqual(d["key"], ("filecap2", "metadata2"))
1593 self.failUnlessEqual(d.get_aux("key"), None)
1595 d.set_with_aux("key2", "value2", "aux2")
1596 self.failUnlessEqual(sorted(d.keys()), ["key", "key2"])
1598 self.failUnlessEqual(d.keys(), ["key"])
1599 self.failIf("key2" in d)
1600 self.failUnlessRaises(KeyError, _get_missing, "key2")
1601 self.failUnlessEqual(d.get("key2"), None)
1602 self.failUnlessEqual(d.get_aux("key2"), None)
1603 d["key2"] = "newvalue2"
1604 self.failUnlessEqual(d.get("key2"), "newvalue2")
1605 self.failUnlessEqual(d.get_aux("key2"), None)
1607 d = dictutil.AuxValueDict({1:2,3:4})
1608 self.failUnlessEqual(sorted(d.keys()), [1,3])
1609 self.failUnlessEqual(d[1], 2)
1610 self.failUnlessEqual(d.get_aux(1), None)
1612 d = dictutil.AuxValueDict([ (1,2), (3,4) ])
1613 self.failUnlessEqual(sorted(d.keys()), [1,3])
1614 self.failUnlessEqual(d[1], 2)
1615 self.failUnlessEqual(d.get_aux(1), None)
1617 d = dictutil.AuxValueDict(one=1, two=2)
1618 self.failUnlessEqual(sorted(d.keys()), ["one","two"])
1619 self.failUnlessEqual(d["one"], 1)
1620 self.failUnlessEqual(d.get_aux("one"), None)
1622 class Pipeline(unittest.TestCase):
1623 def pause(self, *args, **kwargs):
1624 d = defer.Deferred()
1625 self.calls.append( (d, args, kwargs) )
1628 def failUnlessCallsAre(self, expected):
1631 self.failUnlessEqual(len(self.calls), len(expected), self.calls)
1632 for i,c in enumerate(self.calls):
1633 self.failUnlessEqual(c[1:], expected[i], str(i))
1635 def test_basic(self):
1638 p = pipeline.Pipeline(100)
1640 d = p.flush() # fires immediately
1641 d.addCallbacks(finished.append, log.err)
1642 self.failUnlessEqual(len(finished), 1)
1645 d = p.add(10, self.pause, "one")
1646 # the call should start right away, and our return Deferred should
1648 d.addCallbacks(finished.append, log.err)
1649 self.failUnlessEqual(len(finished), 1)
1650 self.failUnlessEqual(finished[0], None)
1651 self.failUnlessCallsAre([ ( ("one",) , {} ) ])
1652 self.failUnlessEqual(p.gauge, 10)
1657 d = p.add(20, self.pause, "two", kw=2)
1658 # pipeline: [one, two]
1660 # the call and the Deferred should fire right away
1661 d.addCallbacks(finished.append, log.err)
1662 self.failUnlessEqual(len(finished), 1)
1663 self.failUnlessEqual(finished[0], None)
1664 self.failUnlessCallsAre([ ( ("one",) , {} ),
1665 ( ("two",) , {"kw": 2} ),
1667 self.failUnlessEqual(p.gauge, 30)
1669 self.calls[0][0].callback("one-result")
1671 self.failUnlessEqual(p.gauge, 20)
1674 d = p.add(90, self.pause, "three", "posarg1")
1675 # pipeline: [two, three]
1678 fd.addCallbacks(flushed.append, log.err)
1679 self.failUnlessEqual(flushed, [])
1681 # the call will be made right away, but the return Deferred will not,
1682 # because the pipeline is now full.
1683 d.addCallbacks(finished.append, log.err)
1684 self.failUnlessEqual(len(finished), 0)
1685 self.failUnlessCallsAre([ ( ("one",) , {} ),
1686 ( ("two",) , {"kw": 2} ),
1687 ( ("three", "posarg1"), {} ),
1689 self.failUnlessEqual(p.gauge, 110)
1691 self.failUnlessRaises(pipeline.SingleFileError, p.add, 10, self.pause)
1693 # retiring either call will unblock the pipeline, causing the #3
1695 self.calls[2][0].callback("three-result")
1698 self.failUnlessEqual(len(finished), 1)
1699 self.failUnlessEqual(finished[0], None)
1700 self.failUnlessEqual(flushed, [])
1702 # retiring call#2 will finally allow the flush() Deferred to fire
1703 self.calls[1][0].callback("two-result")
1704 self.failUnlessEqual(len(flushed), 1)
1706 def test_errors(self):
1708 p = pipeline.Pipeline(100)
1710 d1 = p.add(200, self.pause, "one")
1714 d1.addBoth(finished.append)
1715 self.failUnlessEqual(finished, [])
1718 d2.addBoth(flushed.append)
1719 self.failUnlessEqual(flushed, [])
1721 self.calls[0][0].errback(ValueError("oops"))
1723 self.failUnlessEqual(len(finished), 1)
1725 self.failUnless(isinstance(f, Failure))
1726 self.failUnless(f.check(pipeline.PipelineError))
1727 self.failUnlessIn("PipelineError", str(f.value))
1728 self.failUnlessIn("ValueError", str(f.value))
1730 self.failUnless("ValueError" in r, r)
1732 self.failUnless(f2.check(ValueError))
1734 self.failUnlessEqual(len(flushed), 1)
1736 self.failUnless(isinstance(f, Failure))
1737 self.failUnless(f.check(pipeline.PipelineError))
1739 self.failUnless(f2.check(ValueError))
1741 # now that the pipeline is in the failed state, any new calls will
1744 d3 = p.add(20, self.pause, "two")
1747 d3.addBoth(finished.append)
1748 self.failUnlessEqual(len(finished), 1)
1750 self.failUnless(isinstance(f, Failure))
1751 self.failUnless(f.check(pipeline.PipelineError))
1753 self.failUnless("ValueError" in r, r)
1755 self.failUnless(f2.check(ValueError))
1759 d4.addBoth(flushed.append)
1760 self.failUnlessEqual(len(flushed), 1)
1762 self.failUnless(isinstance(f, Failure))
1763 self.failUnless(f.check(pipeline.PipelineError))
1765 self.failUnless(f2.check(ValueError))
1767 def test_errors2(self):
1769 p = pipeline.Pipeline(100)
1771 d1 = p.add(10, self.pause, "one")
1772 d2 = p.add(20, self.pause, "two")
1773 d3 = p.add(30, self.pause, "three")
1776 # one call fails, then the second one succeeds: make sure
1777 # ExpandableDeferredList tolerates the second one
1780 d4.addBoth(flushed.append)
1781 self.failUnlessEqual(flushed, [])
1783 self.calls[0][0].errback(ValueError("oops"))
1784 self.failUnlessEqual(len(flushed), 1)
1786 self.failUnless(isinstance(f, Failure))
1787 self.failUnless(f.check(pipeline.PipelineError))
1789 self.failUnless(f2.check(ValueError))
1791 self.calls[1][0].callback("two-result")
1792 self.calls[2][0].errback(ValueError("three-error"))
1796 class SampleError(Exception):
1799 class Log(unittest.TestCase):
1801 if not hasattr(self, "flushLoggedErrors"):
1802 # without flushLoggedErrors, we can't get rid of the
1803 # twisted.log.err that tahoe_log records, so we can't keep this
1804 # test from [ERROR]ing
1805 raise unittest.SkipTest("needs flushLoggedErrors from Twisted-2.5.0")
1807 raise SampleError("simple sample")
1810 tahoe_log.err(format="intentional sample error",
1811 failure=f, level=tahoe_log.OPERATIONAL, umid="wO9UoQ")
1812 self.flushLoggedErrors(SampleError)
1816 # this is a simple+inefficient form of util.spans.Spans . We compare the
1817 # behavior of this reference model against the real (efficient) form.
1819 def __init__(self, _span_or_start=None, length=None):
1821 if length is not None:
1822 for i in range(_span_or_start, _span_or_start+length):
1824 elif _span_or_start:
1825 for (start,length) in _span_or_start:
1826 self.add(start, length)
1828 def add(self, start, length):
1829 for i in range(start, start+length):
1833 def remove(self, start, length):
1834 for i in range(start, start+length):
1835 self._have.discard(i)
1839 return sorted(self._have)
1842 items = sorted(self._have)
1846 if prevstart is None:
1847 prevstart = prevend = i
1852 yield (prevstart, prevend-prevstart+1)
1853 prevstart = prevend = i
1854 if prevstart is not None:
1855 yield (prevstart, prevend-prevstart+1)
1857 def __nonzero__(self): # this gets us bool()
1861 return len(self._have)
1863 def __add__(self, other):
1864 s = self.__class__(self)
1865 for (start, length) in other:
1866 s.add(start, length)
1869 def __sub__(self, other):
1870 s = self.__class__(self)
1871 for (start, length) in other:
1872 s.remove(start, length)
1875 def __iadd__(self, other):
1876 for (start, length) in other:
1877 self.add(start, length)
1880 def __isub__(self, other):
1881 for (start, length) in other:
1882 self.remove(start, length)
1885 def __and__(self, other):
1886 s = self.__class__()
1887 for i in other.each():
1892 def __contains__(self, (start,length)):
1893 for i in range(start, start+length):
1894 if i not in self._have:
1898 class ByteSpans(unittest.TestCase):
1899 def test_basic(self):
1901 self.failUnlessEqual(list(s), [])
1903 self.failIf((0,1) in s)
1904 self.failUnlessEqual(s.len(), 0)
1906 s1 = Spans(3, 4) # 3,4,5,6
1909 s1 = Spans(3L, 4L) # 3,4,5,6
1915 s2.add(10,2) # 10,11
1917 self.failUnless((10,1) in s2)
1918 self.failIf((10,1) in s1)
1919 self.failUnlessEqual(list(s2.each()), [3,4,5,6,10,11])
1920 self.failUnlessEqual(s2.len(), 6)
1922 s2.add(15,2).add(20,2)
1923 self.failUnlessEqual(list(s2.each()), [3,4,5,6,10,11,15,16,20,21])
1924 self.failUnlessEqual(s2.len(), 10)
1926 s2.remove(4,3).remove(15,1)
1927 self.failUnlessEqual(list(s2.each()), [3,10,11,16,20,21])
1928 self.failUnlessEqual(s2.len(), 6)
1930 s1 = SimpleSpans(3, 4) # 3 4 5 6
1931 s2 = SimpleSpans(5, 4) # 5 6 7 8
1933 self.failUnlessEqual(list(i.each()), [5, 6])
1935 def _check1(self, s):
1936 self.failUnlessEqual(list(s), [(3,4)])
1938 self.failUnlessEqual(s.len(), 4)
1939 self.failIf((0,1) in s)
1940 self.failUnless((3,4) in s)
1941 self.failUnless((3,1) in s)
1942 self.failUnless((5,2) in s)
1943 self.failUnless((6,1) in s)
1944 self.failIf((6,2) in s)
1945 self.failIf((7,1) in s)
1946 self.failUnlessEqual(list(s.each()), [3,4,5,6])
1948 def test_large(self):
1949 s = Spans(4, 2**65) # don't do this with a SimpleSpans
1950 self.failUnlessEqual(list(s), [(4, 2**65)])
1952 self.failUnlessEqual(s.len(), 2**65)
1953 self.failIf((0,1) in s)
1954 self.failUnless((4,2) in s)
1955 self.failUnless((2**65,2) in s)
1957 def test_math(self):
1958 s1 = Spans(0, 10) # 0,1,2,3,4,5,6,7,8,9
1959 s2 = Spans(5, 3) # 5,6,7
1960 s3 = Spans(8, 4) # 8,9,10,11
1963 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,8,9])
1965 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7])
1967 self.failUnlessEqual(list(s.each()), [5,6,7])
1969 self.failUnlessEqual(list(s.each()), [5,6,7])
1971 self.failUnlessEqual(list(s.each()), [5,6,7])
1973 self.failUnlessEqual(list(s.each()), [8,9])
1975 self.failUnlessEqual(list(s.each()), [8,9])
1977 self.failUnlessEqual(list(s.each()), [])
1979 self.failUnlessEqual(list(s.each()), [])
1981 self.failUnlessEqual(list(s.each()), [])
1983 self.failUnlessEqual(list(s.each()), [])
1986 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7,8,9])
1988 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7,8,9,10,11])
1990 self.failUnlessEqual(list(s.each()), [5,6,7,8,9,10,11])
1994 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,8,9])
1997 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7])
2000 self.failUnlessEqual(list(s.each()), [5,6,7])
2004 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7,8,9])
2007 self.failUnlessEqual(list(s.each()), [0,1,2,3,4,5,6,7,8,9,10,11])
2010 self.failUnlessEqual(list(s.each()), [5,6,7,8,9,10,11])
2012 def test_random(self):
2013 # attempt to increase coverage of corner cases by comparing behavior
2014 # of a simple-but-slow model implementation against the
2015 # complex-but-fast actual implementation, in a large number of random
2019 s1 = S1(); s2 = S2()
2021 def _create(subseed):
2022 ns1 = S1(); ns2 = S2()
2024 what = _hash(subseed+str(i)).hexdigest()
2025 start = int(what[2:4], 16)
2026 length = max(1,int(what[5:6], 16))
2027 ns1.add(start, length); ns2.add(start, length)
2031 for i in range(1000):
2032 what = _hash(seed+str(i)).hexdigest()
2035 start = int(what[2:4], 16)
2036 length = max(1,int(what[5:6], 16))
2039 if subop in "01234":
2040 s1 = S1(); s2 = S2()
2041 elif subop in "5678":
2042 s1 = S1(start, length); s2 = S2(start, length)
2044 s1 = S1(s1); s2 = S2(s2)
2045 #print "s2 = %s" % s2.dump()
2047 #print "s2.add(%d,%d)" % (start, length)
2048 s1.add(start, length); s2.add(start, length)
2050 #print "s2.remove(%d,%d)" % (start, length)
2051 s1.remove(start, length); s2.remove(start, length)
2053 ns1, ns2 = _create(what[7:11])
2054 #print "s2 + %s" % ns2.dump()
2055 s1 = s1 + ns1; s2 = s2 + ns2
2057 ns1, ns2 = _create(what[7:11])
2058 #print "%s - %s" % (s2.dump(), ns2.dump())
2059 s1 = s1 - ns1; s2 = s2 - ns2
2061 ns1, ns2 = _create(what[7:11])
2062 #print "s2 += %s" % ns2.dump()
2063 s1 += ns1; s2 += ns2
2065 ns1, ns2 = _create(what[7:11])
2066 #print "%s -= %s" % (s2.dump(), ns2.dump())
2067 s1 -= ns1; s2 -= ns2
2069 ns1, ns2 = _create(what[7:11])
2070 #print "%s &= %s" % (s2.dump(), ns2.dump())
2071 s1 = s1 & ns1; s2 = s2 & ns2
2072 #print "s2 now %s" % s2.dump()
2073 self.failUnlessEqual(list(s1.each()), list(s2.each()))
2074 self.failUnlessEqual(s1.len(), s2.len())
2075 self.failUnlessEqual(bool(s1), bool(s2))
2076 self.failUnlessEqual(list(s1), list(s2))
2078 what = _hash(what[12:14]+str(j)).hexdigest()
2079 start = int(what[2:4], 16)
2080 length = max(1, int(what[5:6], 16))
2081 span = (start, length)
2082 self.failUnlessEqual(bool(span in s1), bool(span in s2))
2088 # s.add(start,length) : returns s
2089 # s.remove(start,length)
2090 # s.each() -> list of byte offsets, mostly for testing
2091 # list(s) -> list of (start,length) tuples, one per span
2092 # (start,length) in s -> True if (start..start+length-1) are all members
2093 # NOT equivalent to x in list(s)
2094 # s.len() -> number of bytes, for testing, bool(), and accounting/limiting
2095 # bool(s) (__nonzeron__)
2096 # s = s1+s2, s1-s2, +=s1, -=s1
2098 def test_overlap(self):
2103 self._test_overlap(a,b,c,d)
2105 def _test_overlap(self, a, b, c, d):
2106 s1 = set(range(a,a+b))
2107 s2 = set(range(c,c+d))
2109 #self._show_overlap(s1, "1")
2110 #self._show_overlap(s2, "2")
2111 o = overlap(a,b,c,d)
2112 expected = s1.intersection(s2)
2114 self.failUnlessEqual(o, None)
2117 so = set(range(start,start+length))
2118 #self._show(so, "o")
2119 self.failUnlessEqual(so, expected)
2121 def _show_overlap(self, s, c):
2125 for i in range(max(s)):
2132 def extend(s, start, length, fill):
2133 if len(s) >= start+length:
2135 assert len(fill) == 1
2136 return s + fill*(start+length-len(s))
2138 def replace(s, start, data):
2139 assert len(s) >= start+len(data)
2140 return s[:start] + data + s[start+len(data):]
2142 class SimpleDataSpans:
2143 def __init__(self, other=None):
2144 self.missing = "" # "1" where missing, "0" where found
2147 for (start, data) in other.get_chunks():
2148 self.add(start, data)
2150 def __nonzero__(self): # this gets us bool()
2153 return len(self.missing.replace("1", ""))
2155 return [i for (i,c) in enumerate(self.missing) if c == "0"]
2156 def _have(self, start, length):
2157 m = self.missing[start:start+length]
2158 if not m or len(m)<length or int(m):
2161 def get_chunks(self):
2162 for i in self._dump():
2163 yield (i, self.data[i])
2164 def get_spans(self):
2165 return SimpleSpans([(start,len(data))
2166 for (start,data) in self.get_chunks()])
2167 def get(self, start, length):
2168 if self._have(start, length):
2169 return self.data[start:start+length]
2171 def pop(self, start, length):
2172 data = self.get(start, length)
2174 self.remove(start, length)
2176 def remove(self, start, length):
2177 self.missing = replace(extend(self.missing, start, length, "1"),
2179 def add(self, start, data):
2180 self.missing = replace(extend(self.missing, start, len(data), "1"),
2181 start, "0"*len(data))
2182 self.data = replace(extend(self.data, start, len(data), " "),
2186 class StringSpans(unittest.TestCase):
2187 def do_basic(self, klass):
2189 self.failUnlessEqual(ds.len(), 0)
2190 self.failUnlessEqual(list(ds._dump()), [])
2191 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 0)
2193 self.failUnlessEqual(ds.get(0, 4), None)
2194 self.failUnlessEqual(ds.pop(0, 4), None)
2198 self.failUnlessEqual(ds.len(), 4)
2199 self.failUnlessEqual(list(ds._dump()), [2,3,4,5])
2200 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 4)
2202 self.failUnless((2,2) in s1)
2203 self.failUnlessEqual(ds.get(0, 4), None)
2204 self.failUnlessEqual(ds.pop(0, 4), None)
2205 self.failUnlessEqual(ds.get(4, 4), None)
2208 self.failUnlessEqual(ds2.len(), 4)
2209 self.failUnlessEqual(list(ds2._dump()), [2,3,4,5])
2210 self.failUnlessEqual(sum([len(d) for (s,d) in ds2.get_chunks()]), 4)
2211 self.failUnlessEqual(ds2.get(0, 4), None)
2212 self.failUnlessEqual(ds2.pop(0, 4), None)
2213 self.failUnlessEqual(ds2.pop(2, 3), "fou")
2214 self.failUnlessEqual(sum([len(d) for (s,d) in ds2.get_chunks()]), 1)
2215 self.failUnlessEqual(ds2.get(2, 3), None)
2216 self.failUnlessEqual(ds2.get(5, 1), "r")
2217 self.failUnlessEqual(ds.get(2, 3), "fou")
2218 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 4)
2221 self.failUnlessEqual(ds.len(), 6)
2222 self.failUnlessEqual(list(ds._dump()), [0,1,2,3,4,5])
2223 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 6)
2224 self.failUnlessEqual(ds.get(0, 4), "23fo")
2225 self.failUnlessEqual(ds.pop(0, 4), "23fo")
2226 self.failUnlessEqual(sum([len(d) for (s,d) in ds.get_chunks()]), 2)
2227 self.failUnlessEqual(ds.get(0, 4), None)
2228 self.failUnlessEqual(ds.pop(0, 4), None)
2233 self.failUnlessEqual(ds.get(2, 4), "fear")
2238 self.failUnlessEqual(ds.get(2L, 4L), "fear")
2241 def do_scan(self, klass):
2242 # do a test with gaps and spans of size 1 and 2
2243 # left=(1,11) * right=(1,11) * gapsize=(1,2)
2244 # 111, 112, 121, 122, 211, 212, 221, 222
2253 # 11 1 1 11 11 11 1 1 111
2254 # 0123456789012345678901234567
2255 # abcdefghijklmnopqrstuvwxyz-=
2256 pieces = [(1, "bc"),
2266 p_elements = set([1,2,4,7,9,10,12,13,16,17,20,22,25,26,27])
2267 S = "abcdefghijklmnopqrstuvwxyz-="
2268 # TODO: when adding data, add capital letters, to make sure we aren't
2269 # just leaving the old data in place
2273 for start, data in pieces:
2278 d = "".join([((i not in p) and " " or S[i]) for i in range(l)])
2282 for start in range(0, l):
2283 for end in range(start+1, l):
2284 # add [start-end) to the baseline
2285 which = "%d-%d" % (start, end-1)
2286 p_added = set(range(start, end))
2290 print dump(b), which
2291 add = klass(); add.add(start, S[start:end])
2293 b.add(start, S[start:end])
2296 # check that the new span is there
2297 d = b.get(start, end-start)
2298 self.failUnlessEqual(d, S[start:end], which)
2299 # check that all the original pieces are still there
2300 for t_start, t_data in pieces:
2302 self.failUnlessEqual(b.get(t_start, t_len),
2303 S[t_start:t_start+t_len],
2304 "%s %d+%d" % (which, t_start, t_len))
2305 # check that a lot of subspans are mostly correct
2306 for t_start in range(l):
2307 for t_len in range(1,4):
2308 d = b.get(t_start, t_len)
2310 which2 = "%s+(%d-%d)" % (which, t_start,
2312 self.failUnlessEqual(d, S[t_start:t_start+t_len],
2314 # check that removing a subspan gives the right value
2316 b2.remove(t_start, t_len)
2317 removed = set(range(t_start, t_start+t_len))
2319 exp = (((i in p_elements) or (i in p_added))
2320 and (i not in removed))
2321 which2 = "%s-(%d-%d)" % (which, t_start,
2323 self.failUnlessEqual(bool(b2.get(i, 1)), exp,
2326 def test_test(self):
2327 self.do_basic(SimpleDataSpans)
2328 self.do_scan(SimpleDataSpans)
2330 def test_basic(self):
2331 self.do_basic(DataSpans)
2332 self.do_scan(DataSpans)
2334 def test_random(self):
2335 # attempt to increase coverage of corner cases by comparing behavior
2336 # of a simple-but-slow model implementation against the
2337 # complex-but-fast actual implementation, in a large number of random
2339 S1 = SimpleDataSpans
2341 s1 = S1(); s2 = S2()
2343 def _randstr(length, seed):
2346 while created < length:
2347 piece = _hash(seed + str(created)).hexdigest()
2348 pieces.append(piece)
2349 created += len(piece)
2350 return "".join(pieces)[:length]
2351 def _create(subseed):
2352 ns1 = S1(); ns2 = S2()
2354 what = _hash(subseed+str(i)).hexdigest()
2355 start = int(what[2:4], 16)
2356 length = max(1,int(what[5:6], 16))
2357 ns1.add(start, _randstr(length, what[7:9]));
2358 ns2.add(start, _randstr(length, what[7:9]))
2362 for i in range(1000):
2363 what = _hash(seed+str(i)).hexdigest()
2366 start = int(what[2:4], 16)
2367 length = max(1,int(what[5:6], 16))
2370 if subop in "0123456":
2371 s1 = S1(); s2 = S2()
2373 s1, s2 = _create(what[7:11])
2374 #print "s2 = %s" % list(s2._dump())
2375 elif op in "123456":
2376 #print "s2.add(%d,%d)" % (start, length)
2377 s1.add(start, _randstr(length, what[7:9]));
2378 s2.add(start, _randstr(length, what[7:9]))
2379 elif op in "789abc":
2380 #print "s2.remove(%d,%d)" % (start, length)
2381 s1.remove(start, length); s2.remove(start, length)
2383 #print "s2.pop(%d,%d)" % (start, length)
2384 d1 = s1.pop(start, length); d2 = s2.pop(start, length)
2385 self.failUnlessEqual(d1, d2)
2386 #print "s1 now %s" % list(s1._dump())
2387 #print "s2 now %s" % list(s2._dump())
2388 self.failUnlessEqual(s1.len(), s2.len())
2389 self.failUnlessEqual(list(s1._dump()), list(s2._dump()))
2390 for j in range(100):
2391 what = _hash(what[12:14]+str(j)).hexdigest()
2392 start = int(what[2:4], 16)
2393 length = max(1, int(what[5:6], 16))
2394 d1 = s1.get(start, length); d2 = s2.get(start, length)
2395 self.failUnlessEqual(d1, d2, "%d+%d" % (start, length))