From cf65cc2ae3cc10624479d0e52da6592610ab8899 Mon Sep 17 00:00:00 2001
From: Brian Warner <warner@lothar.com>
Date: Mon, 12 Oct 2009 19:15:20 -0700
Subject: [PATCH] replace dirnode.create_empty_directory() with
 create_subdirectory(), which takes an initial_children= argument

---
 src/allmydata/dirnode.py             |  7 ++---
 src/allmydata/frontends/ftpd.py      |  2 +-
 src/allmydata/frontends/sftpd.py     |  2 +-
 src/allmydata/interfaces.py          | 12 ++++---
 src/allmydata/test/test_cli.py       |  9 ++----
 src/allmydata/test/test_deepcheck.py |  6 ++--
 src/allmydata/test/test_dirnode.py   | 47 +++++++++++++++++++++++-----
 src/allmydata/test/test_system.py    |  8 ++---
 src/allmydata/test/test_web.py       |  2 +-
 src/allmydata/web/directory.py       |  8 ++---
 10 files changed, 67 insertions(+), 36 deletions(-)

diff --git a/src/allmydata/dirnode.py b/src/allmydata/dirnode.py
index c7e6f56f..ba6d5528 100644
--- a/src/allmydata/dirnode.py
+++ b/src/allmydata/dirnode.py
@@ -475,14 +475,13 @@ class DirectoryNode:
         d.addCallback(lambda res: deleter.old_child)
         return d
 
-    def create_empty_directory(self, name, overwrite=True):
-        """I create and attach an empty directory at the given name. I return
-        a Deferred that fires (with the new directory node) when the
-        operation finishes."""
+    def create_subdirectory(self, name, initial_children={}, overwrite=True):
         assert isinstance(name, unicode)
         if self.is_readonly():
             return defer.fail(NotMutableError())
         d = self._nodemaker.create_new_mutable_directory()
+        if initial_children:
+            d.addCallback(lambda n: n.set_children(initial_children))
         def _created(child):
             entries = [(name, child, None)]
             a = Adder(self, entries, overwrite=overwrite)
diff --git a/src/allmydata/frontends/ftpd.py b/src/allmydata/frontends/ftpd.py
index e9447716..4d4256df 100644
--- a/src/allmydata/frontends/ftpd.py
+++ b/src/allmydata/frontends/ftpd.py
@@ -85,7 +85,7 @@ class Handler:
         d = node.get(path[0])
         def _maybe_create(f):
             f.trap(NoSuchChildError)
-            return node.create_empty_directory(path[0])
+            return node.create_subdirectory(path[0])
         d.addErrback(_maybe_create)
         d.addCallback(self._get_or_create_directories, path[1:])
         return d
diff --git a/src/allmydata/frontends/sftpd.py b/src/allmydata/frontends/sftpd.py
index aa15bbba..5a713e1c 100644
--- a/src/allmydata/frontends/sftpd.py
+++ b/src/allmydata/frontends/sftpd.py
@@ -242,7 +242,7 @@ class SFTPHandler:
         d = node.get(path[0])
         def _maybe_create(f):
             f.trap(NoSuchChildError)
-            return node.create_empty_directory(path[0])
+            return node.create_subdirectory(path[0])
         d.addErrback(_maybe_create)
         d.addCallback(self._get_or_create_directories, path[1:])
         return d
