]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - src/allmydata/test/test_dirnode.py
move dirnode.CachingDict to dictutil.AuxValueDict, generalize method names,
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / test / test_dirnode.py
1
2 import time
3 from twisted.trial import unittest
4 from twisted.internet import defer
5 from allmydata import uri, dirnode
6 from allmydata.client import Client
7 from allmydata.immutable import upload
8 from allmydata.interfaces import IFileNode, \
9      ExistingChildError, NoSuchChildError, \
10      IDeepCheckResults, IDeepCheckAndRepairResults, CannotPackUnknownNodeError
11 from allmydata.mutable.filenode import MutableFileNode
12 from allmydata.mutable.common import UncoordinatedWriteError
13 from allmydata.util import hashutil, base32
14 from allmydata.monitor import Monitor
15 from allmydata.test.common import make_chk_file_uri, make_mutable_file_uri, \
16      ErrorMixin
17 from allmydata.test.no_network import GridTestMixin
18 from allmydata.unknown import UnknownNode
19 from allmydata.nodemaker import NodeMaker
20 from base64 import b32decode
21 import common_util as testutil
22
23 class Dirnode(GridTestMixin, unittest.TestCase,
24               testutil.ShouldFailMixin, testutil.StallMixin, ErrorMixin):
25     timeout = 240 # It takes longer than 120 seconds on Francois's arm box.
26
27     def test_basic(self):
28         self.basedir = "dirnode/Dirnode/test_basic"
29         self.set_up_grid()
30         c = self.g.clients[0]
31         d = c.create_dirnode()
32         def _done(res):
33             self.failUnless(isinstance(res, dirnode.DirectoryNode))
34             rep = str(res)
35             self.failUnless("RW" in rep)
36         d.addCallback(_done)
37         return d
38
39     def test_check(self):
40         self.basedir = "dirnode/Dirnode/test_check"
41         self.set_up_grid()
42         c = self.g.clients[0]
43         d = c.create_dirnode()
44         d.addCallback(lambda dn: dn.check(Monitor()))
45         def _done(res):
46             self.failUnless(res.is_healthy())
47         d.addCallback(_done)
48         return d
49
50     def _test_deepcheck_create(self):
51         # create a small tree with a loop, and some non-directories
52         #  root/
53         #  root/subdir/
54         #  root/subdir/file1
55         #  root/subdir/link -> root
56         #  root/rodir
57         c = self.g.clients[0]
58         d = c.create_dirnode()
59         def _created_root(rootnode):
60             self._rootnode = rootnode
61             return rootnode.create_subdirectory(u"subdir")
62         d.addCallback(_created_root)
63         def _created_subdir(subdir):
64             self._subdir = subdir
65             d = subdir.add_file(u"file1", upload.Data("data"*100, None))
66             d.addCallback(lambda res: subdir.set_node(u"link", self._rootnode))
67             d.addCallback(lambda res: c.create_dirnode())
68             d.addCallback(lambda dn:
69                           self._rootnode.set_uri(u"rodir",
70                                                  dn.get_uri(),
71                                                  dn.get_readonly_uri()))
72             return d
73         d.addCallback(_created_subdir)
74         def _done(res):
75             return self._rootnode
76         d.addCallback(_done)
77         return d
78
79     def test_deepcheck(self):
80         self.basedir = "dirnode/Dirnode/test_deepcheck"
81         self.set_up_grid()
82         d = self._test_deepcheck_create()
83         d.addCallback(lambda rootnode: rootnode.start_deep_check().when_done())
84         def _check_results(r):
85             self.failUnless(IDeepCheckResults.providedBy(r))
86             c = r.get_counters()
87             self.failUnlessEqual(c,
88                                  {"count-objects-checked": 4,
89                                   "count-objects-healthy": 4,
90                                   "count-objects-unhealthy": 0,
91                                   "count-objects-unrecoverable": 0,
92                                   "count-corrupt-shares": 0,
93                                   })
94             self.failIf(r.get_corrupt_shares())
95             self.failUnlessEqual(len(r.get_all_results()), 4)
96         d.addCallback(_check_results)
97         return d
98
99     def test_deepcheck_and_repair(self):
100         self.basedir = "dirnode/Dirnode/test_deepcheck_and_repair"
101         self.set_up_grid()
102         d = self._test_deepcheck_create()
103         d.addCallback(lambda rootnode:
104                       rootnode.start_deep_check_and_repair().when_done())
105         def _check_results(r):
106             self.failUnless(IDeepCheckAndRepairResults.providedBy(r))
107             c = r.get_counters()
108             self.failUnlessEqual(c,
109                                  {"count-objects-checked": 4,
110                                   "count-objects-healthy-pre-repair": 4,
111                                   "count-objects-unhealthy-pre-repair": 0,
112                                   "count-objects-unrecoverable-pre-repair": 0,
113                                   "count-corrupt-shares-pre-repair": 0,
114                                   "count-objects-healthy-post-repair": 4,
115                                   "count-objects-unhealthy-post-repair": 0,
116                                   "count-objects-unrecoverable-post-repair": 0,
117                                   "count-corrupt-shares-post-repair": 0,
118                                   "count-repairs-attempted": 0,
119                                   "count-repairs-successful": 0,
120                                   "count-repairs-unsuccessful": 0,
121                                   })
122             self.failIf(r.get_corrupt_shares())
123             self.failIf(r.get_remaining_corrupt_shares())
124             self.failUnlessEqual(len(r.get_all_results()), 4)
125         d.addCallback(_check_results)
126         return d
127
128     def _mark_file_bad(self, rootnode):
129         si = rootnode.get_storage_index()
130         self.delete_shares_numbered(rootnode.get_uri(), [0])
131         return rootnode
132
133     def test_deepcheck_problems(self):
134         self.basedir = "dirnode/Dirnode/test_deepcheck_problems"
135         self.set_up_grid()
136         d = self._test_deepcheck_create()
137         d.addCallback(lambda rootnode: self._mark_file_bad(rootnode))
138         d.addCallback(lambda rootnode: rootnode.start_deep_check().when_done())
139         def _check_results(r):
140             c = r.get_counters()
141             self.failUnlessEqual(c,
142                                  {"count-objects-checked": 4,
143                                   "count-objects-healthy": 3,
144                                   "count-objects-unhealthy": 1,
145                                   "count-objects-unrecoverable": 0,
146                                   "count-corrupt-shares": 0,
147                                   })
148             #self.failUnlessEqual(len(r.get_problems()), 1) # TODO
149         d.addCallback(_check_results)
150         return d
151
152     def test_readonly(self):
153         self.basedir = "dirnode/Dirnode/test_readonly"
154         self.set_up_grid()
155         c = self.g.clients[0]
156         nm = c.nodemaker
157         filecap = make_chk_file_uri(1234)
158         filenode = nm.create_from_cap(filecap)
159         uploadable = upload.Data("some data", convergence="some convergence string")
160
161         d = c.create_dirnode()
162         def _created(rw_dn):
163             d2 = rw_dn.set_uri(u"child", filecap, filecap)
164             d2.addCallback(lambda res: rw_dn)
165             return d2
166         d.addCallback(_created)
167
168         def _ready(rw_dn):
169             ro_uri = rw_dn.get_readonly_uri()
170             ro_dn = c.create_node_from_uri(ro_uri)
171             self.failUnless(ro_dn.is_readonly())
172             self.failUnless(ro_dn.is_mutable())
173
174             self.shouldFail(dirnode.NotMutableError, "set_uri ro", None,
175                             ro_dn.set_uri, u"newchild", filecap, filecap)
176             self.shouldFail(dirnode.NotMutableError, "set_uri ro", None,
177                             ro_dn.set_node, u"newchild", filenode)
178             self.shouldFail(dirnode.NotMutableError, "set_nodes ro", None,
179                             ro_dn.set_nodes, [ (u"newchild", filenode) ])
180             self.shouldFail(dirnode.NotMutableError, "set_uri ro", None,
181                             ro_dn.add_file, u"newchild", uploadable)
182             self.shouldFail(dirnode.NotMutableError, "set_uri ro", None,
183                             ro_dn.delete, u"child")
184             self.shouldFail(dirnode.NotMutableError, "set_uri ro", None,
185                             ro_dn.create_subdirectory, u"newchild")
186             self.shouldFail(dirnode.NotMutableError, "set_metadata_for ro", None,
187                             ro_dn.set_metadata_for, u"child", {})
188             self.shouldFail(dirnode.NotMutableError, "set_uri ro", None,
189                             ro_dn.move_child_to, u"child", rw_dn)
190             self.shouldFail(dirnode.NotMutableError, "set_uri ro", None,
191                             rw_dn.move_child_to, u"child", ro_dn)
192             return ro_dn.list()
193         d.addCallback(_ready)
194         def _listed(children):
195             self.failUnless(u"child" in children)
196         d.addCallback(_listed)
197         return d
198
199     def failUnlessGreaterThan(self, a, b):
200         self.failUnless(a > b, "%r should be > %r" % (a, b))
201
202     def failUnlessGreaterOrEqualThan(self, a, b):
203         self.failUnless(a >= b, "%r should be >= %r" % (a, b))
204
205     def test_create(self):
206         self.basedir = "dirnode/Dirnode/test_create"
207         self.set_up_grid()
208         c = self.g.clients[0]
209
210         self.expected_manifest = []
211         self.expected_verifycaps = set()
212         self.expected_storage_indexes = set()
213
214         d = c.create_dirnode()
215         def _then(n):
216             # /
217             self.rootnode = n
218             self.failUnless(n.is_mutable())
219             u = n.get_uri()
220             self.failUnless(u)
221             self.failUnless(u.startswith("URI:DIR2:"), u)
222             u_ro = n.get_readonly_uri()
223             self.failUnless(u_ro.startswith("URI:DIR2-RO:"), u_ro)
224             u_v = n.get_verify_cap().to_string()
225             self.failUnless(u_v.startswith("URI:DIR2-Verifier:"), u_v)
226             u_r = n.get_repair_cap().to_string()
227             self.failUnlessEqual(u_r, u)
228             self.expected_manifest.append( ((), u) )
229             self.expected_verifycaps.add(u_v)
230             si = n.get_storage_index()
231             self.expected_storage_indexes.add(base32.b2a(si))
232             expected_si = n._uri._filenode_uri.storage_index
233             self.failUnlessEqual(si, expected_si)
234
235             d = n.list()
236             d.addCallback(lambda res: self.failUnlessEqual(res, {}))
237             d.addCallback(lambda res: n.has_child(u"missing"))
238             d.addCallback(lambda res: self.failIf(res))
239
240             fake_file_uri = make_mutable_file_uri()
241             other_file_uri = make_mutable_file_uri()
242             m = c.nodemaker.create_from_cap(fake_file_uri)
243             ffu_v = m.get_verify_cap().to_string()
244             self.expected_manifest.append( ((u"child",) , m.get_uri()) )
245             self.expected_verifycaps.add(ffu_v)
246             self.expected_storage_indexes.add(base32.b2a(m.get_storage_index()))
247             d.addCallback(lambda res: n.set_uri(u"child",
248                                                 fake_file_uri, fake_file_uri))
249             d.addCallback(lambda res:
250                           self.shouldFail(ExistingChildError, "set_uri-no",
251                                           "child 'child' already exists",
252                                           n.set_uri, u"child",
253                                           other_file_uri, other_file_uri,
254                                           overwrite=False))
255             # /
256             # /child = mutable
257
258             d.addCallback(lambda res: n.create_subdirectory(u"subdir"))
259
260             # /
261             # /child = mutable
262             # /subdir = directory
263             def _created(subdir):
264                 self.failUnless(isinstance(subdir, dirnode.DirectoryNode))
265                 self.subdir = subdir
266                 new_v = subdir.get_verify_cap().to_string()
267                 assert isinstance(new_v, str)
268                 self.expected_manifest.append( ((u"subdir",), subdir.get_uri()) )
269                 self.expected_verifycaps.add(new_v)
270                 si = subdir.get_storage_index()
271                 self.expected_storage_indexes.add(base32.b2a(si))
272             d.addCallback(_created)
273
274             d.addCallback(lambda res:
275                           self.shouldFail(ExistingChildError, "mkdir-no",
276                                           "child 'subdir' already exists",
277                                           n.create_subdirectory, u"subdir",
278                                           overwrite=False))
279
280             d.addCallback(lambda res: n.list())
281             d.addCallback(lambda children:
282                           self.failUnlessEqual(sorted(children.keys()),
283                                                sorted([u"child", u"subdir"])))
284
285             d.addCallback(lambda res: n.start_deep_stats().when_done())
286             def _check_deepstats(stats):
287                 self.failUnless(isinstance(stats, dict))
288                 expected = {"count-immutable-files": 0,
289                             "count-mutable-files": 1,
290                             "count-literal-files": 0,
291                             "count-files": 1,
292                             "count-directories": 2,
293                             "size-immutable-files": 0,
294                             "size-literal-files": 0,
295                             #"size-directories": 616, # varies
296                             #"largest-directory": 616,
297                             "largest-directory-children": 2,
298                             "largest-immutable-file": 0,
299                             }
300                 for k,v in expected.iteritems():
301                     self.failUnlessEqual(stats[k], v,
302                                          "stats[%s] was %s, not %s" %
303                                          (k, stats[k], v))
304                 self.failUnless(stats["size-directories"] > 500,
305                                 stats["size-directories"])
306                 self.failUnless(stats["largest-directory"] > 500,
307                                 stats["largest-directory"])
308                 self.failUnlessEqual(stats["size-files-histogram"], [])
309             d.addCallback(_check_deepstats)
310
311             d.addCallback(lambda res: n.build_manifest().when_done())
312             def _check_manifest(res):
313                 manifest = res["manifest"]
314                 self.failUnlessEqual(sorted(manifest),
315                                      sorted(self.expected_manifest))
316                 stats = res["stats"]
317                 _check_deepstats(stats)
318                 self.failUnlessEqual(self.expected_verifycaps,
319                                      res["verifycaps"])
320                 self.failUnlessEqual(self.expected_storage_indexes,
321                                      res["storage-index"])
322             d.addCallback(_check_manifest)
323
324             def _add_subsubdir(res):
325                 return self.subdir.create_subdirectory(u"subsubdir")
326             d.addCallback(_add_subsubdir)
327             # /
328             # /child = mutable
329             # /subdir = directory
330             # /subdir/subsubdir = directory
331             d.addCallback(lambda res: n.get_child_at_path(u"subdir/subsubdir"))
332             d.addCallback(lambda subsubdir:
333                           self.failUnless(isinstance(subsubdir,
334                                                      dirnode.DirectoryNode)))
335             d.addCallback(lambda res: n.get_child_at_path(u""))
336             d.addCallback(lambda res: self.failUnlessEqual(res.get_uri(),
337                                                            n.get_uri()))
338
339             d.addCallback(lambda res: n.get_metadata_for(u"child"))
340             d.addCallback(lambda metadata:
341                           self.failUnlessEqual(set(metadata.keys()),
342                                                set(["tahoe", "ctime", "mtime"])))
343
344             d.addCallback(lambda res:
345                           self.shouldFail(NoSuchChildError, "gcamap-no",
346                                           "nope",
347                                           n.get_child_and_metadata_at_path,
348                                           u"subdir/nope"))
349             d.addCallback(lambda res:
350                           n.get_child_and_metadata_at_path(u""))
351             def _check_child_and_metadata1(res):
352                 child, metadata = res
353                 self.failUnless(isinstance(child, dirnode.DirectoryNode))
354                 # edge-metadata needs at least one path segment
355                 self.failUnlessEqual(sorted(metadata.keys()), [])
356             d.addCallback(_check_child_and_metadata1)
357             d.addCallback(lambda res:
358                           n.get_child_and_metadata_at_path(u"child"))
359
360             def _check_child_and_metadata2(res):
361                 child, metadata = res
362                 self.failUnlessEqual(child.get_uri(),
363                                      fake_file_uri)
364                 self.failUnlessEqual(set(metadata.keys()),
365                                      set(["tahoe", "ctime", "mtime"]))
366             d.addCallback(_check_child_and_metadata2)
367
368             d.addCallback(lambda res:
369                           n.get_child_and_metadata_at_path(u"subdir/subsubdir"))
370             def _check_child_and_metadata3(res):
371                 child, metadata = res
372                 self.failUnless(isinstance(child, dirnode.DirectoryNode))
373                 self.failUnlessEqual(set(metadata.keys()),
374                                      set(["tahoe", "ctime", "mtime"]))
375             d.addCallback(_check_child_and_metadata3)
376
377             # set_uri + metadata
378             # it should be possible to add a child without any metadata
379             d.addCallback(lambda res: n.set_uri(u"c2",
380                                                 fake_file_uri, fake_file_uri,
381                                                 {}))
382             d.addCallback(lambda res: n.get_metadata_for(u"c2"))
383             d.addCallback(lambda metadata: self.failUnlessEqual(metadata.keys(), ['tahoe']))
384
385             # You can't override the link timestamps.
386             d.addCallback(lambda res: n.set_uri(u"c2",
387                                                 fake_file_uri, fake_file_uri,
388                                                 { 'tahoe': {'linkcrtime': "bogus"}}))
389             d.addCallback(lambda res: n.get_metadata_for(u"c2"))
390             def _has_good_linkcrtime(metadata):
391                 self.failUnless(metadata.has_key('tahoe'))
392                 self.failUnless(metadata['tahoe'].has_key('linkcrtime'))
393                 self.failIfEqual(metadata['tahoe']['linkcrtime'], 'bogus')
394             d.addCallback(_has_good_linkcrtime)
395
396             # if we don't set any defaults, the child should get timestamps
397             d.addCallback(lambda res: n.set_uri(u"c3",
398                                                 fake_file_uri, fake_file_uri))
399             d.addCallback(lambda res: n.get_metadata_for(u"c3"))
400             d.addCallback(lambda metadata:
401                           self.failUnlessEqual(set(metadata.keys()),
402                                                set(["tahoe", "ctime", "mtime"])))
403
404             # or we can add specific metadata at set_uri() time, which
405             # overrides the timestamps
406             d.addCallback(lambda res: n.set_uri(u"c4",
407                                                 fake_file_uri, fake_file_uri,
408                                                 {"key": "value"}))
409             d.addCallback(lambda res: n.get_metadata_for(u"c4"))
410             d.addCallback(lambda metadata:
411                               self.failUnless((set(metadata.keys()) == set(["key", "tahoe"])) and
412                                               (metadata['key'] == "value"), metadata))
413
414             d.addCallback(lambda res: n.delete(u"c2"))
415             d.addCallback(lambda res: n.delete(u"c3"))
416             d.addCallback(lambda res: n.delete(u"c4"))
417
418             # set_node + metadata
419             # it should be possible to add a child without any metadata
420             d.addCallback(lambda res: n.set_node(u"d2", n, {}))
421             d.addCallback(lambda res: c.create_dirnode())
422             d.addCallback(lambda n2:
423                           self.shouldFail(ExistingChildError, "set_node-no",
424                                           "child 'd2' already exists",
425                                           n.set_node, u"d2", n2,
426                                           overwrite=False))
427             d.addCallback(lambda res: n.get_metadata_for(u"d2"))
428             d.addCallback(lambda metadata: self.failUnlessEqual(metadata.keys(), ['tahoe']))
429
430             # if we don't set any defaults, the child should get timestamps
431             d.addCallback(lambda res: n.set_node(u"d3", n))
432             d.addCallback(lambda res: n.get_metadata_for(u"d3"))
433             d.addCallback(lambda metadata:
434                           self.failUnlessEqual(set(metadata.keys()),
435                                                set(["tahoe", "ctime", "mtime"])))
436
437             # or we can add specific metadata at set_node() time, which
438             # overrides the timestamps
439             d.addCallback(lambda res: n.set_node(u"d4", n,
440                                                 {"key": "value"}))
441             d.addCallback(lambda res: n.get_metadata_for(u"d4"))
442             d.addCallback(lambda metadata:
443                           self.failUnless((set(metadata.keys()) == set(["key", "tahoe"])) and
444                                           (metadata['key'] == "value"), metadata))
445
446             d.addCallback(lambda res: n.delete(u"d2"))
447             d.addCallback(lambda res: n.delete(u"d3"))
448             d.addCallback(lambda res: n.delete(u"d4"))
449
450             # metadata through set_children()
451             d.addCallback(lambda res:
452                           n.set_children({
453                               u"e1": (fake_file_uri, fake_file_uri),
454                               u"e2": (fake_file_uri, fake_file_uri, {}),
455                               u"e3": (fake_file_uri, fake_file_uri,
456                                       {"key": "value"}),
457                               }))
458             d.addCallback(lambda n2: self.failUnlessIdentical(n2, n))
459             d.addCallback(lambda res:
460                           self.shouldFail(ExistingChildError, "set_children-no",
461                                           "child 'e1' already exists",
462                                           n.set_children,
463                                           { u"e1": (other_file_uri,
464                                                     other_file_uri),
465                                             u"new": (other_file_uri,
466                                                      other_file_uri),
467                                             },
468                                           overwrite=False))
469             # and 'new' should not have been created
470             d.addCallback(lambda res: n.list())
471             d.addCallback(lambda children: self.failIf(u"new" in children))
472             d.addCallback(lambda res: n.get_metadata_for(u"e1"))
473             d.addCallback(lambda metadata:
474                           self.failUnlessEqual(set(metadata.keys()),
475                                                set(["tahoe", "ctime", "mtime"])))
476             d.addCallback(lambda res: n.get_metadata_for(u"e2"))
477             d.addCallback(lambda metadata:
478                           self.failUnlessEqual(set(metadata.keys()), set(['tahoe'])))
479             d.addCallback(lambda res: n.get_metadata_for(u"e3"))
480             d.addCallback(lambda metadata:
481                               self.failUnless((set(metadata.keys()) == set(["key", "tahoe"]))
482                                               and (metadata['key'] == "value"), metadata))
483
484             d.addCallback(lambda res: n.delete(u"e1"))
485             d.addCallback(lambda res: n.delete(u"e2"))
486             d.addCallback(lambda res: n.delete(u"e3"))
487
488             # metadata through set_nodes()
489             d.addCallback(lambda res: n.set_nodes([ (u"f1", n),
490                                                     (u"f2", n, {}),
491                                                     (u"f3", n,
492                                                      {"key": "value"}),
493                                                     ]))
494             d.addCallback(lambda n2: self.failUnlessIdentical(n2, n))
495             d.addCallback(lambda res:
496                           self.shouldFail(ExistingChildError, "set_nodes-no",
497                                           "child 'f1' already exists",
498                                           n.set_nodes,
499                                           [ (u"f1", n),
500                                             (u"new", n), ],
501                                           overwrite=False))
502             # and 'new' should not have been created
503             d.addCallback(lambda res: n.list())
504             d.addCallback(lambda children: self.failIf(u"new" in children))
505             d.addCallback(lambda res: n.get_metadata_for(u"f1"))
506             d.addCallback(lambda metadata:
507                           self.failUnlessEqual(set(metadata.keys()),
508                                                set(["tahoe", "ctime", "mtime"])))
509             d.addCallback(lambda res: n.get_metadata_for(u"f2"))
510             d.addCallback(
511                 lambda metadata: self.failUnlessEqual(set(metadata.keys()), set(['tahoe'])))
512             d.addCallback(lambda res: n.get_metadata_for(u"f3"))
513             d.addCallback(lambda metadata:
514                               self.failUnless((set(metadata.keys()) == set(["key", "tahoe"])) and
515                                               (metadata['key'] == "value"), metadata))
516
517             d.addCallback(lambda res: n.delete(u"f1"))
518             d.addCallback(lambda res: n.delete(u"f2"))
519             d.addCallback(lambda res: n.delete(u"f3"))
520
521
522             d.addCallback(lambda res:
523                           n.set_metadata_for(u"child",
524                                              {"tags": ["web2.0-compatible"]}))
525             d.addCallback(lambda n1: n1.get_metadata_for(u"child"))
526             d.addCallback(lambda metadata:
527                           self.failUnlessEqual(metadata,
528                                                {"tags": ["web2.0-compatible"]}))
529
530             def _start(res):
531                 self._start_timestamp = time.time()
532             d.addCallback(_start)
533             # simplejson-1.7.1 (as shipped on Ubuntu 'gutsy') rounds all
534             # floats to hundredeths (it uses str(num) instead of repr(num)).
535             # simplejson-1.7.3 does not have this bug. To prevent this bug
536             # from causing the test to fail, stall for more than a few
537             # hundrededths of a second.
538             d.addCallback(self.stall, 0.1)
539             d.addCallback(lambda res: n.add_file(u"timestamps",
540                                                  upload.Data("stamp me", convergence="some convergence string")))
541             d.addCallback(self.stall, 0.1)
542             def _stop(res):
543                 self._stop_timestamp = time.time()
544             d.addCallback(_stop)
545
546             d.addCallback(lambda res: n.get_metadata_for(u"timestamps"))
547             def _check_timestamp1(metadata):
548                 self.failUnless("ctime" in metadata)
549                 self.failUnless("mtime" in metadata)
550                 self.failUnlessGreaterOrEqualThan(metadata["ctime"],
551                                                   self._start_timestamp)
552                 self.failUnlessGreaterOrEqualThan(self._stop_timestamp,
553                                                   metadata["ctime"])
554                 self.failUnlessGreaterOrEqualThan(metadata["mtime"],
555                                                   self._start_timestamp)
556                 self.failUnlessGreaterOrEqualThan(self._stop_timestamp,
557                                                   metadata["mtime"])
558                 # Our current timestamp rules say that replacing an existing
559                 # child should preserve the 'ctime' but update the mtime
560                 self._old_ctime = metadata["ctime"]
561                 self._old_mtime = metadata["mtime"]
562             d.addCallback(_check_timestamp1)
563             d.addCallback(self.stall, 2.0) # accomodate low-res timestamps
564             d.addCallback(lambda res: n.set_node(u"timestamps", n))
565             d.addCallback(lambda res: n.get_metadata_for(u"timestamps"))
566             def _check_timestamp2(metadata):
567                 self.failUnlessEqual(metadata["ctime"], self._old_ctime,
568                                      "%s != %s" % (metadata["ctime"],
569                                                    self._old_ctime))
570                 self.failUnlessGreaterThan(metadata["mtime"], self._old_mtime)
571                 return n.delete(u"timestamps")
572             d.addCallback(_check_timestamp2)
573
574             # also make sure we can add/update timestamps on a
575             # previously-existing child that didn't have any, since there are
576             # a lot of 0.7.0-generated edges around out there
577             d.addCallback(lambda res: n.set_node(u"no_timestamps", n, {}))
578             d.addCallback(lambda res: n.set_node(u"no_timestamps", n))
579             d.addCallback(lambda res: n.get_metadata_for(u"no_timestamps"))
580             d.addCallback(lambda metadata:
581                           self.failUnlessEqual(set(metadata.keys()),
582                                                set(["tahoe", "ctime", "mtime"])))
583             d.addCallback(lambda res: n.delete(u"no_timestamps"))
584
585             d.addCallback(lambda res: n.delete(u"subdir"))
586             d.addCallback(lambda old_child:
587                           self.failUnlessEqual(old_child.get_uri(),
588                                                self.subdir.get_uri()))
589
590             d.addCallback(lambda res: n.list())
591             d.addCallback(lambda children:
592                           self.failUnlessEqual(sorted(children.keys()),
593                                                sorted([u"child"])))
594
595             uploadable1 = upload.Data("some data", convergence="converge")
596             d.addCallback(lambda res: n.add_file(u"newfile", uploadable1))
597             d.addCallback(lambda newnode:
598                           self.failUnless(IFileNode.providedBy(newnode)))
599             uploadable2 = upload.Data("some data", convergence="stuff")
600             d.addCallback(lambda res:
601                           self.shouldFail(ExistingChildError, "add_file-no",
602                                           "child 'newfile' already exists",
603                                           n.add_file, u"newfile",
604                                           uploadable2,
605                                           overwrite=False))
606             d.addCallback(lambda res: n.list())
607             d.addCallback(lambda children:
608                           self.failUnlessEqual(sorted(children.keys()),
609                                                sorted([u"child", u"newfile"])))
610             d.addCallback(lambda res: n.get_metadata_for(u"newfile"))
611             d.addCallback(lambda metadata:
612                           self.failUnlessEqual(set(metadata.keys()),
613                                                set(["tahoe", "ctime", "mtime"])))
614
615             uploadable3 = upload.Data("some data", convergence="converge")
616             d.addCallback(lambda res: n.add_file(u"newfile-metadata",
617                                                  uploadable3,
618                                                  {"key": "value"}))
619             d.addCallback(lambda newnode:
620                           self.failUnless(IFileNode.providedBy(newnode)))
621             d.addCallback(lambda res: n.get_metadata_for(u"newfile-metadata"))
622             d.addCallback(lambda metadata:
623                               self.failUnless((set(metadata.keys()) == set(["key", "tahoe"])) and
624                                               (metadata['key'] == "value"), metadata))
625             d.addCallback(lambda res: n.delete(u"newfile-metadata"))
626
627             d.addCallback(lambda res: n.create_subdirectory(u"subdir2"))
628             def _created2(subdir2):
629                 self.subdir2 = subdir2
630                 # put something in the way, to make sure it gets overwritten
631                 return subdir2.add_file(u"child", upload.Data("overwrite me",
632                                                               "converge"))
633             d.addCallback(_created2)
634
635             d.addCallback(lambda res:
636                           n.move_child_to(u"child", self.subdir2))
637             d.addCallback(lambda res: n.list())
638             d.addCallback(lambda children:
639                           self.failUnlessEqual(sorted(children.keys()),
640                                                sorted([u"newfile", u"subdir2"])))
641             d.addCallback(lambda res: self.subdir2.list())
642             d.addCallback(lambda children:
643                           self.failUnlessEqual(sorted(children.keys()),
644                                                sorted([u"child"])))
645             d.addCallback(lambda res: self.subdir2.get(u"child"))
646             d.addCallback(lambda child:
647                           self.failUnlessEqual(child.get_uri(),
648                                                fake_file_uri))
649
650             # move it back, using new_child_name=
651             d.addCallback(lambda res:
652                           self.subdir2.move_child_to(u"child", n, u"newchild"))
653             d.addCallback(lambda res: n.list())
654             d.addCallback(lambda children:
655                           self.failUnlessEqual(sorted(children.keys()),
656                                                sorted([u"newchild", u"newfile",
657                                                        u"subdir2"])))
658             d.addCallback(lambda res: self.subdir2.list())
659             d.addCallback(lambda children:
660                           self.failUnlessEqual(sorted(children.keys()), []))
661
662             # now make sure that we honor overwrite=False
663             d.addCallback(lambda res:
664                           self.subdir2.set_uri(u"newchild",
665                                                other_file_uri, other_file_uri))
666
667             d.addCallback(lambda res:
668                           self.shouldFail(ExistingChildError, "move_child_to-no",
669                                           "child 'newchild' already exists",
670                                           n.move_child_to, u"newchild",
671                                           self.subdir2,
672                                           overwrite=False))
673             d.addCallback(lambda res: self.subdir2.get(u"newchild"))
674             d.addCallback(lambda child:
675                           self.failUnlessEqual(child.get_uri(),
676                                                other_file_uri))
677
678             return d
679
680         d.addCallback(_then)
681
682         d.addErrback(self.explain_error)
683         return d
684
685     def test_create_subdirectory(self):
686         self.basedir = "dirnode/Dirnode/test_create_subdirectory"
687         self.set_up_grid()
688         c = self.g.clients[0]
689
690         d = c.create_dirnode()
691         def _then(n):
692             # /
693             self.rootnode = n
694             fake_file_uri = make_mutable_file_uri()
695             other_file_uri = make_mutable_file_uri()
696             md = {"metakey": "metavalue"}
697             kids = {u"kid1": (fake_file_uri, fake_file_uri),
698                     u"kid2": (other_file_uri, other_file_uri, md),
699                     }
700             d = n.create_subdirectory(u"subdir", kids)
701             def _check(sub):
702                 d = n.get_child_at_path(u"subdir")
703                 d.addCallback(lambda sub2: self.failUnlessEqual(sub2.get_uri(),
704                                                                 sub.get_uri()))
705                 d.addCallback(lambda ign: sub.list())
706                 return d
707             d.addCallback(_check)
708             def _check_kids(kids2):
709                 self.failUnlessEqual(sorted(kids.keys()), sorted(kids2.keys()))
710                 self.failUnlessEqual(kids2[u"kid2"][1]["metakey"], "metavalue")
711             d.addCallback(_check_kids)
712             return d
713         d.addCallback(_then)
714         return d
715
716 class Packing(unittest.TestCase):
717     # This is a base32-encoded representation of the directory tree
718     # root/file1
719     # root/file2
720     # root/file3
721     # as represented after being fed to _pack_contents.
722     # We have it here so we can decode it, feed it to
723     # _unpack_contents, and verify that _unpack_contents
724     # works correctly.
725
726     known_tree = "GM4TOORVHJTGS3DFGEWDSNJ2KVJESOSDJBFTU33MPB2GS3LZNVYG6N3GGI3WU5TIORTXC3DOMJ2G4NB2MVWXUZDONBVTE5LNGRZWK2LYN55GY23XGNYXQMTOMZUWU5TENN4DG23ZG5UTO2L2NQ2DO6LFMRWDMZJWGRQTUMZ2GEYDUMJQFQYTIMZ22XZKZORX5XS7CAQCSK3URR6QOHISHRCMGER5LRFSZRNAS5ZSALCS6TWFQAE754IVOIKJVK73WZPP3VUUEDTX3WHTBBZ5YX3CEKHCPG3ZWQLYA4QM6LDRCF7TJQYWLIZHKGN5ROA3AUZPXESBNLQQ6JTC2DBJU2D47IZJTLR3PKZ4RVF57XLPWY7FX7SZV3T6IJ3ORFW37FXUPGOE3ROPFNUX5DCGMAQJ3PGGULBRGM3TU6ZCMN2GS3LFEI5CAMJSGQ3DMNRTHA4TOLRUGI3TKNRWGEWCAITUMFUG6ZJCHIQHWITMNFXGW3LPORUW2ZJCHIQDCMRUGY3DMMZYHE3S4NBSG42TMNRRFQQCE3DJNZVWG4TUNFWWKIR2EAYTENBWGY3DGOBZG4XDIMRXGU3DMML5FQQCE3LUNFWWKIR2EAYTENBWGY3DGOBZG4XDIMRXGU3DMML5FQWDGOJRHI2TUZTJNRSTELBZGQ5FKUSJHJBUQSZ2MFYGKZ3SOBSWQ43IO52WO23CNAZWU3DUGVSWSNTIOE5DK33POVTW4ZLNMNWDK6DHPA2GS2THNF2W25DEN5VGY2LQNFRGG5DKNNRHO5TZPFTWI6LNMRYGQ2LCGJTHM4J2GM5DCMB2GQWDCNBSHKVVQBGRYMACKJ27CVQ6O6B4QPR72RFVTGOZUI76XUSWAX73JRV5PYRHMIFYZIA25MXDPGUGML6M2NMRSG4YD4W4K37ZDYSXHMJ3IUVT4F64YTQQVBJFFFOUC7J7LAB2VFCL5UKKGMR2D3F4EPOYC7UYWQZNR5KXHBSNXLCNBX2SNF22DCXJIHSMEKWEWOG5XCJEVVZ7UW5IB6I64XXQSJ34B5CAYZGZIIMR6LBRGMZTU6ZCMN2GS3LFEI5CAMJSGQ3DMNRTHA4TOLRUGMYDEMJYFQQCE5DBNBXWKIR2EB5SE3DJNZVW233UNFWWKIR2EAYTENBWGY3DGOBZG4XDIMZQGIYTQLBAEJWGS3TLMNZHI2LNMURDUIBRGI2DMNRWGM4DSNZOGQZTAMRRHB6SYIBCNV2GS3LFEI5CAMJSGQ3DMNRTHA4TOLRUGMYDEMJYPUWCYMZZGU5DKOTGNFWGKMZMHE2DUVKSJE5EGSCLHJRW25DDPBYTO2DXPB3GM6DBNYZTI6LJMV3DM2LWNB4TU4LWMNSWW3LKORXWK5DEMN3TI23NNE3WEM3SORRGY5THPA3TKNBUMNZG453BOF2GSZLXMVWWI3DJOFZW623RHIZTUMJQHI2SYMJUGI5BOSHWDPG3WKPAVXCF3XMKA7QVIWPRMWJHDTQHD27AHDCPJWDQENQ5H5ZZILTXQNIXXCIW4LKQABU2GCFRG5FHQN7CHD7HF4EKNRZFIV2ZYQIBM7IQU7F4RGB3XCX3FREPBKQ7UCICHVWPCYFGA6OLH3J45LXQ6GWWICJ3PGWJNLZ7PCRNLAPNYUGU6BENS7OXMBEOOFRIZV3PF2FFWZ5WHDPKXERYP7GNHKRMGEZTOOT3EJRXI2LNMURDUIBRGI2DMNRWGM4DSNZOGQZTGNRSGY4SYIBCORQWQ33FEI5CA6ZCNRUW423NN52GS3LFEI5CAMJSGQ3DMNRTHA4TOLRUGMZTMMRWHEWCAITMNFXGWY3SORUW2ZJCHIQDCMRUGY3DMMZYHE3S4NBTGM3DENRZPUWCAITNORUW2ZJCHIQDCMRUGY3DMMZYHE3S4NBTGM3DENRZPUWCY==="
727
728     def test_unpack_and_pack_behavior(self):
729         known_tree = b32decode(self.known_tree)
730         nodemaker = NodeMaker(None, None, None,
731                               None, None, None,
732                               {"k": 3, "n": 10}, None)
733         writecap = "URI:SSK-RO:e3mdrzfwhoq42hy5ubcz6rp3o4:ybyibhnp3vvwuq2vaw2ckjmesgkklfs6ghxleztqidihjyofgw7q"
734         filenode = nodemaker.create_from_cap(writecap)
735         node = dirnode.DirectoryNode(filenode, nodemaker, None)
736         children = node._unpack_contents(known_tree)
737         self._check_children(children)
738
739         packed_children = node._pack_contents(children)
740         children = node._unpack_contents(packed_children)
741         self._check_children(children)
742
743     def _check_children(self, children):
744         # Are all the expected child nodes there?
745         self.failUnless(children.has_key(u'file1'))
746         self.failUnless(children.has_key(u'file2'))
747         self.failUnless(children.has_key(u'file3'))
748
749         # Are the metadata for child 3 right?
750         file3_rocap = "URI:CHK:cmtcxq7hwxvfxan34yiev6ivhy:qvcekmjtoetdcw4kmi7b3rtblvgx7544crnwaqtiewemdliqsokq:3:10:5"
751         file3_rwcap = "URI:CHK:cmtcxq7hwxvfxan34yiev6ivhy:qvcekmjtoetdcw4kmi7b3rtblvgx7544crnwaqtiewemdliqsokq:3:10:5"
752         file3_metadata = {'ctime': 1246663897.4336269, 'tahoe': {'linkmotime': 1246663897.4336269, 'linkcrtime': 1246663897.4336269}, 'mtime': 1246663897.4336269}
753         self.failUnlessEqual(file3_metadata, children[u'file3'][1])
754         self.failUnlessEqual(file3_rocap,
755                              children[u'file3'][0].get_readonly_uri())
756         self.failUnlessEqual(file3_rwcap,
757                              children[u'file3'][0].get_uri())
758
759         # Are the metadata for child 2 right?
760         file2_rocap = "URI:CHK:apegrpehshwugkbh3jlt5ei6hq:5oougnemcl5xgx4ijgiumtdojlipibctjkbwvyygdymdphib2fvq:3:10:4"
761         file2_rwcap = "URI:CHK:apegrpehshwugkbh3jlt5ei6hq:5oougnemcl5xgx4ijgiumtdojlipibctjkbwvyygdymdphib2fvq:3:10:4"
762         file2_metadata = {'ctime': 1246663897.430218, 'tahoe': {'linkmotime': 1246663897.430218, 'linkcrtime': 1246663897.430218}, 'mtime': 1246663897.430218}
763         self.failUnlessEqual(file2_metadata, children[u'file2'][1])
764         self.failUnlessEqual(file2_rocap,
765                              children[u'file2'][0].get_readonly_uri())
766         self.failUnlessEqual(file2_rwcap,
767                              children[u'file2'][0].get_uri())
768
769         # Are the metadata for child 1 right?
770         file1_rocap = "URI:CHK:olxtimympo7f27jvhtgqlnbtn4:emzdnhk2um4seixozlkw3qx2nfijvdkx3ky7i7izl47yedl6e64a:3:10:10"
771         file1_rwcap = "URI:CHK:olxtimympo7f27jvhtgqlnbtn4:emzdnhk2um4seixozlkw3qx2nfijvdkx3ky7i7izl47yedl6e64a:3:10:10"
772         file1_metadata = {'ctime': 1246663897.4275661, 'tahoe': {'linkmotime': 1246663897.4275661, 'linkcrtime': 1246663897.4275661}, 'mtime': 1246663897.4275661}
773         self.failUnlessEqual(file1_metadata, children[u'file1'][1])
774         self.failUnlessEqual(file1_rocap,
775                              children[u'file1'][0].get_readonly_uri())
776         self.failUnlessEqual(file1_rwcap,
777                              children[u'file1'][0].get_uri())
778
779 class FakeMutableFile:
780     counter = 0
781     def __init__(self, initial_contents=""):
782         self.data = initial_contents
783         counter = FakeMutableFile.counter
784         FakeMutableFile.counter += 1
785         writekey = hashutil.ssk_writekey_hash(str(counter))
786         fingerprint = hashutil.ssk_pubkey_fingerprint_hash(str(counter))
787         self.uri = uri.WriteableSSKFileURI(writekey, fingerprint)
788     def get_uri(self):
789         return self.uri.to_string()
790     def download_best_version(self):
791         return defer.succeed(self.data)
792     def get_writekey(self):
793         return "writekey"
794     def is_readonly(self):
795         return False
796     def is_mutable(self):
797         return True
798     def modify(self, modifier):
799         self.data = modifier(self.data, None, True)
800         return defer.succeed(None)
801
802 class FakeNodeMaker(NodeMaker):
803     def create_mutable_file(self, contents="", keysize=None):
804         return defer.succeed(FakeMutableFile(contents))
805
806 class FakeClient2(Client):
807     def __init__(self):
808         self.nodemaker = FakeNodeMaker(None, None, None,
809                                        None, None, None,
810                                        {"k":3,"n":10}, None)
811     def create_node_from_uri(self, rwcap, rocap):
812         return self.nodemaker.create_from_cap(rwcap, rocap)
813
814 class Dirnode2(unittest.TestCase, testutil.ShouldFailMixin):
815     def setUp(self):
816         self.client = FakeClient2()
817         self.nodemaker = self.client.nodemaker
818
819     def test_from_future(self):
820         # create a dirnode that contains unknown URI types, and make sure we
821         # tolerate them properly. Since dirnodes aren't allowed to add
822         # unknown node types, we have to be tricky.
823         d = self.nodemaker.create_new_mutable_directory()
824         future_writecap = "x-tahoe-crazy://I_am_from_the_future."
825         future_readcap = "x-tahoe-crazy-readonly://I_am_from_the_future."
826         future_node = UnknownNode(future_writecap, future_readcap)
827         def _then(n):
828             self._node = n
829             return n.set_node(u"future", future_node)
830         d.addCallback(_then)
831
832         # we should be prohibited from adding an unknown URI to a directory,
833         # since we don't know how to diminish the cap to a readcap (for the
834         # dirnode's rocap slot), and we don't want to accidentally grant
835         # write access to a holder of the dirnode's readcap.
836         d.addCallback(lambda ign:
837              self.shouldFail(CannotPackUnknownNodeError,
838                              "copy unknown",
839                              "cannot pack unknown node as child add",
840                              self._node.set_uri, u"add",
841                              future_writecap, future_readcap))
842         d.addCallback(lambda ign: self._node.list())
843         def _check(children):
844             self.failUnlessEqual(len(children), 1)
845             (fn, metadata) = children[u"future"]
846             self.failUnless(isinstance(fn, UnknownNode), fn)
847             self.failUnlessEqual(fn.get_uri(), future_writecap)
848             self.failUnlessEqual(fn.get_readonly_uri(), future_readcap)
849             # but we *should* be allowed to copy this node, because the
850             # UnknownNode contains all the information that was in the
851             # original directory (readcap and writecap), so we're preserving
852             # everything.
853             return self._node.set_node(u"copy", fn)
854         d.addCallback(_check)
855         d.addCallback(lambda ign: self._node.list())
856         def _check2(children):
857             self.failUnlessEqual(len(children), 2)
858             (fn, metadata) = children[u"copy"]
859             self.failUnless(isinstance(fn, UnknownNode), fn)
860             self.failUnlessEqual(fn.get_uri(), future_writecap)
861             self.failUnlessEqual(fn.get_readonly_uri(), future_readcap)
862         return d
863
864 class DeepStats(unittest.TestCase):
865     timeout = 240 # It takes longer than 120 seconds on Francois's arm box.
866     def test_stats(self):
867         ds = dirnode.DeepStats(None)
868         ds.add("count-files")
869         ds.add("size-immutable-files", 123)
870         ds.histogram("size-files-histogram", 123)
871         ds.max("largest-directory", 444)
872
873         s = ds.get_results()
874         self.failUnlessEqual(s["count-files"], 1)
875         self.failUnlessEqual(s["size-immutable-files"], 123)
876         self.failUnlessEqual(s["largest-directory"], 444)
877         self.failUnlessEqual(s["count-literal-files"], 0)
878
879         ds.add("count-files")
880         ds.add("size-immutable-files", 321)
881         ds.histogram("size-files-histogram", 321)
882         ds.max("largest-directory", 2)
883
884         s = ds.get_results()
885         self.failUnlessEqual(s["count-files"], 2)
886         self.failUnlessEqual(s["size-immutable-files"], 444)
887         self.failUnlessEqual(s["largest-directory"], 444)
888         self.failUnlessEqual(s["count-literal-files"], 0)
889         self.failUnlessEqual(s["size-files-histogram"],
890                              [ (101, 316, 1), (317, 1000, 1) ])
891
892         ds = dirnode.DeepStats(None)
893         for i in range(1, 1100):
894             ds.histogram("size-files-histogram", i)
895         ds.histogram("size-files-histogram", 4*1000*1000*1000*1000) # 4TB
896         s = ds.get_results()
897         self.failUnlessEqual(s["size-files-histogram"],
898                              [ (1, 3, 3),
899                                (4, 10, 7),
900                                (11, 31, 21),
901                                (32, 100, 69),
902                                (101, 316, 216),
903                                (317, 1000, 684),
904                                (1001, 3162, 99),
905                                (3162277660169L, 10000000000000L, 1),
906                                ])
907
908 class UCWEingMutableFileNode(MutableFileNode):
909     please_ucwe_after_next_upload = False
910
911     def _upload(self, new_contents, servermap):
912         d = MutableFileNode._upload(self, new_contents, servermap)
913         def _ucwe(res):
914             if self.please_ucwe_after_next_upload:
915                 self.please_ucwe_after_next_upload = False
916                 raise UncoordinatedWriteError()
917             return res
918         d.addCallback(_ucwe)
919         return d
920
921 class UCWEingNodeMaker(NodeMaker):
922     def _create_mutable(self, cap):
923         n = UCWEingMutableFileNode(self.storage_broker, self.secret_holder,
924                                    self.default_encoding_parameters,
925                                    self.history)
926         return n.init_from_uri(cap)
927
928
929 class Deleter(GridTestMixin, unittest.TestCase):
930     timeout = 3600 # It takes longer than 433 seconds on Zandr's ARM box.
931     def test_retry(self):
932         # ticket #550, a dirnode.delete which experiences an
933         # UncoordinatedWriteError will fail with an incorrect "you're
934         # deleting something which isn't there" NoSuchChildError exception.
935
936         # to trigger this, we start by creating a directory with a single
937         # file in it. Then we create a special dirnode that uses a modified
938         # MutableFileNode which will raise UncoordinatedWriteError once on
939         # demand. We then call dirnode.delete, which ought to retry and
940         # succeed.
941
942         self.basedir = self.mktemp()
943         self.set_up_grid()
944         c0 = self.g.clients[0]
945         d = c0.create_dirnode()
946         small = upload.Data("Small enough for a LIT", None)
947         def _created_dir(dn):
948             self.root = dn
949             self.root_uri = dn.get_uri()
950             return dn.add_file(u"file", small)
951         d.addCallback(_created_dir)
952         def _do_delete(ignored):
953             nm = UCWEingNodeMaker(c0.storage_broker, c0._secret_holder,
954                                   c0.get_history(), c0.getServiceNamed("uploader"),
955                                   c0.downloader,
956                                   c0.download_cache_dirman,
957                                   c0.get_encoding_parameters(),
958                                   c0._key_generator)
959             n = nm.create_from_cap(self.root_uri)
960             assert n._node.please_ucwe_after_next_upload == False
961             n._node.please_ucwe_after_next_upload = True
962             # This should succeed, not raise an exception
963             return n.delete(u"file")
964         d.addCallback(_do_delete)
965
966         return d
967
968 class Adder(GridTestMixin, unittest.TestCase, testutil.ShouldFailMixin):
969
970     def test_overwrite(self):
971         # note: This functionality could be tested without actually creating
972         # several RSA keys. It would be faster without the GridTestMixin: use
973         # dn.set_node(nodemaker.create_from_cap(make_chk_file_uri())) instead
974         # of dn.add_file, and use a special NodeMaker that creates fake
975         # mutable files.
976         self.basedir = "dirnode/Adder/test_overwrite"
977         self.set_up_grid()
978         c = self.g.clients[0]
979         fileuri = make_chk_file_uri(1234)
980         filenode = c.nodemaker.create_from_cap(fileuri)
981         d = c.create_dirnode()
982
983         def _create_directory_tree(root_node):
984             # Build
985             # root/file1
986             # root/file2
987             # root/dir1
988             d = root_node.add_file(u'file1', upload.Data("Important Things",
989                 None))
990             d.addCallback(lambda res:
991                 root_node.add_file(u'file2', upload.Data("Sekrit Codes", None)))
992             d.addCallback(lambda res:
993                 root_node.create_subdirectory(u"dir1"))
994             d.addCallback(lambda res: root_node)
995             return d
996
997         d.addCallback(_create_directory_tree)
998
999         def _test_adder(root_node):
1000             d = root_node.set_node(u'file1', filenode)
1001             # We've overwritten file1. Let's try it with a directory
1002             d.addCallback(lambda res:
1003                 root_node.create_subdirectory(u'dir2'))
1004             d.addCallback(lambda res:
1005                 root_node.set_node(u'dir2', filenode))
1006             # We try overwriting a file with a child while also specifying
1007             # overwrite=False. We should receive an ExistingChildError
1008             # when we do this.
1009             d.addCallback(lambda res:
1010                 self.shouldFail(ExistingChildError, "set_node",
1011                                 "child 'file1' already exists",
1012                                root_node.set_node, u"file1",
1013                                filenode, overwrite=False))
1014             # If we try with a directory, we should see the same thing
1015             d.addCallback(lambda res:
1016                 self.shouldFail(ExistingChildError, "set_node",
1017                                 "child 'dir1' already exists",
1018                                 root_node.set_node, u'dir1', filenode,
1019                                 overwrite=False))
1020             d.addCallback(lambda res:
1021                  root_node.set_node(u'file1', filenode,
1022                                     overwrite="only-files"))
1023             d.addCallback(lambda res:
1024                  self.shouldFail(ExistingChildError, "set_node",
1025                                 "child 'dir1' already exists",
1026                                 root_node.set_node, u'dir1', filenode,
1027                                 overwrite="only-files"))
1028             return d
1029
1030         d.addCallback(_test_adder)
1031         return d