From f1c3ff62c14e2018c96fabce016bb3b01ced794f Mon Sep 17 00:00:00 2001 From: Brian Warner Date: Thu, 1 Nov 2007 18:35:54 -0700 Subject: [PATCH] mutable: improve NewDirectoryNode test coverage --- src/allmydata/mutable.py | 40 ++++++++++++++----- src/allmydata/test/test_mutable.py | 64 ++++++++++++++++++++++++++---- src/allmydata/uri.py | 13 +++++- 3 files changed, 97 insertions(+), 20 deletions(-) diff --git a/src/allmydata/mutable.py b/src/allmydata/mutable.py index a5e831df..d6757c81 100644 --- a/src/allmydata/mutable.py +++ b/src/allmydata/mutable.py @@ -4,7 +4,8 @@ from zope.interface import implements from twisted.internet import defer import simplejson from allmydata.interfaces import IMutableFileNode, IDirectoryNode,\ - IMutableFileURI, INewDirectoryURI, IURI, IFileNode, NotMutableError + IMutableFileURI, INewDirectoryURI, IURI, IFileNode, NotMutableError, \ + IVerifierURI from allmydata.util import hashutil from allmydata.util.hashutil import netstring from allmydata.dirnode import IntegrityCheckError, FileNode @@ -55,7 +56,7 @@ class MutableFileNode: return cmp(self.uri, them.uri) def get_verifier(self): - return IMutableFileURI(self.uri).get_verifier() + return IMutableFileURI(self._uri).get_verifier() def check(self): verifier = self.get_verifier() @@ -162,7 +163,7 @@ class NewDirectoryNode: return self._client.create_file_from_uri(u) if IMutableFileURI.providedBy(u): return self._client.create_mutable_file_from_uri(u) - raise TypeError("cannot handle URI") + raise TypeError("cannot handle '%s' URI" % (u.__class__,)) def _unpack_contents(self, data): # the directory is serialized as a list of netstrings, one per child. @@ -216,6 +217,9 @@ class NewDirectoryNode: def get_uri(self): return self._uri.to_string() + def get_readonly(self): + return self._uri.get_readonly().to_string() + def get_immutable_uri(self): return self._uri.get_readonly().to_string() @@ -242,7 +246,12 @@ class NewDirectoryNode: """I return a Deferred that fires with a specific named child node, either an IFileNode or an IDirectoryNode.""" d = self._read() - d.addCallback(lambda children: children[name]) + d.addCallback(lambda children: children[name][0]) + return d + + def get_metadata_for(self, name): + d = self._read() + d.addCallback(lambda children: children[name][1]) return d def get_child_at_path(self, path): @@ -315,25 +324,34 @@ class NewDirectoryNode: def delete(self, name): """I remove the child at the specific name. I return a Deferred that - fires when the operation finishes.""" + fires (with the node just removed) when the operation finishes.""" if self.is_readonly(): return defer.fail(NotMutableError()) d = self._read() def _delete(children): + old_child, metadata = children[name] del children[name] new_contents = self._pack_contents(children) - return self._node.replace(new_contents) + d = self._node.replace(new_contents) + def _done(res): + return old_child + d.addCallback(_done) + return d d.addCallback(_delete) - d.addCallback(lambda res: None) return d def create_empty_directory(self, name): """I create and attach an empty directory at the given name. I return - a Deferred that fires when the operation finishes.""" + a Deferred that fires (with the new directory node) when the + operation finishes.""" if self.is_readonly(): return defer.fail(NotMutableError()) d = self._client.create_empty_dirnode() - d.addCallback(lambda child: self.set_node(name, child)) + def _created(child): + d = self.set_node(name, child) + d.addCallback(lambda res: child) + return d + d.addCallback(_created) return d def move_child_to(self, current_child_name, new_parent, @@ -368,7 +386,7 @@ class NewDirectoryNode: # They indicate this by returning None from their get_verifier # method. We need to remove any such Nones from our set. We also # want to convert all these caps into strings. - return frozenset([cap.to_string() + return frozenset([IVerifierURI(cap).to_string() for cap in manifest if cap is not None]) d.addCallback(_done) @@ -378,7 +396,7 @@ class NewDirectoryNode: d = node.list() def _got_list(res): dl = [] - for name, child in res.iteritems(): + for name, (child, metadata) in res.iteritems(): verifier = child.get_verifier() if verifier not in manifest: manifest.add(verifier) diff --git a/src/allmydata/test/test_mutable.py b/src/allmydata/test/test_mutable.py index 0ed5dee4..d7d78f4c 100644 --- a/src/allmydata/test/test_mutable.py +++ b/src/allmydata/test/test_mutable.py @@ -1,4 +1,5 @@ +import itertools from twisted.trial import unittest from twisted.internet import defer @@ -38,18 +39,23 @@ class Netstring(unittest.TestCase): self.failUnlessEqual(bottom, ("hello", "world", "extra stuff")) class FakeFilenode(mutable.MutableFileNode): + counter = itertools.count(1) + all_contents = {} + def init_from_uri(self, myuri): self._uri = myuri self.writekey = myuri.writekey return self def create(self, initial_contents): - self.contents = initial_contents - self.init_from_uri(uri.WriteableSSKFileURI("key", "fingerprint")) + count = self.counter.next() + self.init_from_uri(uri.WriteableSSKFileURI("key%d" % count, + "fingerprint%d" % count)) + self.all_contents[self._uri] = initial_contents return defer.succeed(None) def download_to_data(self): - return defer.succeed(self.contents) + return defer.succeed(self.all_contents[self._uri]) def replace(self, newdata): - self.contents = newdata + self.all_contents[self._uri] = newdata return defer.succeed(None) def is_readonly(self): return False @@ -116,6 +122,8 @@ class Dirnode(unittest.TestCase): self.client = MyClient() def test_create(self): + self.expected_manifest = [] + d = self.client.create_empty_dirnode() def _check(n): self.failUnless(n.is_mutable()) @@ -126,18 +134,60 @@ class Dirnode(unittest.TestCase): self.failUnless(u_ro.startswith("URI:DIR2-RO:"), u_ro) u_v = n.get_verifier() self.failUnless(u_v.startswith("URI:DIR2-Verifier:"), u_v) + self.expected_manifest.append(u_v) d = n.list() d.addCallback(lambda res: self.failUnlessEqual(res, {})) d.addCallback(lambda res: n.has_child("missing")) d.addCallback(lambda res: self.failIf(res)) fake_file_uri = uri.WriteableSSKFileURI("a"*16,"b"*32) + ffu_v = fake_file_uri.get_verifier().to_string() + self.expected_manifest.append(ffu_v) d.addCallback(lambda res: n.set_uri("child", fake_file_uri)) d.addCallback(lambda res: self.failUnlessEqual(res, None)) + + d.addCallback(lambda res: n.create_empty_directory("subdir")) + def _created(subdir): + self.failUnless(isinstance(subdir, FakeNewDirectoryNode)) + self.subdir = subdir + new_v = subdir.get_verifier() + self.expected_manifest.append(new_v) + d.addCallback(_created) + + d.addCallback(lambda res: n.list()) + d.addCallback(lambda children: + self.failUnlessEqual(sorted(children.keys()), + sorted(["child", "subdir"]))) + + d.addCallback(lambda res: n.build_manifest()) + def _check_manifest(manifest): + self.failUnlessEqual(sorted(manifest), + sorted(self.expected_manifest)) + d.addCallback(_check_manifest) + + def _add_subsubdir(res): + return self.subdir.create_empty_directory("subsubdir") + d.addCallback(_add_subsubdir) + d.addCallback(lambda res: n.get_child_at_path("subdir/subsubdir")) + d.addCallback(lambda subsubdir: + self.failUnless(isinstance(subsubdir, + FakeNewDirectoryNode))) + d.addCallback(lambda res: n.get_child_at_path("")) + d.addCallback(lambda res: self.failUnlessEqual(res.get_uri(), + n.get_uri())) + + d.addCallback(lambda res: n.get_metadata_for("child")) + d.addCallback(lambda metadata: self.failUnlessEqual(metadata, {})) + + d.addCallback(lambda res: n.delete("subdir")) + d.addCallback(lambda old_child: + self.failUnlessEqual(old_child.get_uri(), + self.subdir.get_uri())) + d.addCallback(lambda res: n.list()) - def _check_list(children): - self.failUnless("child" in children) - d.addCallback(_check_list) + d.addCallback(lambda children: + self.failUnlessEqual(sorted(children.keys()), + sorted(["child"]))) return d diff --git a/src/allmydata/uri.py b/src/allmydata/uri.py index 127c066c..9a41f40b 100644 --- a/src/allmydata/uri.py +++ b/src/allmydata/uri.py @@ -4,7 +4,7 @@ from zope.interface import implements from twisted.python.components import registerAdapter from allmydata.util import idlib, hashutil from allmydata.interfaces import IURI, IDirnodeURI, IFileURI, IVerifierURI, \ - IMutableFileURI + IMutableFileURI, INewDirectoryURI # the URI shall be an ascii representation of the file. It shall contain # enough information to retrieve and validate the contents. It shall be @@ -272,7 +272,7 @@ class SSKVerifierURI(_BaseURI): idlib.b2a(self.fingerprint)) class NewDirectoryURI(_BaseURI): - implements(IURI, IDirnodeURI) + implements(IURI, IDirnodeURI, INewDirectoryURI) def __init__(self, filenode_uri=None): if filenode_uri: @@ -293,6 +293,9 @@ class NewDirectoryURI(_BaseURI): (header_uri, header_ssk, bits) = fn_u.split(":", 2) return "URI:DIR2:" + bits + def get_filenode_uri(self): + return self._filenode_uri + def is_readonly(self): return False def is_mutable(self): @@ -324,6 +327,9 @@ class ReadonlyNewDirectoryURI(_BaseURI): (header_uri, header_ssk, bits) = fn_u.split(":", 2) return "URI:DIR2-RO:" + bits + def get_filenode_uri(self): + return self._filenode_uri + def is_readonly(self): return True def is_mutable(self): @@ -355,6 +361,9 @@ class NewDirectoryURIVerifier(_BaseURI): (header_uri, header_ssk, bits) = fn_u.split(":", 2) return "URI:DIR2-Verifier:" + bits + def get_filenode_uri(self): + return self._filenode_uri + class DirnodeURI(_BaseURI): -- 2.45.2