mutable: improve NewDirectoryNode test coverage

This commit is contained in:
Brian Warner 2007-11-01 18:35:54 -07:00
parent d777283e9e
commit f1c3ff62c1
3 changed files with 97 additions and 20 deletions

View File

@ -4,7 +4,8 @@ from zope.interface import implements
from twisted.internet import defer from twisted.internet import defer
import simplejson import simplejson
from allmydata.interfaces import IMutableFileNode, IDirectoryNode,\ 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 import hashutil
from allmydata.util.hashutil import netstring from allmydata.util.hashutil import netstring
from allmydata.dirnode import IntegrityCheckError, FileNode from allmydata.dirnode import IntegrityCheckError, FileNode
@ -55,7 +56,7 @@ class MutableFileNode:
return cmp(self.uri, them.uri) return cmp(self.uri, them.uri)
def get_verifier(self): def get_verifier(self):
return IMutableFileURI(self.uri).get_verifier() return IMutableFileURI(self._uri).get_verifier()
def check(self): def check(self):
verifier = self.get_verifier() verifier = self.get_verifier()
@ -162,7 +163,7 @@ class NewDirectoryNode:
return self._client.create_file_from_uri(u) return self._client.create_file_from_uri(u)
if IMutableFileURI.providedBy(u): if IMutableFileURI.providedBy(u):
return self._client.create_mutable_file_from_uri(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): def _unpack_contents(self, data):
# the directory is serialized as a list of netstrings, one per child. # the directory is serialized as a list of netstrings, one per child.
@ -216,6 +217,9 @@ class NewDirectoryNode:
def get_uri(self): def get_uri(self):
return self._uri.to_string() return self._uri.to_string()
def get_readonly(self):
return self._uri.get_readonly().to_string()
def get_immutable_uri(self): def get_immutable_uri(self):
return self._uri.get_readonly().to_string() 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, """I return a Deferred that fires with a specific named child node,
either an IFileNode or an IDirectoryNode.""" either an IFileNode or an IDirectoryNode."""
d = self._read() 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 return d
def get_child_at_path(self, path): def get_child_at_path(self, path):
@ -315,25 +324,34 @@ class NewDirectoryNode:
def delete(self, name): def delete(self, name):
"""I remove the child at the specific name. I return a Deferred that """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(): if self.is_readonly():
return defer.fail(NotMutableError()) return defer.fail(NotMutableError())
d = self._read() d = self._read()
def _delete(children): def _delete(children):
old_child, metadata = children[name]
del children[name] del children[name]
new_contents = self._pack_contents(children) 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(_delete)
d.addCallback(lambda res: None)
return d return d
def create_empty_directory(self, name): def create_empty_directory(self, name):
"""I create and attach an empty directory at the given name. I return """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(): if self.is_readonly():
return defer.fail(NotMutableError()) return defer.fail(NotMutableError())
d = self._client.create_empty_dirnode() 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 return d
def move_child_to(self, current_child_name, new_parent, 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 # They indicate this by returning None from their get_verifier
# method. We need to remove any such Nones from our set. We also # method. We need to remove any such Nones from our set. We also
# want to convert all these caps into strings. # want to convert all these caps into strings.
return frozenset([cap.to_string() return frozenset([IVerifierURI(cap).to_string()
for cap in manifest for cap in manifest
if cap is not None]) if cap is not None])
d.addCallback(_done) d.addCallback(_done)
@ -378,7 +396,7 @@ class NewDirectoryNode:
d = node.list() d = node.list()
def _got_list(res): def _got_list(res):
dl = [] dl = []
for name, child in res.iteritems(): for name, (child, metadata) in res.iteritems():
verifier = child.get_verifier() verifier = child.get_verifier()
if verifier not in manifest: if verifier not in manifest:
manifest.add(verifier) manifest.add(verifier)

View File

@ -1,4 +1,5 @@
import itertools
from twisted.trial import unittest from twisted.trial import unittest
from twisted.internet import defer from twisted.internet import defer
@ -38,18 +39,23 @@ class Netstring(unittest.TestCase):
self.failUnlessEqual(bottom, ("hello", "world", "extra stuff")) self.failUnlessEqual(bottom, ("hello", "world", "extra stuff"))
class FakeFilenode(mutable.MutableFileNode): class FakeFilenode(mutable.MutableFileNode):
counter = itertools.count(1)
all_contents = {}
def init_from_uri(self, myuri): def init_from_uri(self, myuri):
self._uri = myuri self._uri = myuri
self.writekey = myuri.writekey self.writekey = myuri.writekey
return self return self
def create(self, initial_contents): def create(self, initial_contents):
self.contents = initial_contents count = self.counter.next()
self.init_from_uri(uri.WriteableSSKFileURI("key", "fingerprint")) self.init_from_uri(uri.WriteableSSKFileURI("key%d" % count,
"fingerprint%d" % count))
self.all_contents[self._uri] = initial_contents
return defer.succeed(None) return defer.succeed(None)
def download_to_data(self): def download_to_data(self):
return defer.succeed(self.contents) return defer.succeed(self.all_contents[self._uri])
def replace(self, newdata): def replace(self, newdata):
self.contents = newdata self.all_contents[self._uri] = newdata
return defer.succeed(None) return defer.succeed(None)
def is_readonly(self): def is_readonly(self):
return False return False
@ -116,6 +122,8 @@ class Dirnode(unittest.TestCase):
self.client = MyClient() self.client = MyClient()
def test_create(self): def test_create(self):
self.expected_manifest = []
d = self.client.create_empty_dirnode() d = self.client.create_empty_dirnode()
def _check(n): def _check(n):
self.failUnless(n.is_mutable()) self.failUnless(n.is_mutable())
@ -126,18 +134,60 @@ class Dirnode(unittest.TestCase):
self.failUnless(u_ro.startswith("URI:DIR2-RO:"), u_ro) self.failUnless(u_ro.startswith("URI:DIR2-RO:"), u_ro)
u_v = n.get_verifier() u_v = n.get_verifier()
self.failUnless(u_v.startswith("URI:DIR2-Verifier:"), u_v) self.failUnless(u_v.startswith("URI:DIR2-Verifier:"), u_v)
self.expected_manifest.append(u_v)
d = n.list() d = n.list()
d.addCallback(lambda res: self.failUnlessEqual(res, {})) d.addCallback(lambda res: self.failUnlessEqual(res, {}))
d.addCallback(lambda res: n.has_child("missing")) d.addCallback(lambda res: n.has_child("missing"))
d.addCallback(lambda res: self.failIf(res)) d.addCallback(lambda res: self.failIf(res))
fake_file_uri = uri.WriteableSSKFileURI("a"*16,"b"*32) 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: n.set_uri("child", fake_file_uri))
d.addCallback(lambda res: self.failUnlessEqual(res, None)) 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 res: n.list())
def _check_list(children): d.addCallback(lambda children:
self.failUnless("child" in children) self.failUnlessEqual(sorted(children.keys()),
d.addCallback(_check_list) 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())
d.addCallback(lambda children:
self.failUnlessEqual(sorted(children.keys()),
sorted(["child"])))
return d return d

View File

@ -4,7 +4,7 @@ from zope.interface import implements
from twisted.python.components import registerAdapter from twisted.python.components import registerAdapter
from allmydata.util import idlib, hashutil from allmydata.util import idlib, hashutil
from allmydata.interfaces import IURI, IDirnodeURI, IFileURI, IVerifierURI, \ from allmydata.interfaces import IURI, IDirnodeURI, IFileURI, IVerifierURI, \
IMutableFileURI IMutableFileURI, INewDirectoryURI
# the URI shall be an ascii representation of the file. It shall contain # the URI shall be an ascii representation of the file. It shall contain
# enough information to retrieve and validate the contents. It shall be # enough information to retrieve and validate the contents. It shall be
@ -272,7 +272,7 @@ class SSKVerifierURI(_BaseURI):
idlib.b2a(self.fingerprint)) idlib.b2a(self.fingerprint))
class NewDirectoryURI(_BaseURI): class NewDirectoryURI(_BaseURI):
implements(IURI, IDirnodeURI) implements(IURI, IDirnodeURI, INewDirectoryURI)
def __init__(self, filenode_uri=None): def __init__(self, filenode_uri=None):
if filenode_uri: if filenode_uri:
@ -293,6 +293,9 @@ class NewDirectoryURI(_BaseURI):
(header_uri, header_ssk, bits) = fn_u.split(":", 2) (header_uri, header_ssk, bits) = fn_u.split(":", 2)
return "URI:DIR2:" + bits return "URI:DIR2:" + bits
def get_filenode_uri(self):
return self._filenode_uri
def is_readonly(self): def is_readonly(self):
return False return False
def is_mutable(self): def is_mutable(self):
@ -324,6 +327,9 @@ class ReadonlyNewDirectoryURI(_BaseURI):
(header_uri, header_ssk, bits) = fn_u.split(":", 2) (header_uri, header_ssk, bits) = fn_u.split(":", 2)
return "URI:DIR2-RO:" + bits return "URI:DIR2-RO:" + bits
def get_filenode_uri(self):
return self._filenode_uri
def is_readonly(self): def is_readonly(self):
return True return True
def is_mutable(self): def is_mutable(self):
@ -355,6 +361,9 @@ class NewDirectoryURIVerifier(_BaseURI):
(header_uri, header_ssk, bits) = fn_u.split(":", 2) (header_uri, header_ssk, bits) = fn_u.split(":", 2)
return "URI:DIR2-Verifier:" + bits return "URI:DIR2-Verifier:" + bits
def get_filenode_uri(self):
return self._filenode_uri
class DirnodeURI(_BaseURI): class DirnodeURI(_BaseURI):