3 from zope.interface import implements
4 from twisted.trial import unittest
5 from twisted.internet import defer
6 from twisted.internet.interfaces import IConsumer
7 from allmydata import uri, dirnode
8 from allmydata.client import Client
9 from allmydata.immutable import upload
10 from allmydata.interfaces import IImmutableFileNode, IMutableFileNode, \
11 ExistingChildError, NoSuchChildError, MustNotBeUnknownRWError, \
12 MustBeDeepImmutableError, MustBeReadonlyError, \
13 IDeepCheckResults, IDeepCheckAndRepairResults
14 from allmydata.mutable.filenode import MutableFileNode
15 from allmydata.mutable.common import UncoordinatedWriteError
16 from allmydata.util import hashutil, base32
17 from allmydata.util.netstring import split_netstring
18 from allmydata.monitor import Monitor
19 from allmydata.test.common import make_chk_file_uri, make_mutable_file_uri, \
21 from allmydata.test.no_network import GridTestMixin
22 from allmydata.unknown import UnknownNode, strip_prefix_for_ro
23 from allmydata.nodemaker import NodeMaker
24 from base64 import b32decode
25 import allmydata.test.common_util as testutil
29 def registerProducer(self, producer, streaming):
30 self.producer = producer
31 self.producer.resumeProducing()
33 def unregisterProducer(self):
35 def write(self, data):
36 assert not hasattr(self, 'data')
38 self.producer.resumeProducing()
40 setup_py_uri = "URI:CHK:n7r3m6wmomelk4sep3kw5cvduq:os7ijw5c3maek7pg65e5254k2fzjflavtpejjyhshpsxuqzhcwwq:3:20:14861"
41 one_uri = "URI:LIT:n5xgk" # LIT for "one"
42 mut_write_uri = "URI:SSK:vfvcbdfbszyrsaxchgevhmmlii:euw4iw7bbnkrrwpzuburbhppuxhc3gwxv26f6imekhz7zyw2ojnq"
43 empty_litdir_uri = "URI:DIR2-LIT:"
44 tiny_litdir_uri = "URI:DIR2-LIT:gqytunj2onug64tufqzdcosvkjetutcjkq5gw4tvm5vwszdgnz5hgyzufqydulbshj5x2lbm" # contains one child which is itself also LIT
45 mut_read_uri = "URI:SSK-RO:jf6wkflosyvntwxqcdo7a54jvm:euw4iw7bbnkrrwpzuburbhppuxhc3gwxv26f6imekhz7zyw2ojnq"
46 future_write_uri = "x-tahoe-crazy://I_am_from_the_future."
47 future_read_uri = "x-tahoe-crazy-readonly://I_am_from_the_future."
49 class Dirnode(GridTestMixin, unittest.TestCase,
50 testutil.ShouldFailMixin, testutil.StallMixin, ErrorMixin):
51 timeout = 240 # It takes longer than 120 seconds on Francois's arm box.
54 self.basedir = "dirnode/Dirnode/test_basic"
57 d = c.create_dirnode()
59 self.failUnless(isinstance(res, dirnode.DirectoryNode))
60 self.failUnless(res.is_mutable())
61 self.failIf(res.is_readonly())
62 self.failIf(res.is_unknown())
63 self.failIf(res.is_allowed_in_immutable_directory())
66 self.failUnless("RW-MUT" in rep)
70 def test_initial_children(self):
71 self.basedir = "dirnode/Dirnode/test_initial_children"
76 kids = {u"one": (nm.create_from_cap(one_uri), {}),
77 u"two": (nm.create_from_cap(setup_py_uri),
78 {"metakey": "metavalue"}),
79 u"mut": (nm.create_from_cap(mut_write_uri, mut_read_uri), {}),
80 u"fut": (nm.create_from_cap(future_write_uri, future_read_uri), {}),
81 u"fro": (nm.create_from_cap(None, future_read_uri), {}),
82 u"empty_litdir": (nm.create_from_cap(empty_litdir_uri), {}),
83 u"tiny_litdir": (nm.create_from_cap(tiny_litdir_uri), {}),
85 d = c.create_dirnode(kids)
88 self.failUnless(isinstance(dn, dirnode.DirectoryNode))
89 self.failUnless(dn.is_mutable())
90 self.failIf(dn.is_readonly())
91 self.failIf(dn.is_unknown())
92 self.failIf(dn.is_allowed_in_immutable_directory())
95 self.failUnless("RW-MUT" in rep)
97 d.addCallback(_created)
99 def _check_kids(children):
100 self.failUnlessEqual(set(children.keys()),
101 set([u"one", u"two", u"mut", u"fut", u"fro", u"empty_litdir", u"tiny_litdir"]))
102 one_node, one_metadata = children[u"one"]
103 two_node, two_metadata = children[u"two"]
104 mut_node, mut_metadata = children[u"mut"]
105 fut_node, fut_metadata = children[u"fut"]
106 fro_node, fro_metadata = children[u"fro"]
107 emptylit_node, emptylit_metadata = children[u"empty_litdir"]
108 tinylit_node, tinylit_metadata = children[u"tiny_litdir"]
110 self.failUnlessEqual(one_node.get_size(), 3)
111 self.failUnlessEqual(one_node.get_uri(), one_uri)
112 self.failUnlessEqual(one_node.get_readonly_uri(), one_uri)
113 self.failUnless(isinstance(one_metadata, dict), one_metadata)
115 self.failUnlessEqual(two_node.get_size(), 14861)
116 self.failUnlessEqual(two_node.get_uri(), setup_py_uri)
117 self.failUnlessEqual(two_node.get_readonly_uri(), setup_py_uri)
118 self.failUnlessEqual(two_metadata["metakey"], "metavalue")
120 self.failUnlessEqual(mut_node.get_uri(), mut_write_uri)
121 self.failUnlessEqual(mut_node.get_readonly_uri(), mut_read_uri)
122 self.failUnless(isinstance(mut_metadata, dict), mut_metadata)
124 self.failUnless(fut_node.is_unknown())
125 self.failUnlessEqual(fut_node.get_uri(), future_write_uri)
126 self.failUnlessEqual(fut_node.get_readonly_uri(), "ro." + future_read_uri)
127 self.failUnless(isinstance(fut_metadata, dict), fut_metadata)
129 self.failUnless(fro_node.is_unknown())
130 self.failUnlessEqual(fro_node.get_uri(), "ro." + future_read_uri)
131 self.failUnlessEqual(fut_node.get_readonly_uri(), "ro." + future_read_uri)
132 self.failUnless(isinstance(fro_metadata, dict), fro_metadata)
134 self.failIf(emptylit_node.is_unknown())
135 self.failUnlessEqual(emptylit_node.get_storage_index(), None)
136 self.failIf(tinylit_node.is_unknown())
137 self.failUnlessEqual(tinylit_node.get_storage_index(), None)
139 d2 = defer.succeed(None)
140 d2.addCallback(lambda ignored: emptylit_node.list())
141 d2.addCallback(lambda children: self.failUnlessEqual(children, {}))
142 d2.addCallback(lambda ignored: tinylit_node.list())
143 d2.addCallback(lambda children: self.failUnlessEqual(set(children.keys()),
145 d2.addCallback(lambda ignored: tinylit_node.list())
146 d2.addCallback(lambda children: children[u"short"][0].read(MemAccum()))
147 d2.addCallback(lambda accum: self.failUnlessEqual(accum.data, "The end."))
150 d.addCallback(_check_kids)
152 d.addCallback(lambda ign: nm.create_new_mutable_directory(kids))
153 d.addCallback(lambda dn: dn.list())
154 d.addCallback(_check_kids)
156 bad_future_node = UnknownNode(future_write_uri, None)
157 bad_kids1 = {u"one": (bad_future_node, {})}
158 # This should fail because we don't know how to diminish the future_write_uri
159 # cap (given in a write slot and not prefixed with "ro." or "imm.") to a readcap.
160 d.addCallback(lambda ign:
161 self.shouldFail(MustNotBeUnknownRWError, "bad_kids1",
162 "cannot attach unknown",
163 nm.create_new_mutable_directory,
165 bad_kids2 = {u"one": (nm.create_from_cap(one_uri), None)}
166 d.addCallback(lambda ign:
167 self.shouldFail(AssertionError, "bad_kids2",
168 "requires metadata to be a dict",
169 nm.create_new_mutable_directory,
173 def test_immutable(self):
174 self.basedir = "dirnode/Dirnode/test_immutable"
176 c = self.g.clients[0]
179 kids = {u"one": (nm.create_from_cap(one_uri), {}),
180 u"two": (nm.create_from_cap(setup_py_uri),
181 {"metakey": "metavalue"}),
182 u"fut": (nm.create_from_cap(None, future_read_uri), {}),
183 u"empty_litdir": (nm.create_from_cap(empty_litdir_uri), {}),
184 u"tiny_litdir": (nm.create_from_cap(tiny_litdir_uri), {}),
186 d = c.create_immutable_dirnode(kids)
189 self.failUnless(isinstance(dn, dirnode.DirectoryNode))
190 self.failIf(dn.is_mutable())
191 self.failUnless(dn.is_readonly())
192 self.failIf(dn.is_unknown())
193 self.failUnless(dn.is_allowed_in_immutable_directory())
196 self.failUnless("RO-IMM" in rep)
198 self.failUnlessIn("CHK", cap.to_string())
201 d.addCallback(_created)
203 def _check_kids(children):
204 self.failUnlessEqual(set(children.keys()),
205 set([u"one", u"two", u"fut", u"empty_litdir", u"tiny_litdir"]))
206 one_node, one_metadata = children[u"one"]
207 two_node, two_metadata = children[u"two"]
208 fut_node, fut_metadata = children[u"fut"]
209 emptylit_node, emptylit_metadata = children[u"empty_litdir"]
210 tinylit_node, tinylit_metadata = children[u"tiny_litdir"]
212 self.failUnlessEqual(one_node.get_size(), 3)
213 self.failUnlessEqual(one_node.get_uri(), one_uri)
214 self.failUnlessEqual(one_node.get_readonly_uri(), one_uri)
215 self.failUnless(isinstance(one_metadata, dict), one_metadata)
217 self.failUnlessEqual(two_node.get_size(), 14861)
218 self.failUnlessEqual(two_node.get_uri(), setup_py_uri)
219 self.failUnlessEqual(two_node.get_readonly_uri(), setup_py_uri)
220 self.failUnlessEqual(two_metadata["metakey"], "metavalue")
222 self.failUnless(fut_node.is_unknown())
223 self.failUnlessEqual(fut_node.get_uri(), "imm." + future_read_uri)
224 self.failUnlessEqual(fut_node.get_readonly_uri(), "imm." + future_read_uri)
225 self.failUnless(isinstance(fut_metadata, dict), fut_metadata)
227 self.failIf(emptylit_node.is_unknown())
228 self.failUnlessEqual(emptylit_node.get_storage_index(), None)
229 self.failIf(tinylit_node.is_unknown())
230 self.failUnlessEqual(tinylit_node.get_storage_index(), None)
232 d2 = defer.succeed(None)
233 d2.addCallback(lambda ignored: emptylit_node.list())
234 d2.addCallback(lambda children: self.failUnlessEqual(children, {}))
235 d2.addCallback(lambda ignored: tinylit_node.list())
236 d2.addCallback(lambda children: self.failUnlessEqual(set(children.keys()),
238 d2.addCallback(lambda ignored: tinylit_node.list())
239 d2.addCallback(lambda children: children[u"short"][0].read(MemAccum()))
240 d2.addCallback(lambda accum: self.failUnlessEqual(accum.data, "The end."))
243 d.addCallback(_check_kids)
245 d.addCallback(lambda ign: nm.create_from_cap(self.cap.to_string()))
246 d.addCallback(lambda dn: dn.list())
247 d.addCallback(_check_kids)
249 bad_future_node1 = UnknownNode(future_write_uri, None)
250 bad_kids1 = {u"one": (bad_future_node1, {})}
251 d.addCallback(lambda ign:
252 self.shouldFail(MustNotBeUnknownRWError, "bad_kids1",
253 "cannot attach unknown",
254 c.create_immutable_dirnode,
256 bad_future_node2 = UnknownNode(future_write_uri, future_read_uri)
257 bad_kids2 = {u"one": (bad_future_node2, {})}
258 d.addCallback(lambda ign:
259 self.shouldFail(MustBeDeepImmutableError, "bad_kids2",
261 c.create_immutable_dirnode,
263 bad_kids3 = {u"one": (nm.create_from_cap(one_uri), None)}
264 d.addCallback(lambda ign:
265 self.shouldFail(AssertionError, "bad_kids3",
266 "requires metadata to be a dict",
267 c.create_immutable_dirnode,
269 bad_kids4 = {u"one": (nm.create_from_cap(mut_write_uri), {})}
270 d.addCallback(lambda ign:
271 self.shouldFail(MustBeDeepImmutableError, "bad_kids4",
273 c.create_immutable_dirnode,
275 bad_kids5 = {u"one": (nm.create_from_cap(mut_read_uri), {})}
276 d.addCallback(lambda ign:
277 self.shouldFail(MustBeDeepImmutableError, "bad_kids5",
279 c.create_immutable_dirnode,
281 d.addCallback(lambda ign: c.create_immutable_dirnode({}))
282 def _created_empty(dn):
283 self.failUnless(isinstance(dn, dirnode.DirectoryNode))
284 self.failIf(dn.is_mutable())
285 self.failUnless(dn.is_readonly())
286 self.failIf(dn.is_unknown())
287 self.failUnless(dn.is_allowed_in_immutable_directory())
290 self.failUnless("RO-IMM" in rep)
292 self.failUnlessIn("LIT", cap.to_string())
293 self.failUnlessEqual(cap.to_string(), "URI:DIR2-LIT:")
296 d.addCallback(_created_empty)
297 d.addCallback(lambda kids: self.failUnlessEqual(kids, {}))
298 smallkids = {u"o": (nm.create_from_cap(one_uri), {})}
299 d.addCallback(lambda ign: c.create_immutable_dirnode(smallkids))
300 def _created_small(dn):
301 self.failUnless(isinstance(dn, dirnode.DirectoryNode))
302 self.failIf(dn.is_mutable())
303 self.failUnless(dn.is_readonly())
304 self.failIf(dn.is_unknown())
305 self.failUnless(dn.is_allowed_in_immutable_directory())
308 self.failUnless("RO-IMM" in rep)
310 self.failUnlessIn("LIT", cap.to_string())
311 self.failUnlessEqual(cap.to_string(),
312 "URI:DIR2-LIT:gi4tumj2n4wdcmz2kvjesosmjfkdu3rvpbtwwlbqhiwdeot3puwcy")
315 d.addCallback(_created_small)
316 d.addCallback(lambda kids: self.failUnlessEqual(kids.keys(), [u"o"]))
318 # now test n.create_subdirectory(mutable=False)
319 d.addCallback(lambda ign: c.create_dirnode())
321 d = n.create_subdirectory(u"subdir", kids, mutable=False)
322 d.addCallback(lambda sd: sd.list())
323 d.addCallback(_check_kids)
324 d.addCallback(lambda ign: n.list())
325 d.addCallback(lambda children:
326 self.failUnlessEqual(children.keys(), [u"subdir"]))
327 d.addCallback(lambda ign: n.get(u"subdir"))
328 d.addCallback(lambda sd: sd.list())
329 d.addCallback(_check_kids)
330 d.addCallback(lambda ign: n.get(u"subdir"))
331 d.addCallback(lambda sd: self.failIf(sd.is_mutable()))
332 bad_kids = {u"one": (nm.create_from_cap(mut_write_uri), {})}
333 d.addCallback(lambda ign:
334 self.shouldFail(MustBeDeepImmutableError, "YZ",
336 n.create_subdirectory,
337 u"sub2", bad_kids, mutable=False))
339 d.addCallback(_made_parent)
342 def test_spaces_are_stripped_on_the_way_out(self):
343 self.basedir = "dirnode/Dirnode/test_spaces_are_stripped_on_the_way_out"
345 c = self.g.clients[0]
348 # This test checks that any trailing spaces in URIs are retained in the
349 # encoded directory, but stripped when we get them out of the directory.
350 # See ticket #925 for why we want that.
352 stripped_write_uri = "lafs://from_the_future\t"
353 stripped_read_uri = "lafs://readonly_from_the_future\t"
354 spacedout_write_uri = stripped_write_uri + " "
355 spacedout_read_uri = stripped_read_uri + " "
357 child = nm.create_from_cap(spacedout_write_uri, spacedout_read_uri)
358 self.failUnlessEqual(child.get_write_uri(), spacedout_write_uri)
359 self.failUnlessEqual(child.get_readonly_uri(), "ro." + spacedout_read_uri)
361 kids = {u"child": (child, {})}
362 d = c.create_dirnode(kids)
365 self.failUnless(isinstance(dn, dirnode.DirectoryNode))
366 self.failUnless(dn.is_mutable())
367 self.failIf(dn.is_readonly())
369 self.cap = dn.get_cap()
371 return dn._node.download_best_version()
372 d.addCallback(_created)
374 def _check_data(data):
375 # Decode the netstring representation of the directory to check that the
376 # spaces are retained when the URIs are stored.
379 while position < len(data):
380 entries, position = split_netstring(data, 1, position)
382 (name_utf8, ro_uri, rwcapdata, metadata_s), subpos = split_netstring(entry, 4)
383 name = name_utf8.decode("utf-8")
384 rw_uri = self.rootnode._decrypt_rwcapdata(rwcapdata)
385 self.failUnless(name in kids)
386 (expected_child, ign) = kids[name]
387 self.failUnlessEqual(rw_uri, expected_child.get_write_uri())
388 self.failUnlessEqual("ro." + ro_uri, expected_child.get_readonly_uri())
391 self.failUnlessEqual(numkids, 1)
392 return self.rootnode.list()
393 d.addCallback(_check_data)
395 # Now when we use the real directory listing code, the trailing spaces
396 # should have been stripped (and "ro." should have been prepended to the
397 # ro_uri, since it's unknown).
398 def _check_kids(children):
399 self.failUnlessEqual(set(children.keys()), set([u"child"]))
400 child_node, child_metadata = children[u"child"]
402 self.failUnlessEqual(child_node.get_write_uri(), stripped_write_uri)
403 self.failUnlessEqual(child_node.get_readonly_uri(), "ro." + stripped_read_uri)
404 d.addCallback(_check_kids)
406 d.addCallback(lambda ign: nm.create_from_cap(self.cap.to_string()))
407 d.addCallback(lambda n: n.list())
408 d.addCallback(_check_kids) # again with dirnode recreated from cap
411 def test_check(self):
412 self.basedir = "dirnode/Dirnode/test_check"
414 c = self.g.clients[0]
415 d = c.create_dirnode()
416 d.addCallback(lambda dn: dn.check(Monitor()))
418 self.failUnless(res.is_healthy())
422 def _test_deepcheck_create(self):
423 # create a small tree with a loop, and some non-directories
427 # root/subdir/link -> root
429 c = self.g.clients[0]
430 d = c.create_dirnode()
431 def _created_root(rootnode):
432 self._rootnode = rootnode
433 return rootnode.create_subdirectory(u"subdir")
434 d.addCallback(_created_root)
435 def _created_subdir(subdir):
436 self._subdir = subdir
437 d = subdir.add_file(u"file1", upload.Data("data"*100, None))
438 d.addCallback(lambda res: subdir.set_node(u"link", self._rootnode))
439 d.addCallback(lambda res: c.create_dirnode())
440 d.addCallback(lambda dn:
441 self._rootnode.set_uri(u"rodir",
443 dn.get_readonly_uri()))
445 d.addCallback(_created_subdir)
447 return self._rootnode
451 def test_deepcheck(self):
452 self.basedir = "dirnode/Dirnode/test_deepcheck"
454 d = self._test_deepcheck_create()
455 d.addCallback(lambda rootnode: rootnode.start_deep_check().when_done())
456 def _check_results(r):
457 self.failUnless(IDeepCheckResults.providedBy(r))
459 self.failUnlessEqual(c,
460 {"count-objects-checked": 4,
461 "count-objects-healthy": 4,
462 "count-objects-unhealthy": 0,
463 "count-objects-unrecoverable": 0,
464 "count-corrupt-shares": 0,
466 self.failIf(r.get_corrupt_shares())
467 self.failUnlessEqual(len(r.get_all_results()), 4)
468 d.addCallback(_check_results)
471 def test_deepcheck_and_repair(self):
472 self.basedir = "dirnode/Dirnode/test_deepcheck_and_repair"
474 d = self._test_deepcheck_create()
475 d.addCallback(lambda rootnode:
476 rootnode.start_deep_check_and_repair().when_done())
477 def _check_results(r):
478 self.failUnless(IDeepCheckAndRepairResults.providedBy(r))
480 self.failUnlessEqual(c,
481 {"count-objects-checked": 4,
482 "count-objects-healthy-pre-repair": 4,
483 "count-objects-unhealthy-pre-repair": 0,
484 "count-objects-unrecoverable-pre-repair": 0,
485 "count-corrupt-shares-pre-repair": 0,
486 "count-objects-healthy-post-repair": 4,
487 "count-objects-unhealthy-post-repair": 0,
488 "count-objects-unrecoverable-post-repair": 0,
489 "count-corrupt-shares-post-repair": 0,
490 "count-repairs-attempted": 0,
491 "count-repairs-successful": 0,
492 "count-repairs-unsuccessful": 0,
494 self.failIf(r.get_corrupt_shares())
495 self.failIf(r.get_remaining_corrupt_shares())
496 self.failUnlessEqual(len(r.get_all_results()), 4)
497 d.addCallback(_check_results)
500 def _mark_file_bad(self, rootnode):
501 self.delete_shares_numbered(rootnode.get_uri(), [0])
504 def test_deepcheck_problems(self):
505 self.basedir = "dirnode/Dirnode/test_deepcheck_problems"
507 d = self._test_deepcheck_create()
508 d.addCallback(lambda rootnode: self._mark_file_bad(rootnode))
509 d.addCallback(lambda rootnode: rootnode.start_deep_check().when_done())
510 def _check_results(r):
512 self.failUnlessEqual(c,
513 {"count-objects-checked": 4,
514 "count-objects-healthy": 3,
515 "count-objects-unhealthy": 1,
516 "count-objects-unrecoverable": 0,
517 "count-corrupt-shares": 0,
519 #self.failUnlessEqual(len(r.get_problems()), 1) # TODO
520 d.addCallback(_check_results)
523 def test_readonly(self):
524 self.basedir = "dirnode/Dirnode/test_readonly"
526 c = self.g.clients[0]
528 filecap = make_chk_file_uri(1234)
529 filenode = nm.create_from_cap(filecap)
530 uploadable = upload.Data("some data", convergence="some convergence string")
532 d = c.create_dirnode()
534 d2 = rw_dn.set_uri(u"child", filecap, filecap)
535 d2.addCallback(lambda res: rw_dn)
537 d.addCallback(_created)
540 ro_uri = rw_dn.get_readonly_uri()
541 ro_dn = c.create_node_from_uri(ro_uri)
542 self.failUnless(ro_dn.is_readonly())
543 self.failUnless(ro_dn.is_mutable())
544 self.failIf(ro_dn.is_unknown())
545 self.failIf(ro_dn.is_allowed_in_immutable_directory())
548 self.shouldFail(dirnode.NotWriteableError, "set_uri ro", None,
549 ro_dn.set_uri, u"newchild", filecap, filecap)
550 self.shouldFail(dirnode.NotWriteableError, "set_uri ro", None,
551 ro_dn.set_node, u"newchild", filenode)
552 self.shouldFail(dirnode.NotWriteableError, "set_nodes ro", None,
553 ro_dn.set_nodes, { u"newchild": (filenode, None) })
554 self.shouldFail(dirnode.NotWriteableError, "set_uri ro", None,
555 ro_dn.add_file, u"newchild", uploadable)
556 self.shouldFail(dirnode.NotWriteableError, "set_uri ro", None,
557 ro_dn.delete, u"child")
558 self.shouldFail(dirnode.NotWriteableError, "set_uri ro", None,
559 ro_dn.create_subdirectory, u"newchild")
560 self.shouldFail(dirnode.NotWriteableError, "set_metadata_for ro", None,
561 ro_dn.set_metadata_for, u"child", {})
562 self.shouldFail(dirnode.NotWriteableError, "set_uri ro", None,
563 ro_dn.move_child_to, u"child", rw_dn)
564 self.shouldFail(dirnode.NotWriteableError, "set_uri ro", None,
565 rw_dn.move_child_to, u"child", ro_dn)
567 d.addCallback(_ready)
568 def _listed(children):
569 self.failUnless(u"child" in children)
570 d.addCallback(_listed)
573 def failUnlessGreaterThan(self, a, b):
574 self.failUnless(a > b, "%r should be > %r" % (a, b))
576 def failUnlessGreaterOrEqualThan(self, a, b):
577 self.failUnless(a >= b, "%r should be >= %r" % (a, b))
579 def test_create(self):
580 self.basedir = "dirnode/Dirnode/test_create"
582 c = self.g.clients[0]
584 self.expected_manifest = []
585 self.expected_verifycaps = set()
586 self.expected_storage_indexes = set()
588 d = c.create_dirnode()
592 self.failUnless(n.is_mutable())
595 self.failUnless(u.startswith("URI:DIR2:"), u)
596 u_ro = n.get_readonly_uri()
597 self.failUnless(u_ro.startswith("URI:DIR2-RO:"), u_ro)
598 u_v = n.get_verify_cap().to_string()
599 self.failUnless(u_v.startswith("URI:DIR2-Verifier:"), u_v)
600 u_r = n.get_repair_cap().to_string()
601 self.failUnlessEqual(u_r, u)
602 self.expected_manifest.append( ((), u) )
603 self.expected_verifycaps.add(u_v)
604 si = n.get_storage_index()
605 self.expected_storage_indexes.add(base32.b2a(si))
606 expected_si = n._uri.get_storage_index()
607 self.failUnlessEqual(si, expected_si)
610 d.addCallback(lambda res: self.failUnlessEqual(res, {}))
611 d.addCallback(lambda res: n.has_child(u"missing"))
612 d.addCallback(lambda res: self.failIf(res))
614 fake_file_uri = make_mutable_file_uri()
615 other_file_uri = make_mutable_file_uri()
616 m = c.nodemaker.create_from_cap(fake_file_uri)
617 ffu_v = m.get_verify_cap().to_string()
618 self.expected_manifest.append( ((u"child",) , m.get_uri()) )
619 self.expected_verifycaps.add(ffu_v)
620 self.expected_storage_indexes.add(base32.b2a(m.get_storage_index()))
621 d.addCallback(lambda res: n.set_uri(u"child",
622 fake_file_uri, fake_file_uri))
623 d.addCallback(lambda res:
624 self.shouldFail(ExistingChildError, "set_uri-no",
625 "child 'child' already exists",
627 other_file_uri, other_file_uri,
632 d.addCallback(lambda res: n.create_subdirectory(u"subdir"))
636 # /subdir = directory
637 def _created(subdir):
638 self.failUnless(isinstance(subdir, dirnode.DirectoryNode))
640 new_v = subdir.get_verify_cap().to_string()
641 assert isinstance(new_v, str)
642 self.expected_manifest.append( ((u"subdir",), subdir.get_uri()) )
643 self.expected_verifycaps.add(new_v)
644 si = subdir.get_storage_index()
645 self.expected_storage_indexes.add(base32.b2a(si))
646 d.addCallback(_created)
648 d.addCallback(lambda res:
649 self.shouldFail(ExistingChildError, "mkdir-no",
650 "child 'subdir' already exists",
651 n.create_subdirectory, u"subdir",
654 d.addCallback(lambda res: n.list())
655 d.addCallback(lambda children:
656 self.failUnlessEqual(set(children.keys()),
657 set([u"child", u"subdir"])))
659 d.addCallback(lambda res: n.start_deep_stats().when_done())
660 def _check_deepstats(stats):
661 self.failUnless(isinstance(stats, dict))
662 expected = {"count-immutable-files": 0,
663 "count-mutable-files": 1,
664 "count-literal-files": 0,
666 "count-directories": 2,
667 "size-immutable-files": 0,
668 "size-literal-files": 0,
669 #"size-directories": 616, # varies
670 #"largest-directory": 616,
671 "largest-directory-children": 2,
672 "largest-immutable-file": 0,
674 for k,v in expected.iteritems():
675 self.failUnlessEqual(stats[k], v,
676 "stats[%s] was %s, not %s" %
678 self.failUnless(stats["size-directories"] > 500,
679 stats["size-directories"])
680 self.failUnless(stats["largest-directory"] > 500,
681 stats["largest-directory"])
682 self.failUnlessEqual(stats["size-files-histogram"], [])
683 d.addCallback(_check_deepstats)
685 d.addCallback(lambda res: n.build_manifest().when_done())
686 def _check_manifest(res):
687 manifest = res["manifest"]
688 self.failUnlessEqual(sorted(manifest),
689 sorted(self.expected_manifest))
691 _check_deepstats(stats)
692 self.failUnlessEqual(self.expected_verifycaps,
694 self.failUnlessEqual(self.expected_storage_indexes,
695 res["storage-index"])
696 d.addCallback(_check_manifest)
698 def _add_subsubdir(res):
699 return self.subdir.create_subdirectory(u"subsubdir")
700 d.addCallback(_add_subsubdir)
703 # /subdir = directory
704 # /subdir/subsubdir = directory
705 d.addCallback(lambda res: n.get_child_at_path(u"subdir/subsubdir"))
706 d.addCallback(lambda subsubdir:
707 self.failUnless(isinstance(subsubdir,
708 dirnode.DirectoryNode)))
709 d.addCallback(lambda res: n.get_child_at_path(u""))
710 d.addCallback(lambda res: self.failUnlessEqual(res.get_uri(),
713 d.addCallback(lambda res: n.get_metadata_for(u"child"))
714 d.addCallback(lambda metadata:
715 self.failUnlessEqual(set(metadata.keys()),
716 set(["tahoe", "ctime", "mtime"])))
718 d.addCallback(lambda res:
719 self.shouldFail(NoSuchChildError, "gcamap-no",
721 n.get_child_and_metadata_at_path,
723 d.addCallback(lambda res:
724 n.get_child_and_metadata_at_path(u""))
725 def _check_child_and_metadata1(res):
726 child, metadata = res
727 self.failUnless(isinstance(child, dirnode.DirectoryNode))
728 # edge-metadata needs at least one path segment
729 self.failUnlessEqual(set(metadata.keys()), set([]))
730 d.addCallback(_check_child_and_metadata1)
731 d.addCallback(lambda res:
732 n.get_child_and_metadata_at_path(u"child"))
734 def _check_child_and_metadata2(res):
735 child, metadata = res
736 self.failUnlessEqual(child.get_uri(),
738 self.failUnlessEqual(set(metadata.keys()),
739 set(["tahoe", "ctime", "mtime"]))
740 d.addCallback(_check_child_and_metadata2)
742 d.addCallback(lambda res:
743 n.get_child_and_metadata_at_path(u"subdir/subsubdir"))
744 def _check_child_and_metadata3(res):
745 child, metadata = res
746 self.failUnless(isinstance(child, dirnode.DirectoryNode))
747 self.failUnlessEqual(set(metadata.keys()),
748 set(["tahoe", "ctime", "mtime"]))
749 d.addCallback(_check_child_and_metadata3)
752 # it should be possible to add a child without any metadata
753 d.addCallback(lambda res: n.set_uri(u"c2",
754 fake_file_uri, fake_file_uri,
756 d.addCallback(lambda res: n.get_metadata_for(u"c2"))
757 d.addCallback(lambda metadata:
758 self.failUnlessEqual(set(metadata.keys()), set(["tahoe", "ctime", "mtime"])))
760 # You can't override the link timestamps.
761 d.addCallback(lambda res: n.set_uri(u"c2",
762 fake_file_uri, fake_file_uri,
763 { 'tahoe': {'linkcrtime': "bogus"}}))
764 d.addCallback(lambda res: n.get_metadata_for(u"c2"))
765 def _has_good_linkcrtime(metadata):
766 self.failUnless(metadata.has_key('tahoe'))
767 self.failUnless(metadata['tahoe'].has_key('linkcrtime'))
768 self.failIfEqual(metadata['tahoe']['linkcrtime'], 'bogus')
769 d.addCallback(_has_good_linkcrtime)
771 # if we don't set any defaults, the child should get timestamps
772 d.addCallback(lambda res: n.set_uri(u"c3",
773 fake_file_uri, fake_file_uri))
774 d.addCallback(lambda res: n.get_metadata_for(u"c3"))
775 d.addCallback(lambda metadata:
776 self.failUnlessEqual(set(metadata.keys()), set(["tahoe", "ctime", "mtime"])))
778 # or we can add specific metadata at set_uri() time, which
779 # overrides the timestamps
780 d.addCallback(lambda res: n.set_uri(u"c4",
781 fake_file_uri, fake_file_uri,
783 d.addCallback(lambda res: n.get_metadata_for(u"c4"))
784 d.addCallback(lambda metadata:
785 self.failUnless((set(metadata.keys()) == set(["key", "tahoe", "ctime", "mtime"])) and
786 (metadata['key'] == "value"), metadata))
788 d.addCallback(lambda res: n.delete(u"c2"))
789 d.addCallback(lambda res: n.delete(u"c3"))
790 d.addCallback(lambda res: n.delete(u"c4"))
792 # set_node + metadata
793 # it should be possible to add a child without any metadata
794 d.addCallback(lambda res: n.set_node(u"d2", n, {}))
795 d.addCallback(lambda res: c.create_dirnode())
796 d.addCallback(lambda n2:
797 self.shouldFail(ExistingChildError, "set_node-no",
798 "child 'd2' already exists",
799 n.set_node, u"d2", n2,
801 d.addCallback(lambda res: n.get_metadata_for(u"d2"))
802 d.addCallback(lambda metadata:
803 self.failUnlessEqual(set(metadata.keys()), set(["tahoe", "ctime", "mtime"])))
805 # if we don't set any defaults, the child should get timestamps
806 d.addCallback(lambda res: n.set_node(u"d3", n))
807 d.addCallback(lambda res: n.get_metadata_for(u"d3"))
808 d.addCallback(lambda metadata:
809 self.failUnlessEqual(set(metadata.keys()), set(["tahoe", "ctime", "mtime"])))
811 # or we can add specific metadata at set_node() time, which
812 # overrides the timestamps
813 d.addCallback(lambda res: n.set_node(u"d4", n,
815 d.addCallback(lambda res: n.get_metadata_for(u"d4"))
816 d.addCallback(lambda metadata:
817 self.failUnless((set(metadata.keys()) == set(["key", "tahoe", "ctime", "mtime"])) and
818 (metadata["key"] == "value"), metadata))
820 d.addCallback(lambda res: n.delete(u"d2"))
821 d.addCallback(lambda res: n.delete(u"d3"))
822 d.addCallback(lambda res: n.delete(u"d4"))
824 # metadata through set_children()
825 d.addCallback(lambda res:
827 u"e1": (fake_file_uri, fake_file_uri),
828 u"e2": (fake_file_uri, fake_file_uri, {}),
829 u"e3": (fake_file_uri, fake_file_uri,
832 d.addCallback(lambda n2: self.failUnlessIdentical(n2, n))
833 d.addCallback(lambda res:
834 self.shouldFail(ExistingChildError, "set_children-no",
835 "child 'e1' already exists",
837 { u"e1": (other_file_uri,
839 u"new": (other_file_uri,
843 # and 'new' should not have been created
844 d.addCallback(lambda res: n.list())
845 d.addCallback(lambda children: self.failIf(u"new" in children))
846 d.addCallback(lambda res: n.get_metadata_for(u"e1"))
847 d.addCallback(lambda metadata:
848 self.failUnlessEqual(set(metadata.keys()), set(["tahoe", "ctime", "mtime"])))
849 d.addCallback(lambda res: n.get_metadata_for(u"e2"))
850 d.addCallback(lambda metadata:
851 self.failUnlessEqual(set(metadata.keys()), set(["tahoe", "ctime", "mtime"])))
852 d.addCallback(lambda res: n.get_metadata_for(u"e3"))
853 d.addCallback(lambda metadata:
854 self.failUnless((set(metadata.keys()) == set(["key", "tahoe", "ctime", "mtime"])) and
855 (metadata["key"] == "value"), metadata))
857 d.addCallback(lambda res: n.delete(u"e1"))
858 d.addCallback(lambda res: n.delete(u"e2"))
859 d.addCallback(lambda res: n.delete(u"e3"))
861 # metadata through set_nodes()
862 d.addCallback(lambda res:
863 n.set_nodes({ u"f1": (n, None),
865 u"f3": (n, {"key": "value"}),
867 d.addCallback(lambda n2: self.failUnlessIdentical(n2, n))
868 d.addCallback(lambda res:
869 self.shouldFail(ExistingChildError, "set_nodes-no",
870 "child 'f1' already exists",
871 n.set_nodes, { u"f1": (n, None),
872 u"new": (n, None), },
874 # and 'new' should not have been created
875 d.addCallback(lambda res: n.list())
876 d.addCallback(lambda children: self.failIf(u"new" in children))
877 d.addCallback(lambda res: n.get_metadata_for(u"f1"))
878 d.addCallback(lambda metadata:
879 self.failUnlessEqual(set(metadata.keys()), set(["tahoe", "ctime", "mtime"])))
880 d.addCallback(lambda res: n.get_metadata_for(u"f2"))
881 d.addCallback(lambda metadata:
882 self.failUnlessEqual(set(metadata.keys()), set(["tahoe", "ctime", "mtime"])))
883 d.addCallback(lambda res: n.get_metadata_for(u"f3"))
884 d.addCallback(lambda metadata:
885 self.failUnless((set(metadata.keys()) == set(["key", "tahoe", "ctime", "mtime"])) and
886 (metadata["key"] == "value"), metadata))
888 d.addCallback(lambda res: n.delete(u"f1"))
889 d.addCallback(lambda res: n.delete(u"f2"))
890 d.addCallback(lambda res: n.delete(u"f3"))
893 d.addCallback(lambda res:
894 n.set_metadata_for(u"child",
895 {"tags": ["web2.0-compatible"], "tahoe": {"bad": "mojo"}}))
896 d.addCallback(lambda n1: n1.get_metadata_for(u"child"))
897 d.addCallback(lambda metadata:
898 self.failUnless((set(metadata.keys()) == set(["tags", "tahoe", "ctime", "mtime"])) and
899 metadata["tags"] == ["web2.0-compatible"] and
900 "bad" not in metadata["tahoe"], metadata))
903 self._start_timestamp = time.time()
904 d.addCallback(_start)
905 # simplejson-1.7.1 (as shipped on Ubuntu 'gutsy') rounds all
906 # floats to hundredeths (it uses str(num) instead of repr(num)).
907 # simplejson-1.7.3 does not have this bug. To prevent this bug
908 # from causing the test to fail, stall for more than a few
909 # hundrededths of a second.
910 d.addCallback(self.stall, 0.1)
911 d.addCallback(lambda res: n.add_file(u"timestamps",
912 upload.Data("stamp me", convergence="some convergence string")))
913 d.addCallback(self.stall, 0.1)
915 self._stop_timestamp = time.time()
918 d.addCallback(lambda res: n.get_metadata_for(u"timestamps"))
919 def _check_timestamp1(metadata):
920 self.failUnless("ctime" in metadata)
921 self.failUnless("mtime" in metadata)
922 self.failUnlessGreaterOrEqualThan(metadata["ctime"],
923 self._start_timestamp)
924 self.failUnlessGreaterOrEqualThan(self._stop_timestamp,
926 self.failUnlessGreaterOrEqualThan(metadata["mtime"],
927 self._start_timestamp)
928 self.failUnlessGreaterOrEqualThan(self._stop_timestamp,
930 # Our current timestamp rules say that replacing an existing
931 # child should preserve the 'ctime' but update the mtime
932 self._old_ctime = metadata["ctime"]
933 self._old_mtime = metadata["mtime"]
934 d.addCallback(_check_timestamp1)
935 d.addCallback(self.stall, 2.0) # accomodate low-res timestamps
936 d.addCallback(lambda res: n.set_node(u"timestamps", n))
937 d.addCallback(lambda res: n.get_metadata_for(u"timestamps"))
938 def _check_timestamp2(metadata):
939 self.failUnlessEqual(metadata["ctime"], self._old_ctime,
940 "%s != %s" % (metadata["ctime"],
942 self.failUnlessGreaterThan(metadata["mtime"], self._old_mtime)
943 return n.delete(u"timestamps")
944 d.addCallback(_check_timestamp2)
946 # also make sure we can add/update timestamps on a
947 # previously-existing child that didn't have any, since there are
948 # a lot of 0.7.0-generated edges around out there
949 d.addCallback(lambda res: n.set_node(u"no_timestamps", n, {}))
950 d.addCallback(lambda res: n.set_node(u"no_timestamps", n))
951 d.addCallback(lambda res: n.get_metadata_for(u"no_timestamps"))
952 d.addCallback(lambda metadata:
953 self.failUnlessEqual(set(metadata.keys()),
954 set(["tahoe", "ctime", "mtime"])))
955 d.addCallback(lambda res: n.delete(u"no_timestamps"))
957 d.addCallback(lambda res: n.delete(u"subdir"))
958 d.addCallback(lambda old_child:
959 self.failUnlessEqual(old_child.get_uri(),
960 self.subdir.get_uri()))
962 d.addCallback(lambda res: n.list())
963 d.addCallback(lambda children:
964 self.failUnlessEqual(set(children.keys()),
967 uploadable1 = upload.Data("some data", convergence="converge")
968 d.addCallback(lambda res: n.add_file(u"newfile", uploadable1))
969 d.addCallback(lambda newnode:
970 self.failUnless(IImmutableFileNode.providedBy(newnode)))
971 uploadable2 = upload.Data("some data", convergence="stuff")
972 d.addCallback(lambda res:
973 self.shouldFail(ExistingChildError, "add_file-no",
974 "child 'newfile' already exists",
975 n.add_file, u"newfile",
978 d.addCallback(lambda res: n.list())
979 d.addCallback(lambda children:
980 self.failUnlessEqual(set(children.keys()),
981 set([u"child", u"newfile"])))
982 d.addCallback(lambda res: n.get_metadata_for(u"newfile"))
983 d.addCallback(lambda metadata:
984 self.failUnlessEqual(set(metadata.keys()),
985 set(["tahoe", "ctime", "mtime"])))
987 uploadable3 = upload.Data("some data", convergence="converge")
988 d.addCallback(lambda res: n.add_file(u"newfile-metadata",
991 d.addCallback(lambda newnode:
992 self.failUnless(IImmutableFileNode.providedBy(newnode)))
993 d.addCallback(lambda res: n.get_metadata_for(u"newfile-metadata"))
994 d.addCallback(lambda metadata:
995 self.failUnless((set(metadata.keys()) == set(["key", "tahoe", "ctime", "mtime"])) and
996 (metadata['key'] == "value"), metadata))
997 d.addCallback(lambda res: n.delete(u"newfile-metadata"))
999 d.addCallback(lambda res: n.create_subdirectory(u"subdir2"))
1000 def _created2(subdir2):
1001 self.subdir2 = subdir2
1002 # put something in the way, to make sure it gets overwritten
1003 return subdir2.add_file(u"child", upload.Data("overwrite me",
1005 d.addCallback(_created2)
1007 d.addCallback(lambda res:
1008 n.move_child_to(u"child", self.subdir2))
1009 d.addCallback(lambda res: n.list())
1010 d.addCallback(lambda children:
1011 self.failUnlessEqual(set(children.keys()),
1012 set([u"newfile", u"subdir2"])))
1013 d.addCallback(lambda res: self.subdir2.list())
1014 d.addCallback(lambda children:
1015 self.failUnlessEqual(set(children.keys()),
1017 d.addCallback(lambda res: self.subdir2.get(u"child"))
1018 d.addCallback(lambda child:
1019 self.failUnlessEqual(child.get_uri(),
1022 # move it back, using new_child_name=
1023 d.addCallback(lambda res:
1024 self.subdir2.move_child_to(u"child", n, u"newchild"))
1025 d.addCallback(lambda res: n.list())
1026 d.addCallback(lambda children:
1027 self.failUnlessEqual(set(children.keys()),
1028 set([u"newchild", u"newfile",
1030 d.addCallback(lambda res: self.subdir2.list())
1031 d.addCallback(lambda children:
1032 self.failUnlessEqual(set(children.keys()), set([])))
1034 # now make sure that we honor overwrite=False
1035 d.addCallback(lambda res:
1036 self.subdir2.set_uri(u"newchild",
1037 other_file_uri, other_file_uri))
1039 d.addCallback(lambda res:
1040 self.shouldFail(ExistingChildError, "move_child_to-no",
1041 "child 'newchild' already exists",
1042 n.move_child_to, u"newchild",
1045 d.addCallback(lambda res: self.subdir2.get(u"newchild"))
1046 d.addCallback(lambda child:
1047 self.failUnlessEqual(child.get_uri(),
1052 d.addCallback(_then)
1054 d.addErrback(self.explain_error)
1057 def test_update_metadata(self):
1058 (t1, t2, t3) = (626644800, 634745640, 892226160)
1060 md1 = dirnode.update_metadata({}, {"ctime": t1}, t2)
1061 self.failUnlessEqual(md1, {"ctime": t1, "mtime": t2,
1062 "tahoe":{"linkcrtime": t1, "linkmotime": t2}})
1064 md2 = dirnode.update_metadata(md1, {"key": "value", "tahoe": {"bad": "mojo"}}, t3)
1065 self.failUnlessEqual(md2, {"key": "value", "ctime": t1, "mtime": t3,
1066 "tahoe":{"linkcrtime": t1, "linkmotime": t3}})
1068 md3 = dirnode.update_metadata({}, None, t3)
1069 self.failUnlessEqual(md3, {"ctime": t3, "mtime": t3,
1070 "tahoe":{"linkcrtime": t3, "linkmotime": t3}})
1072 def test_create_subdirectory(self):
1073 self.basedir = "dirnode/Dirnode/test_create_subdirectory"
1075 c = self.g.clients[0]
1078 d = c.create_dirnode()
1082 fake_file_uri = make_mutable_file_uri()
1083 other_file_uri = make_mutable_file_uri()
1084 md = {"metakey": "metavalue"}
1085 kids = {u"kid1": (nm.create_from_cap(fake_file_uri), {}),
1086 u"kid2": (nm.create_from_cap(other_file_uri), md),
1088 d = n.create_subdirectory(u"subdir", kids)
1090 d = n.get_child_at_path(u"subdir")
1091 d.addCallback(lambda sub2: self.failUnlessEqual(sub2.get_uri(),
1093 d.addCallback(lambda ign: sub.list())
1095 d.addCallback(_check)
1096 def _check_kids(kids2):
1097 self.failUnlessEqual(set(kids.keys()), set(kids2.keys()))
1098 self.failUnlessEqual(kids2[u"kid2"][1]["metakey"], "metavalue")
1099 d.addCallback(_check_kids)
1101 d.addCallback(_then)
1104 class MinimalFakeMutableFile:
1105 def get_writekey(self):
1108 class Packing(unittest.TestCase):
1109 # This is a base32-encoded representation of the directory tree
1113 # as represented after being fed to _pack_contents.
1114 # We have it here so we can decode it, feed it to
1115 # _unpack_contents, and verify that _unpack_contents
1118 known_tree = "GM4TOORVHJTGS3DFGEWDSNJ2KVJESOSDJBFTU33MPB2GS3LZNVYG6N3GGI3WU5TIORTXC3DOMJ2G4NB2MVWXUZDONBVTE5LNGRZWK2LYN55GY23XGNYXQMTOMZUWU5TENN4DG23ZG5UTO2L2NQ2DO6LFMRWDMZJWGRQTUMZ2GEYDUMJQFQYTIMZ22XZKZORX5XS7CAQCSK3URR6QOHISHRCMGER5LRFSZRNAS5ZSALCS6TWFQAE754IVOIKJVK73WZPP3VUUEDTX3WHTBBZ5YX3CEKHCPG3ZWQLYA4QM6LDRCF7TJQYWLIZHKGN5ROA3AUZPXESBNLQQ6JTC2DBJU2D47IZJTLR3PKZ4RVF57XLPWY7FX7SZV3T6IJ3ORFW37FXUPGOE3ROPFNUX5DCGMAQJ3PGGULBRGM3TU6ZCMN2GS3LFEI5CAMJSGQ3DMNRTHA4TOLRUGI3TKNRWGEWCAITUMFUG6ZJCHIQHWITMNFXGW3LPORUW2ZJCHIQDCMRUGY3DMMZYHE3S4NBSG42TMNRRFQQCE3DJNZVWG4TUNFWWKIR2EAYTENBWGY3DGOBZG4XDIMRXGU3DMML5FQQCE3LUNFWWKIR2EAYTENBWGY3DGOBZG4XDIMRXGU3DMML5FQWDGOJRHI2TUZTJNRSTELBZGQ5FKUSJHJBUQSZ2MFYGKZ3SOBSWQ43IO52WO23CNAZWU3DUGVSWSNTIOE5DK33POVTW4ZLNMNWDK6DHPA2GS2THNF2W25DEN5VGY2LQNFRGG5DKNNRHO5TZPFTWI6LNMRYGQ2LCGJTHM4J2GM5DCMB2GQWDCNBSHKVVQBGRYMACKJ27CVQ6O6B4QPR72RFVTGOZUI76XUSWAX73JRV5PYRHMIFYZIA25MXDPGUGML6M2NMRSG4YD4W4K37ZDYSXHMJ3IUVT4F64YTQQVBJFFFOUC7J7LAB2VFCL5UKKGMR2D3F4EPOYC7UYWQZNR5KXHBSNXLCNBX2SNF22DCXJIHSMEKWEWOG5XCJEVVZ7UW5IB6I64XXQSJ34B5CAYZGZIIMR6LBRGMZTU6ZCMN2GS3LFEI5CAMJSGQ3DMNRTHA4TOLRUGMYDEMJYFQQCE5DBNBXWKIR2EB5SE3DJNZVW233UNFWWKIR2EAYTENBWGY3DGOBZG4XDIMZQGIYTQLBAEJWGS3TLMNZHI2LNMURDUIBRGI2DMNRWGM4DSNZOGQZTAMRRHB6SYIBCNV2GS3LFEI5CAMJSGQ3DMNRTHA4TOLRUGMYDEMJYPUWCYMZZGU5DKOTGNFWGKMZMHE2DUVKSJE5EGSCLHJRW25DDPBYTO2DXPB3GM6DBNYZTI6LJMV3DM2LWNB4TU4LWMNSWW3LKORXWK5DEMN3TI23NNE3WEM3SORRGY5THPA3TKNBUMNZG453BOF2GSZLXMVWWI3DJOFZW623RHIZTUMJQHI2SYMJUGI5BOSHWDPG3WKPAVXCF3XMKA7QVIWPRMWJHDTQHD27AHDCPJWDQENQ5H5ZZILTXQNIXXCIW4LKQABU2GCFRG5FHQN7CHD7HF4EKNRZFIV2ZYQIBM7IQU7F4RGB3XCX3FREPBKQ7UCICHVWPCYFGA6OLH3J45LXQ6GWWICJ3PGWJNLZ7PCRNLAPNYUGU6BENS7OXMBEOOFRIZV3PF2FFWZ5WHDPKXERYP7GNHKRMGEZTOOT3EJRXI2LNMURDUIBRGI2DMNRWGM4DSNZOGQZTGNRSGY4SYIBCORQWQ33FEI5CA6ZCNRUW423NN52GS3LFEI5CAMJSGQ3DMNRTHA4TOLRUGMZTMMRWHEWCAITMNFXGWY3SORUW2ZJCHIQDCMRUGY3DMMZYHE3S4NBTGM3DENRZPUWCAITNORUW2ZJCHIQDCMRUGY3DMMZYHE3S4NBTGM3DENRZPUWCY==="
1120 def test_unpack_and_pack_behavior(self):
1121 known_tree = b32decode(self.known_tree)
1122 nodemaker = NodeMaker(None, None, None,
1124 {"k": 3, "n": 10}, None)
1125 write_uri = "URI:SSK-RO:e3mdrzfwhoq42hy5ubcz6rp3o4:ybyibhnp3vvwuq2vaw2ckjmesgkklfs6ghxleztqidihjyofgw7q"
1126 filenode = nodemaker.create_from_cap(write_uri)
1127 node = dirnode.DirectoryNode(filenode, nodemaker, None)
1128 children = node._unpack_contents(known_tree)
1129 self._check_children(children)
1131 packed_children = node._pack_contents(children)
1132 children = node._unpack_contents(packed_children)
1133 self._check_children(children)
1135 def _check_children(self, children):
1136 # Are all the expected child nodes there?
1137 self.failUnless(children.has_key(u'file1'))
1138 self.failUnless(children.has_key(u'file2'))
1139 self.failUnless(children.has_key(u'file3'))
1141 # Are the metadata for child 3 right?
1142 file3_rocap = "URI:CHK:cmtcxq7hwxvfxan34yiev6ivhy:qvcekmjtoetdcw4kmi7b3rtblvgx7544crnwaqtiewemdliqsokq:3:10:5"
1143 file3_rwcap = "URI:CHK:cmtcxq7hwxvfxan34yiev6ivhy:qvcekmjtoetdcw4kmi7b3rtblvgx7544crnwaqtiewemdliqsokq:3:10:5"
1144 file3_metadata = {'ctime': 1246663897.4336269, 'tahoe': {'linkmotime': 1246663897.4336269, 'linkcrtime': 1246663897.4336269}, 'mtime': 1246663897.4336269}
1145 self.failUnlessEqual(file3_metadata, children[u'file3'][1])
1146 self.failUnlessEqual(file3_rocap,
1147 children[u'file3'][0].get_readonly_uri())
1148 self.failUnlessEqual(file3_rwcap,
1149 children[u'file3'][0].get_uri())
1151 # Are the metadata for child 2 right?
1152 file2_rocap = "URI:CHK:apegrpehshwugkbh3jlt5ei6hq:5oougnemcl5xgx4ijgiumtdojlipibctjkbwvyygdymdphib2fvq:3:10:4"
1153 file2_rwcap = "URI:CHK:apegrpehshwugkbh3jlt5ei6hq:5oougnemcl5xgx4ijgiumtdojlipibctjkbwvyygdymdphib2fvq:3:10:4"
1154 file2_metadata = {'ctime': 1246663897.430218, 'tahoe': {'linkmotime': 1246663897.430218, 'linkcrtime': 1246663897.430218}, 'mtime': 1246663897.430218}
1155 self.failUnlessEqual(file2_metadata, children[u'file2'][1])
1156 self.failUnlessEqual(file2_rocap,
1157 children[u'file2'][0].get_readonly_uri())
1158 self.failUnlessEqual(file2_rwcap,
1159 children[u'file2'][0].get_uri())
1161 # Are the metadata for child 1 right?
1162 file1_rocap = "URI:CHK:olxtimympo7f27jvhtgqlnbtn4:emzdnhk2um4seixozlkw3qx2nfijvdkx3ky7i7izl47yedl6e64a:3:10:10"
1163 file1_rwcap = "URI:CHK:olxtimympo7f27jvhtgqlnbtn4:emzdnhk2um4seixozlkw3qx2nfijvdkx3ky7i7izl47yedl6e64a:3:10:10"
1164 file1_metadata = {'ctime': 1246663897.4275661, 'tahoe': {'linkmotime': 1246663897.4275661, 'linkcrtime': 1246663897.4275661}, 'mtime': 1246663897.4275661}
1165 self.failUnlessEqual(file1_metadata, children[u'file1'][1])
1166 self.failUnlessEqual(file1_rocap,
1167 children[u'file1'][0].get_readonly_uri())
1168 self.failUnlessEqual(file1_rwcap,
1169 children[u'file1'][0].get_uri())
1171 def _make_kids(self, nm, which):
1172 caps = {"imm": "URI:CHK:n7r3m6wmomelk4sep3kw5cvduq:os7ijw5c3maek7pg65e5254k2fzjflavtpejjyhshpsxuqzhcwwq:3:20:14861",
1173 "lit": "URI:LIT:n5xgk", # LIT for "one"
1174 "write": "URI:SSK:vfvcbdfbszyrsaxchgevhmmlii:euw4iw7bbnkrrwpzuburbhppuxhc3gwxv26f6imekhz7zyw2ojnq",
1175 "read": "URI:SSK-RO:e3mdrzfwhoq42hy5ubcz6rp3o4:ybyibhnp3vvwuq2vaw2ckjmesgkklfs6ghxleztqidihjyofgw7q",
1176 "dirwrite": "URI:DIR2:n6x24zd3seu725yluj75q5boaa:mm6yoqjhl6ueh7iereldqxue4nene4wl7rqfjfybqrehdqmqskvq",
1177 "dirread": "URI:DIR2-RO:b7sr5qsifnicca7cbk3rhrhbvq:mm6yoqjhl6ueh7iereldqxue4nene4wl7rqfjfybqrehdqmqskvq",
1181 kids[unicode(name)] = (nm.create_from_cap(caps[name]), {})
1184 def test_deep_immutable(self):
1185 nm = NodeMaker(None, None, None, None, None, None, {"k": 3, "n": 10},
1187 fn = MinimalFakeMutableFile()
1189 kids = self._make_kids(nm, ["imm", "lit", "write", "read",
1190 "dirwrite", "dirread"])
1191 packed = dirnode.pack_children(fn, kids, deep_immutable=False)
1192 self.failUnlessIn("lit", packed)
1194 kids = self._make_kids(nm, ["imm", "lit"])
1195 packed = dirnode.pack_children(fn, kids, deep_immutable=True)
1196 self.failUnlessIn("lit", packed)
1198 kids = self._make_kids(nm, ["imm", "lit", "write"])
1199 self.failUnlessRaises(dirnode.MustBeDeepImmutableError,
1200 dirnode.pack_children,
1201 fn, kids, deep_immutable=True)
1203 # read-only is not enough: all children must be immutable
1204 kids = self._make_kids(nm, ["imm", "lit", "read"])
1205 self.failUnlessRaises(dirnode.MustBeDeepImmutableError,
1206 dirnode.pack_children,
1207 fn, kids, deep_immutable=True)
1209 kids = self._make_kids(nm, ["imm", "lit", "dirwrite"])
1210 self.failUnlessRaises(dirnode.MustBeDeepImmutableError,
1211 dirnode.pack_children,
1212 fn, kids, deep_immutable=True)
1214 kids = self._make_kids(nm, ["imm", "lit", "dirread"])
1215 self.failUnlessRaises(dirnode.MustBeDeepImmutableError,
1216 dirnode.pack_children,
1217 fn, kids, deep_immutable=True)
1219 class FakeMutableFile:
1220 implements(IMutableFileNode)
1222 def __init__(self, initial_contents=""):
1223 self.data = self._get_initial_contents(initial_contents)
1224 counter = FakeMutableFile.counter
1225 FakeMutableFile.counter += 1
1226 writekey = hashutil.ssk_writekey_hash(str(counter))
1227 fingerprint = hashutil.ssk_pubkey_fingerprint_hash(str(counter))
1228 self.uri = uri.WriteableSSKFileURI(writekey, fingerprint)
1230 def _get_initial_contents(self, contents):
1231 if isinstance(contents, str):
1233 if contents is None:
1235 assert callable(contents), "%s should be callable, not %s" % \
1236 (contents, type(contents))
1237 return contents(self)
1243 return self.uri.to_string()
1245 def get_write_uri(self):
1246 return self.uri.to_string()
1248 def download_best_version(self):
1249 return defer.succeed(self.data)
1251 def get_writekey(self):
1254 def is_readonly(self):
1257 def is_mutable(self):
1260 def is_unknown(self):
1263 def is_allowed_in_immutable_directory(self):
1266 def raise_error(self):
1269 def modify(self, modifier):
1270 self.data = modifier(self.data, None, True)
1271 return defer.succeed(None)
1273 class FakeNodeMaker(NodeMaker):
1274 def create_mutable_file(self, contents="", keysize=None):
1275 return defer.succeed(FakeMutableFile(contents))
1277 class FakeClient2(Client):
1279 self.nodemaker = FakeNodeMaker(None, None, None,
1281 {"k":3,"n":10}, None)
1282 def create_node_from_uri(self, rwcap, rocap):
1283 return self.nodemaker.create_from_cap(rwcap, rocap)
1285 class Dirnode2(unittest.TestCase, testutil.ShouldFailMixin):
1287 client = FakeClient2()
1288 self.nodemaker = client.nodemaker
1290 def test_from_future(self):
1291 # Create a mutable directory that contains unknown URI types, and make sure
1292 # we tolerate them properly.
1293 d = self.nodemaker.create_new_mutable_directory()
1294 future_write_uri = "x-tahoe-crazy://I_am_from_the_future."
1295 future_read_uri = "x-tahoe-crazy-readonly://I_am_from_the_future."
1296 future_imm_uri = "x-tahoe-crazy-immutable://I_am_from_the_future."
1297 future_node = UnknownNode(future_write_uri, future_read_uri)
1300 return n.set_node(u"future", future_node)
1301 d.addCallback(_then)
1303 # We should be prohibited from adding an unknown URI to a directory
1304 # just in the rw_uri slot, since we don't know how to diminish the cap
1305 # to a readcap (for the ro_uri slot).
1306 d.addCallback(lambda ign:
1307 self.shouldFail(MustNotBeUnknownRWError,
1309 "cannot attach unknown rw cap as child",
1310 self._node.set_uri, u"add",
1311 future_write_uri, None))
1313 # However, we should be able to add both rw_uri and ro_uri as a pair of
1315 d.addCallback(lambda ign: self._node.set_uri(u"add-pair",
1316 future_write_uri, future_read_uri))
1318 # and to add an URI prefixed with "ro." or "imm." when it is given in a
1319 # write slot (or URL parameter).
1320 d.addCallback(lambda ign: self._node.set_uri(u"add-ro",
1321 "ro." + future_read_uri, None))
1322 d.addCallback(lambda ign: self._node.set_uri(u"add-imm",
1323 "imm." + future_imm_uri, None))
1325 d.addCallback(lambda ign: self._node.list())
1326 def _check(children):
1327 self.failUnlessEqual(len(children), 4)
1328 (fn, metadata) = children[u"future"]
1329 self.failUnless(isinstance(fn, UnknownNode), fn)
1330 self.failUnlessEqual(fn.get_uri(), future_write_uri)
1331 self.failUnlessEqual(fn.get_write_uri(), future_write_uri)
1332 self.failUnlessEqual(fn.get_readonly_uri(), "ro." + future_read_uri)
1334 (fn2, metadata2) = children[u"add-pair"]
1335 self.failUnless(isinstance(fn2, UnknownNode), fn2)
1336 self.failUnlessEqual(fn2.get_uri(), future_write_uri)
1337 self.failUnlessEqual(fn2.get_write_uri(), future_write_uri)
1338 self.failUnlessEqual(fn2.get_readonly_uri(), "ro." + future_read_uri)
1340 (fn3, metadata3) = children[u"add-ro"]
1341 self.failUnless(isinstance(fn3, UnknownNode), fn3)
1342 self.failUnlessEqual(fn3.get_uri(), "ro." + future_read_uri)
1343 self.failUnlessEqual(fn3.get_write_uri(), None)
1344 self.failUnlessEqual(fn3.get_readonly_uri(), "ro." + future_read_uri)
1346 (fn4, metadata4) = children[u"add-imm"]
1347 self.failUnless(isinstance(fn4, UnknownNode), fn4)
1348 self.failUnlessEqual(fn4.get_uri(), "imm." + future_imm_uri)
1349 self.failUnlessEqual(fn4.get_write_uri(), None)
1350 self.failUnlessEqual(fn4.get_readonly_uri(), "imm." + future_imm_uri)
1352 # We should also be allowed to copy the "future" UnknownNode, because
1353 # it contains all the information that was in the original directory
1354 # (readcap and writecap), so we're preserving everything.
1355 return self._node.set_node(u"copy", fn)
1356 d.addCallback(_check)
1358 d.addCallback(lambda ign: self._node.list())
1359 def _check2(children):
1360 self.failUnlessEqual(len(children), 5)
1361 (fn, metadata) = children[u"copy"]
1362 self.failUnless(isinstance(fn, UnknownNode), fn)
1363 self.failUnlessEqual(fn.get_uri(), future_write_uri)
1364 self.failUnlessEqual(fn.get_write_uri(), future_write_uri)
1365 self.failUnlessEqual(fn.get_readonly_uri(), "ro." + future_read_uri)
1366 d.addCallback(_check2)
1369 def test_unknown_strip_prefix_for_ro(self):
1370 self.failUnlessEqual(strip_prefix_for_ro("foo", False), "foo")
1371 self.failUnlessEqual(strip_prefix_for_ro("ro.foo", False), "foo")
1372 self.failUnlessEqual(strip_prefix_for_ro("imm.foo", False), "imm.foo")
1373 self.failUnlessEqual(strip_prefix_for_ro("foo", True), "foo")
1374 self.failUnlessEqual(strip_prefix_for_ro("ro.foo", True), "foo")
1375 self.failUnlessEqual(strip_prefix_for_ro("imm.foo", True), "foo")
1377 def test_unknownnode(self):
1378 mut_write_uri = "URI:SSK:vfvcbdfbszyrsaxchgevhmmlii:euw4iw7bbnkrrwpzuburbhppuxhc3gwxv26f6imekhz7zyw2ojnq"
1379 mut_read_uri = "URI:SSK-RO:jf6wkflosyvntwxqcdo7a54jvm:euw4iw7bbnkrrwpzuburbhppuxhc3gwxv26f6imekhz7zyw2ojnq"
1380 lit_uri = "URI:LIT:n5xgk"
1382 # This does not attempt to be exhaustive.
1383 no_no = [# Opaque node, but not an error.
1384 ( 0, UnknownNode(None, None)),
1385 ( 1, UnknownNode(None, None, deep_immutable=True)),
1387 unknown_rw = [# These are errors because we're only given a rw_uri, and we can't
1389 ( 2, UnknownNode("foo", None)),
1390 ( 3, UnknownNode("foo", None, deep_immutable=True)),
1391 ( 4, UnknownNode("ro.foo", None, deep_immutable=True)),
1392 ( 5, UnknownNode("ro." + mut_read_uri, None, deep_immutable=True)),
1393 ( 6, UnknownNode("URI:SSK-RO:foo", None, deep_immutable=True)),
1394 ( 7, UnknownNode("URI:SSK:foo", None)),
1396 must_be_ro = [# These are errors because a readonly constraint is not met.
1397 ( 8, UnknownNode("ro." + mut_write_uri, None)),
1398 ( 9, UnknownNode(None, "ro." + mut_write_uri)),
1400 must_be_imm = [# These are errors because an immutable constraint is not met.
1401 (10, UnknownNode(None, "ro.URI:SSK-RO:foo", deep_immutable=True)),
1402 (11, UnknownNode(None, "imm.URI:SSK:foo")),
1403 (12, UnknownNode(None, "imm.URI:SSK-RO:foo")),
1404 (13, UnknownNode("bar", "ro.foo", deep_immutable=True)),
1405 (14, UnknownNode("bar", "imm.foo", deep_immutable=True)),
1406 (15, UnknownNode("bar", "imm." + lit_uri, deep_immutable=True)),
1407 (16, UnknownNode("imm." + mut_write_uri, None)),
1408 (17, UnknownNode("imm." + mut_read_uri, None)),
1409 (18, UnknownNode("bar", "imm.foo")),
1411 bad_uri = [# These are errors because the URI is bad once we've stripped the prefix.
1412 (19, UnknownNode("ro.URI:SSK-RO:foo", None)),
1413 (20, UnknownNode("imm.URI:CHK:foo", None, deep_immutable=True)),
1414 (21, UnknownNode(None, "URI:CHK:foo")),
1415 (22, UnknownNode(None, "URI:CHK:foo", deep_immutable=True)),
1417 ro_prefixed = [# These are valid, and the readcap should end up with a ro. prefix.
1418 (23, UnknownNode(None, "foo")),
1419 (24, UnknownNode(None, "ro.foo")),
1420 (25, UnknownNode(None, "ro." + lit_uri)),
1421 (26, UnknownNode("bar", "foo")),
1422 (27, UnknownNode("bar", "ro.foo")),
1423 (28, UnknownNode("bar", "ro." + lit_uri)),
1424 (29, UnknownNode("ro.foo", None)),
1425 (30, UnknownNode("ro." + lit_uri, None)),
1427 imm_prefixed = [# These are valid, and the readcap should end up with an imm. prefix.
1428 (31, UnknownNode(None, "foo", deep_immutable=True)),
1429 (32, UnknownNode(None, "ro.foo", deep_immutable=True)),
1430 (33, UnknownNode(None, "imm.foo")),
1431 (34, UnknownNode(None, "imm.foo", deep_immutable=True)),
1432 (35, UnknownNode("imm." + lit_uri, None)),
1433 (36, UnknownNode("imm." + lit_uri, None, deep_immutable=True)),
1434 (37, UnknownNode(None, "imm." + lit_uri)),
1435 (38, UnknownNode(None, "imm." + lit_uri, deep_immutable=True)),
1437 error = unknown_rw + must_be_ro + must_be_imm + bad_uri
1438 ok = ro_prefixed + imm_prefixed
1440 for (i, n) in no_no + error + ok:
1441 self.failUnless(n.is_unknown(), i)
1443 for (i, n) in no_no + error:
1444 self.failUnless(n.get_uri() is None, i)
1445 self.failUnless(n.get_write_uri() is None, i)
1446 self.failUnless(n.get_readonly_uri() is None, i)
1448 for (i, n) in no_no + ok:
1451 for (i, n) in unknown_rw:
1452 self.failUnlessRaises(MustNotBeUnknownRWError, lambda: n.raise_error())
1454 for (i, n) in must_be_ro:
1455 self.failUnlessRaises(MustBeReadonlyError, lambda: n.raise_error())
1457 for (i, n) in must_be_imm:
1458 self.failUnlessRaises(MustBeDeepImmutableError, lambda: n.raise_error())
1460 for (i, n) in bad_uri:
1461 self.failUnlessRaises(uri.BadURIError, lambda: n.raise_error())
1464 self.failIf(n.get_readonly_uri() is None, i)
1466 for (i, n) in ro_prefixed:
1467 self.failUnless(n.get_readonly_uri().startswith("ro."), i)
1469 for (i, n) in imm_prefixed:
1470 self.failUnless(n.get_readonly_uri().startswith("imm."), i)
1473 class DeepStats(unittest.TestCase):
1474 timeout = 240 # It takes longer than 120 seconds on Francois's arm box.
1475 def test_stats(self):
1476 ds = dirnode.DeepStats(None)
1477 ds.add("count-files")
1478 ds.add("size-immutable-files", 123)
1479 ds.histogram("size-files-histogram", 123)
1480 ds.max("largest-directory", 444)
1482 s = ds.get_results()
1483 self.failUnlessEqual(s["count-files"], 1)
1484 self.failUnlessEqual(s["size-immutable-files"], 123)
1485 self.failUnlessEqual(s["largest-directory"], 444)
1486 self.failUnlessEqual(s["count-literal-files"], 0)
1488 ds.add("count-files")
1489 ds.add("size-immutable-files", 321)
1490 ds.histogram("size-files-histogram", 321)
1491 ds.max("largest-directory", 2)
1493 s = ds.get_results()
1494 self.failUnlessEqual(s["count-files"], 2)
1495 self.failUnlessEqual(s["size-immutable-files"], 444)
1496 self.failUnlessEqual(s["largest-directory"], 444)
1497 self.failUnlessEqual(s["count-literal-files"], 0)
1498 self.failUnlessEqual(s["size-files-histogram"],
1499 [ (101, 316, 1), (317, 1000, 1) ])
1501 ds = dirnode.DeepStats(None)
1502 for i in range(1, 1100):
1503 ds.histogram("size-files-histogram", i)
1504 ds.histogram("size-files-histogram", 4*1000*1000*1000*1000) # 4TB
1505 s = ds.get_results()
1506 self.failUnlessEqual(s["size-files-histogram"],
1514 (3162277660169L, 10000000000000L, 1),
1517 class UCWEingMutableFileNode(MutableFileNode):
1518 please_ucwe_after_next_upload = False
1520 def _upload(self, new_contents, servermap):
1521 d = MutableFileNode._upload(self, new_contents, servermap)
1523 if self.please_ucwe_after_next_upload:
1524 self.please_ucwe_after_next_upload = False
1525 raise UncoordinatedWriteError()
1527 d.addCallback(_ucwe)
1530 class UCWEingNodeMaker(NodeMaker):
1531 def _create_mutable(self, cap):
1532 n = UCWEingMutableFileNode(self.storage_broker, self.secret_holder,
1533 self.default_encoding_parameters,
1535 return n.init_from_cap(cap)
1538 class Deleter(GridTestMixin, unittest.TestCase):
1539 timeout = 3600 # It takes longer than 433 seconds on Zandr's ARM box.
1540 def test_retry(self):
1541 # ticket #550, a dirnode.delete which experiences an
1542 # UncoordinatedWriteError will fail with an incorrect "you're
1543 # deleting something which isn't there" NoSuchChildError exception.
1545 # to trigger this, we start by creating a directory with a single
1546 # file in it. Then we create a special dirnode that uses a modified
1547 # MutableFileNode which will raise UncoordinatedWriteError once on
1548 # demand. We then call dirnode.delete, which ought to retry and
1551 self.basedir = self.mktemp()
1553 c0 = self.g.clients[0]
1554 d = c0.create_dirnode()
1555 small = upload.Data("Small enough for a LIT", None)
1556 def _created_dir(dn):
1558 self.root_uri = dn.get_uri()
1559 return dn.add_file(u"file", small)
1560 d.addCallback(_created_dir)
1561 def _do_delete(ignored):
1562 nm = UCWEingNodeMaker(c0.storage_broker, c0._secret_holder,
1563 c0.get_history(), c0.getServiceNamed("uploader"),
1565 c0.download_cache_dirman,
1566 c0.get_encoding_parameters(),
1568 n = nm.create_from_cap(self.root_uri)
1569 assert n._node.please_ucwe_after_next_upload == False
1570 n._node.please_ucwe_after_next_upload = True
1571 # This should succeed, not raise an exception
1572 return n.delete(u"file")
1573 d.addCallback(_do_delete)
1577 class Adder(GridTestMixin, unittest.TestCase, testutil.ShouldFailMixin):
1579 def test_overwrite(self):
1580 # note: This functionality could be tested without actually creating
1581 # several RSA keys. It would be faster without the GridTestMixin: use
1582 # dn.set_node(nodemaker.create_from_cap(make_chk_file_uri())) instead
1583 # of dn.add_file, and use a special NodeMaker that creates fake
1585 self.basedir = "dirnode/Adder/test_overwrite"
1587 c = self.g.clients[0]
1588 fileuri = make_chk_file_uri(1234)
1589 filenode = c.nodemaker.create_from_cap(fileuri)
1590 d = c.create_dirnode()
1592 def _create_directory_tree(root_node):
1597 d = root_node.add_file(u'file1', upload.Data("Important Things",
1599 d.addCallback(lambda res:
1600 root_node.add_file(u'file2', upload.Data("Sekrit Codes", None)))
1601 d.addCallback(lambda res:
1602 root_node.create_subdirectory(u"dir1"))
1603 d.addCallback(lambda res: root_node)
1606 d.addCallback(_create_directory_tree)
1608 def _test_adder(root_node):
1609 d = root_node.set_node(u'file1', filenode)
1610 # We've overwritten file1. Let's try it with a directory
1611 d.addCallback(lambda res:
1612 root_node.create_subdirectory(u'dir2'))
1613 d.addCallback(lambda res:
1614 root_node.set_node(u'dir2', filenode))
1615 # We try overwriting a file with a child while also specifying
1616 # overwrite=False. We should receive an ExistingChildError
1618 d.addCallback(lambda res:
1619 self.shouldFail(ExistingChildError, "set_node",
1620 "child 'file1' already exists",
1621 root_node.set_node, u"file1",
1622 filenode, overwrite=False))
1623 # If we try with a directory, we should see the same thing
1624 d.addCallback(lambda res:
1625 self.shouldFail(ExistingChildError, "set_node",
1626 "child 'dir1' already exists",
1627 root_node.set_node, u'dir1', filenode,
1629 d.addCallback(lambda res:
1630 root_node.set_node(u'file1', filenode,
1631 overwrite="only-files"))
1632 d.addCallback(lambda res:
1633 self.shouldFail(ExistingChildError, "set_node",
1634 "child 'dir1' already exists",
1635 root_node.set_node, u'dir1', filenode,
1636 overwrite="only-files"))
1639 d.addCallback(_test_adder)