diff --git a/src/allmydata/interfaces.py b/src/allmydata/interfaces.py
index 42ec62c0..66d742ee 100644
--- a/src/allmydata/interfaces.py
+++ b/src/allmydata/interfaces.py
@@ -946,10 +946,14 @@ class IDirectoryNode(IMutableFilesystemNode):
         string. I raise NoSuchChildError if I do not have a child by that
         name."""
 
-    def create_empty_directory(name, overwrite=True):
-        """I create and attach an empty directory at the given name. The
-        child name must be a unicode string. I return a Deferred that fires
-        when the operation finishes."""
+    def create_subdirectory(name, initial_children={}, overwrite=True):
+        """I create and attach a directory at the given name. The new
+        directory can be empty, or it can be populated with children
+        according to 'initial_children', which takes a dictionary in the same
+        format as set_children (i.e. mapping unicode child name to (writecap,
+        readcap, metadata) triples). The child name must be a unicode string.
+        I return a Deferred that fires (with the new directory node) when the
+        operation finishes."""
 
     def move_child_to(current_child_name, new_parent, new_child_name=None,
                       overwrite=True):
diff --git a/src/allmydata/test/test_cli.py b/src/allmydata/test/test_cli.py
index 468f0fe7..56a83470 100644
--- a/src/allmydata/test/test_cli.py
+++ b/src/allmydata/test/test_cli.py
@@ -754,12 +754,10 @@ class List(GridTestMixin, CLITestMixin, unittest.TestCase):
             self.rooturi = n.get_uri()
             return n.add_file(u"good", upload.Data("small", convergence=""))
         d.addCallback(_stash_root_and_create_file)
-        d.addCallback(lambda ign:
-                      self.rootnode.create_empty_directory(u"1share"))
+        d.addCallback(lambda ign: self.rootnode.create_subdirectory(u"1share"))
         d.addCallback(lambda n:
                       self.delete_shares_numbered(n.get_uri(), range(1,10)))
-        d.addCallback(lambda ign:
-                      self.rootnode.create_empty_directory(u"0share"))
+        d.addCallback(lambda ign: self.rootnode.create_subdirectory(u"0share"))
         d.addCallback(lambda n:
                       self.delete_shares_numbered(n.get_uri(), range(0,10)))
         d.addCallback(lambda ign:
@@ -1471,8 +1469,7 @@ class Check(GridTestMixin, CLITestMixin, unittest.TestCase):
         # now add a subdir, and a file below that, then make the subdir
         # unrecoverable
 
-        d.addCallback(lambda ign:
-                      self.rootnode.create_empty_directory(u"subdir"))
+        d.addCallback(lambda ign: self.rootnode.create_subdirectory(u"subdir"))
         d.addCallback(_stash_uri, "subdir")
         d.addCallback(lambda fn:
                       fn.add_file(u"subfile", upload.Data(DATA+"2", "")))
diff --git a/src/allmydata/test/test_deepcheck.py b/src/allmydata/test/test_deepcheck.py
index 0ec2ef84..165013c5 100644
--- a/src/allmydata/test/test_deepcheck.py
+++ b/src/allmydata/test/test_deepcheck.py
@@ -887,11 +887,11 @@ class DeepCheckWebBad(DeepCheckBase, unittest.TestCase):
         d.addCallback(lambda ignored:
                       self.nodes["broken"].add_file(u"large1", large1))
         d.addCallback(lambda ignored:
-                      self.nodes["broken"].create_empty_directory(u"subdir-good"))
+                      self.nodes["broken"].create_subdirectory(u"subdir-good"))
         large2 = upload.Data("Lots of data\n" * 1000 + "large2" + "\n", None)
         d.addCallback(lambda subdir: subdir.add_file(u"large2-good", large2))
         d.addCallback(lambda ignored:
-                      self.nodes["broken"].create_empty_directory(u"subdir-unrecoverable"))
+                      self.nodes["broken"].create_subdirectory(u"subdir-unrecoverable"))
         d.addCallback(self._stash_node, "subdir-unrecoverable")
         large3 = upload.Data("Lots of data\n" * 1000 + "large3" + "\n", None)
         d.addCallback(lambda subdir: subdir.add_file(u"large3-good", large3))
@@ -1181,7 +1181,7 @@ class Large(DeepCheckBase, unittest.TestCase):
             self.root = n
             return n
         d.addCallback(_created_root)
-        d.addCallback(lambda root: root.create_empty_directory(u"subdir"))
+        d.addCallback(lambda root: root.create_subdirectory(u"subdir"))
         def _add_children(subdir_node):
             self.subdir_node = subdir_node
             kids = {}
diff --git a/src/allmydata/test/test_dirnode.py b/src/allmydata/test/test_dirnode.py
index 097a9a15..4330ce9e 100644
--- a/src/allmydata/test/test_dirnode.py
+++ b/src/allmydata/test/test_dirnode.py
@@ -58,7 +58,7 @@ class Dirnode(GridTestMixin, unittest.TestCase,
         d = c.create_dirnode()
         def _created_root(rootnode):
             self._rootnode = rootnode
-            return rootnode.create_empty_directory(u"subdir")
+            return rootnode.create_subdirectory(u"subdir")
         d.addCallback(_created_root)
         def _created_subdir(subdir):
             self._subdir = subdir
@@ -182,7 +182,7 @@ class Dirnode(GridTestMixin, unittest.TestCase,
             self.shouldFail(dirnode.NotMutableError, "set_uri ro", None,
                             ro_dn.delete, u"child")
             self.shouldFail(dirnode.NotMutableError, "set_uri ro", None,
-                            ro_dn.create_empty_directory, u"newchild")
+                            ro_dn.create_subdirectory, u"newchild")
             self.shouldFail(dirnode.NotMutableError, "set_metadata_for ro", None,
                             ro_dn.set_metadata_for, u"child", {})
             self.shouldFail(dirnode.NotMutableError, "set_uri ro", None,
@@ -255,7 +255,7 @@ class Dirnode(GridTestMixin, unittest.TestCase,
             # /
             # /child = mutable
 
-            d.addCallback(lambda res: n.create_empty_directory(u"subdir"))
+            d.addCallback(lambda res: n.create_subdirectory(u"subdir"))
 
             # /
             # /child = mutable
@@ -274,7 +274,7 @@ class Dirnode(GridTestMixin, unittest.TestCase,
             d.addCallback(lambda res:
                           self.shouldFail(ExistingChildError, "mkdir-no",
                                           "child 'subdir' already exists",
-                                          n.create_empty_directory, u"subdir",
+                                          n.create_subdirectory, u"subdir",
                                           overwrite=False))
 
             d.addCallback(lambda res: n.list())
@@ -322,7 +322,7 @@ class Dirnode(GridTestMixin, unittest.TestCase,
             d.addCallback(_check_manifest)
 
             def _add_subsubdir(res):
-                return self.subdir.create_empty_directory(u"subsubdir")
+                return self.subdir.create_subdirectory(u"subsubdir")
             d.addCallback(_add_subsubdir)
             # /
             # /child = mutable
@@ -624,7 +624,7 @@ class Dirnode(GridTestMixin, unittest.TestCase,
                                               (metadata['key'] == "value"), metadata))
             d.addCallback(lambda res: n.delete(u"newfile-metadata"))
 
-            d.addCallback(lambda res: n.create_empty_directory(u"subdir2"))
+            d.addCallback(lambda res: n.create_subdirectory(u"subdir2"))
             def _created2(subdir2):
                 self.subdir2 = subdir2
                 # put something in the way, to make sure it gets overwritten
@@ -682,6 +682,37 @@ class Dirnode(GridTestMixin, unittest.TestCase,
         d.addErrback(self.explain_error)
         return d
 
+    def test_create_subdirectory(self):
+        self.basedir = "dirnode/Dirnode/test_create_subdirectory"
+        self.set_up_grid()
+        c = self.g.clients[0]
+
+        d = c.create_dirnode()
+        def _then(n):
+            # /
+            self.rootnode = n
+            fake_file_uri = make_mutable_file_uri()
+            other_file_uri = make_mutable_file_uri()
+            md = {"metakey": "metavalue"}
+            kids = {u"kid1": (fake_file_uri, fake_file_uri),
+                    u"kid2": (other_file_uri, other_file_uri, md),
+                    }
+            d = n.create_subdirectory(u"subdir", kids)
+            def _check(sub):
+                d = n.get_child_at_path(u"subdir")
+                d.addCallback(lambda sub2: self.failUnlessEqual(sub2.get_uri(),
+                                                                sub.get_uri()))
+                d.addCallback(lambda ign: sub.list())
+                return d
+            d.addCallback(_check)
+            def _check_kids(kids2):
+                self.failUnlessEqual(sorted(kids.keys()), sorted(kids2.keys()))
+                self.failUnlessEqual(kids2[u"kid2"][1]["metakey"], "metavalue")
+            d.addCallback(_check_kids)
+            return d
+        d.addCallback(_then)
+        return d
+
 class Packing(unittest.TestCase):
     # This is a base32-encoded representation of the directory tree
     # root/file1
@@ -974,7 +1005,7 @@ class Adder(GridTestMixin, unittest.TestCase, testutil.ShouldFailMixin):
             d.addCallback(lambda res:
                 root_node.add_file(u'file2', upload.Data("Sekrit Codes", None)))
             d.addCallback(lambda res:
-                root_node.create_empty_directory(u"dir1"))
+                root_node.create_subdirectory(u"dir1"))
             d.addCallback(lambda res: root_node)
             return d
 
@@ -984,7 +1015,7 @@ class Adder(GridTestMixin, unittest.TestCase, testutil.ShouldFailMixin):
             d = root_node.set_node(u'file1', filenode)
             # We've overwritten file1. Let's try it with a directory
             d.addCallback(lambda res:
-                root_node.create_empty_directory(u'dir2'))
+                root_node.create_subdirectory(u'dir2'))
             d.addCallback(lambda res:
                 root_node.set_node(u'dir2', filenode))
             # We try overwriting a file with a child while also specifying
diff --git a/src/allmydata/test/test_system.py b/src/allmydata/test/test_system.py
index ed99d795..f419d243 100644
--- a/src/allmydata/test/test_system.py
+++ b/src/allmydata/test/test_system.py
@@ -825,7 +825,7 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
             self._root_directory_uri = new_dirnode.get_uri()
             return c0.create_node_from_uri(self._root_directory_uri)
         d.addCallback(_made_root)
-        d.addCallback(lambda root: root.create_empty_directory(u"subdir1"))
+        d.addCallback(lambda root: root.create_subdirectory(u"subdir1"))
         def _made_subdir1(subdir1_node):
             self._subdir1_node = subdir1_node
             d1 = subdir1_node.add_file(u"mydata567", ut)
@@ -840,7 +840,7 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
 
     def _do_publish2(self, res):
         ut = upload.Data(self.data, convergence=None)
-        d = self._subdir1_node.create_empty_directory(u"subdir2")
+        d = self._subdir1_node.create_subdirectory(u"subdir2")
         d.addCallback(lambda subdir2: subdir2.add_file(u"mydata992", ut))
         return d
 
@@ -856,7 +856,7 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
         d.addCallback(self.log, "GOT private directory")
         def _got_new_dir(privnode):
             rootnode = self.clients[0].create_node_from_uri(self._root_directory_uri)
-            d1 = privnode.create_empty_directory(u"personal")
+            d1 = privnode.create_subdirectory(u"personal")
             d1.addCallback(self.log, "made P/personal")
             d1.addCallback(lambda node: node.add_file(u"sekrit data", ut))
             d1.addCallback(self.log, "made P/personal/sekrit data")
@@ -934,7 +934,7 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
             d1.addCallback(lambda res: dirnode.list())
             d1.addCallback(self.log, "dirnode.list")
 
-            d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "mkdir(nope)", None, dirnode.create_empty_directory, u"nope"))
+            d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "mkdir(nope)", None, dirnode.create_subdirectory, u"nope"))
 
             d1.addCallback(self.log, "doing add_file(ro)")
             ut = upload.Data("I will disappear, unrecorded and unobserved. The tragedy of my demise is made more poignant by its silence, but this beauty is not for you to ever know.", convergence="99i-p1x4-xd4-18yc-ywt-87uu-msu-zo -- completely and totally unguessable string (unless you read this)")
diff --git a/src/allmydata/test/test_web.py b/src/allmydata/test/test_web.py
index b3573dc4..3ac6c660 100644
--- a/src/allmydata/test/test_web.py
+++ b/src/allmydata/test/test_web.py
@@ -2972,7 +2972,7 @@ class Grid(GridTestMixin, WebErrorMixin, unittest.TestCase, ShouldFailMixin):
         # unrecoverable, then see what happens
 
         d.addCallback(lambda ign:
-                      self.rootnode.create_empty_directory(u"subdir"))
+                      self.rootnode.create_subdirectory(u"subdir"))
         d.addCallback(_stash_uri, "subdir")
         d.addCallback(lambda subdir_node:
                       subdir_node.add_file(u"grandchild",
diff --git a/src/allmydata/web/directory.py b/src/allmydata/web/directory.py
index 37dc574b..034b1ac7 100644
--- a/src/allmydata/web/directory.py
+++ b/src/allmydata/web/directory.py
@@ -86,7 +86,7 @@ class DirectoryNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
                 if should_create_intermediate_directories(req):
                     # create intermediate directories
                     if DEBUG: print " making intermediate directory"
-                    d = self.node.create_empty_directory(name)
+                    d = self.node.create_subdirectory(name)
                     d.addCallback(make_handler_for,
                                   self.client, self.node, name)
                     return d
@@ -96,7 +96,7 @@ class DirectoryNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
                 if (method,t) in [ ("POST","mkdir"), ("PUT","mkdir") ]:
                     if DEBUG: print " making final directory"
                     # final directory
-                    d = self.node.create_empty_directory(name)
+                    d = self.node.create_subdirectory(name)
                     d.addCallback(make_handler_for,
                                   self.client, self.node, name)
                     return d
@@ -221,7 +221,7 @@ class DirectoryNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
             return defer.succeed(self.node.get_uri()) # TODO: urlencode
         name = name.decode("utf-8")
         replace = boolean_of_arg(get_arg(req, "replace", "true"))
-        d = self.node.create_empty_directory(name, overwrite=replace)
+        d = self.node.create_subdirectory(name, overwrite=replace)
         d.addCallback(lambda child: child.get_uri()) # TODO: urlencode
         return d
 
@@ -246,7 +246,7 @@ class DirectoryNodeHandler(RenderMixin, rend.Page, ReplaceMeMixin):
         d = node.get(path[0])
         def _maybe_create(f):
             f.trap(NoSuchChildError)
-            return node.create_empty_directory(path[0])
+            return node.create_subdirectory(path[0])
         d.addErrback(_maybe_create)
         d.addCallback(self._get_or_create_directories, path[1:])
         return d
-- 
2.45.2