mirror of
https://github.com/tahoe-lafs/tahoe-lafs.git
synced 2025-04-26 13:59:59 +00:00
more filetree work, more tests now pass
This commit is contained in:
parent
b61a4ff371
commit
ce4610c3e6
@ -3,7 +3,7 @@ from zope.interface import implements
|
|||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
from cStringIO import StringIO
|
from cStringIO import StringIO
|
||||||
from allmydata.filetree.interfaces import (
|
from allmydata.filetree.interfaces import (
|
||||||
INode, IDirectoryNode, ISubTree,
|
INode, INodeMaker, IDirectoryNode, ISubTree,
|
||||||
ICHKDirectoryNode, ISSKDirectoryNode,
|
ICHKDirectoryNode, ISSKDirectoryNode,
|
||||||
NoSuchChildError,
|
NoSuchChildError,
|
||||||
)
|
)
|
||||||
@ -19,17 +19,14 @@ from allmydata.util import bencode
|
|||||||
# each time the vdrive changes, update the local drive to match, and
|
# each time the vdrive changes, update the local drive to match, and
|
||||||
# vice versa.
|
# vice versa.
|
||||||
|
|
||||||
# from the itertools 'recipes' page
|
from itertools import islice, izip
|
||||||
from itertools import izip, tee
|
def in_pairs(iterable):
|
||||||
def pairwise(iterable):
|
"s -> (s0,s1), (s2,s3), (s4,s5), ..."
|
||||||
"s -> (s0,s1), (s1,s2), (s2, s3), ..."
|
a = islice(iterable, 0, None, 2)
|
||||||
a, b = tee(iterable)
|
b = islice(iterable, 1, None, 2)
|
||||||
try:
|
|
||||||
b.next()
|
|
||||||
except StopIteration:
|
|
||||||
pass
|
|
||||||
return izip(a, b)
|
return izip(a, b)
|
||||||
|
|
||||||
|
|
||||||
class SubTreeNode:
|
class SubTreeNode:
|
||||||
implements(INode, IDirectoryNode)
|
implements(INode, IDirectoryNode)
|
||||||
|
|
||||||
@ -95,15 +92,16 @@ class SubTreeNode:
|
|||||||
data.append(self.children[name].serialize_node())
|
data.append(self.children[name].serialize_node())
|
||||||
return data
|
return data
|
||||||
|
|
||||||
def populate_node(self, data, node_maker):
|
def populate_dirnode(self, data, node_maker):
|
||||||
|
assert INodeMaker(node_maker)
|
||||||
assert len(data) % 2 == 0
|
assert len(data) % 2 == 0
|
||||||
for (name, child_data) in pairwise(data):
|
for (name, child_data) in in_pairs(data):
|
||||||
if isinstance(child_data, (list, tuple)):
|
if isinstance(child_data, (list, tuple)):
|
||||||
child = SubTreeNode(self.enclosing_tree)
|
child = SubTreeNode(self.enclosing_tree)
|
||||||
child.populate_node(child_data)
|
child.populate_dirnode(child_data, node_maker)
|
||||||
else:
|
else:
|
||||||
assert isinstance(child_data, str)
|
assert isinstance(child_data, str)
|
||||||
child = node_maker(child_data)
|
child = node_maker.make_node_from_serialized(child_data)
|
||||||
self.children[name] = child
|
self.children[name] = child
|
||||||
|
|
||||||
|
|
||||||
@ -141,7 +139,7 @@ class _DirectorySubTree(object):
|
|||||||
|
|
||||||
def _populate_from_data(self, data, node_maker):
|
def _populate_from_data(self, data, node_maker):
|
||||||
self.root = SubTreeNode(self)
|
self.root = SubTreeNode(self)
|
||||||
self.root.populate_node(bencode.bdecode(data), node_maker)
|
self.root.populate_dirnode(bencode.bdecode(data), node_maker)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
def serialize_subtree_to_file(self, f):
|
def serialize_subtree_to_file(self, f):
|
||||||
@ -205,14 +203,16 @@ class LocalFileSubTree(_DirectorySubTree):
|
|||||||
f = open(self.filename, "rb")
|
f = open(self.filename, "rb")
|
||||||
data = f.read()
|
data = f.read()
|
||||||
f.close()
|
f.close()
|
||||||
return defer.succeed(self._populate_from_data(node_maker))
|
d = defer.succeed(data)
|
||||||
|
d.addCallback(self._populate_from_data, node_maker)
|
||||||
|
return d
|
||||||
|
|
||||||
def create_node_now(self):
|
def create_node_now(self):
|
||||||
return LocalFileSubTreeNode().new(self.filename)
|
return LocalFileSubTreeNode().new(self.filename)
|
||||||
|
|
||||||
def _update(self):
|
def _update(self):
|
||||||
f = open(self.filename, "wb")
|
f = open(self.filename, "wb")
|
||||||
self.serialize_to_file(f)
|
self.serialize_subtree_to_file(f)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
def update_now(self, uploader):
|
def update_now(self, uploader):
|
||||||
@ -258,7 +258,7 @@ class CHKDirectorySubTree(_DirectorySubTree):
|
|||||||
|
|
||||||
def update_now(self, uploader):
|
def update_now(self, uploader):
|
||||||
f = StringIO()
|
f = StringIO()
|
||||||
self.serialize_to_file(f)
|
self.serialize_subtree_to_file(f)
|
||||||
data = f.getvalue()
|
data = f.getvalue()
|
||||||
d = uploader.upload_data(data)
|
d = uploader.upload_data(data)
|
||||||
def _uploaded(uri):
|
def _uploaded(uri):
|
||||||
@ -271,7 +271,7 @@ class CHKDirectorySubTree(_DirectorySubTree):
|
|||||||
# this is the CHK form
|
# this is the CHK form
|
||||||
old_uri = self.uri
|
old_uri = self.uri
|
||||||
f, filename = workqueue.create_tempfile(".chkdir")
|
f, filename = workqueue.create_tempfile(".chkdir")
|
||||||
self.serialize_to_file(f)
|
self.serialize_subtree_to_file(f)
|
||||||
f.close()
|
f.close()
|
||||||
boxname = workqueue.create_boxname()
|
boxname = workqueue.create_boxname()
|
||||||
workqueue.add_upload_chk(filename, boxname)
|
workqueue.add_upload_chk(filename, boxname)
|
||||||
@ -339,7 +339,7 @@ class SSKDirectorySubTree(_DirectorySubTree):
|
|||||||
raise RuntimeError("This SSKDirectorySubTree is not mutable")
|
raise RuntimeError("This SSKDirectorySubTree is not mutable")
|
||||||
|
|
||||||
f = StringIO()
|
f = StringIO()
|
||||||
self.serialize_to_file(f)
|
self.serialize_subtree_to_file(f)
|
||||||
data = f.getvalue()
|
data = f.getvalue()
|
||||||
|
|
||||||
self.version += 1
|
self.version += 1
|
||||||
@ -350,7 +350,7 @@ class SSKDirectorySubTree(_DirectorySubTree):
|
|||||||
def update(self, workqueue):
|
def update(self, workqueue):
|
||||||
# this is the SSK form
|
# this is the SSK form
|
||||||
f, filename = workqueue.create_tempfile(".sskdir")
|
f, filename = workqueue.create_tempfile(".sskdir")
|
||||||
self.serialize_to_file(f)
|
self.serialize_subtree_to_file(f)
|
||||||
f.close()
|
f.close()
|
||||||
|
|
||||||
oldversion = self.version
|
oldversion = self.version
|
||||||
|
@ -17,8 +17,11 @@ class INode(Interface):
|
|||||||
.prefix ."""
|
.prefix ."""
|
||||||
def populate_node(body, node_maker):
|
def populate_node(body, node_maker):
|
||||||
"""vdrive.make_node_from_serialized() will first use the prefix from
|
"""vdrive.make_node_from_serialized() will first use the prefix from
|
||||||
the .prefix attribute to decide what kind of Node to create. It will
|
the .prefix attribute to decide what kind of Node to create. They
|
||||||
then call this function with the body to populate the new Node."""
|
will then call this populate_node() method with the body to populate
|
||||||
|
the new Node. 'node_maker' provides INodeMaker, which provides that
|
||||||
|
same make_node_from_serialized function to create any internal child
|
||||||
|
nodes that might be necessary."""
|
||||||
|
|
||||||
class IFileNode(Interface):
|
class IFileNode(Interface):
|
||||||
"""This is a file which can be retrieved."""
|
"""This is a file which can be retrieved."""
|
||||||
@ -190,6 +193,21 @@ class ISubTree(Interface):
|
|||||||
(SSKDirectorySubTrees and redirections).
|
(SSKDirectorySubTrees and redirections).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
class INodeMaker(Interface):
|
||||||
|
def make_node_from_serialized(serialized):
|
||||||
|
"""Turn a string into an INode, which contains information about
|
||||||
|
the file or directory (like a URI), but does not contain the actual
|
||||||
|
contents. An IOpener can be used later to retrieve the contents
|
||||||
|
(which means downloading the file if this is an IFileNode, or
|
||||||
|
perhaps creating a new subtree from the contents)."""
|
||||||
|
|
||||||
|
class ISubTreeMaker(Interface):
|
||||||
|
def make_subtree_from_node(node, parent_is_mutable):
|
||||||
|
"""Turn an INode into an ISubTree (using an internal opener to
|
||||||
|
download the data, if necessary).
|
||||||
|
This returns a Deferred that fires with the ISubTree instance.
|
||||||
|
"""
|
||||||
|
|
||||||
#class IMutableSubTree(Interface):
|
#class IMutableSubTree(Interface):
|
||||||
# def mutation_affects_parent():
|
# def mutation_affects_parent():
|
||||||
# """This returns True for CHK nodes where you must inform the parent
|
# """This returns True for CHK nodes where you must inform the parent
|
||||||
|
@ -3,7 +3,7 @@ from zope.interface import implements
|
|||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
from allmydata.filetree import interfaces, directory, redirect
|
from allmydata.filetree import interfaces, directory, redirect
|
||||||
#from allmydata.filetree.file import CHKFile, MutableSSKFile, ImmutableSSKFile
|
#from allmydata.filetree.file import CHKFile, MutableSSKFile, ImmutableSSKFile
|
||||||
from allmydata.filetree.interfaces import INode, IDirectoryNode
|
from allmydata.filetree.interfaces import INode, IDirectoryNode, INodeMaker
|
||||||
|
|
||||||
all_openable_subtree_types = [
|
all_openable_subtree_types = [
|
||||||
directory.LocalFileSubTree,
|
directory.LocalFileSubTree,
|
||||||
@ -27,6 +27,7 @@ class Opener(object):
|
|||||||
|
|
||||||
def _create(self, node, parent_is_mutable, node_maker):
|
def _create(self, node, parent_is_mutable, node_maker):
|
||||||
assert INode(node)
|
assert INode(node)
|
||||||
|
assert INodeMaker(node_maker)
|
||||||
for subtree_class in all_openable_subtree_types:
|
for subtree_class in all_openable_subtree_types:
|
||||||
if isinstance(node, subtree_class.node_class):
|
if isinstance(node, subtree_class.node_class):
|
||||||
subtree = subtree_class()
|
subtree = subtree_class()
|
||||||
@ -41,6 +42,7 @@ class Opener(object):
|
|||||||
def open(self, node, parent_is_mutable, node_maker):
|
def open(self, node, parent_is_mutable, node_maker):
|
||||||
assert INode(node)
|
assert INode(node)
|
||||||
assert not isinstance(node, IDirectoryNode)
|
assert not isinstance(node, IDirectoryNode)
|
||||||
|
assert INodeMaker(node_maker)
|
||||||
|
|
||||||
# is it in cache? To check this we need to use the node's serialized
|
# is it in cache? To check this we need to use the node's serialized
|
||||||
# form, since nodes are instances and don't compare by value
|
# form, since nodes are instances and don't compare by value
|
||||||
|
@ -3,7 +3,7 @@ from cStringIO import StringIO
|
|||||||
from zope.interface import implements
|
from zope.interface import implements
|
||||||
from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
|
|
||||||
from allmydata.filetree.interfaces import ISubTree
|
from allmydata.filetree.interfaces import ISubTree, INodeMaker
|
||||||
from allmydata.filetree.basenode import BaseDataNode
|
from allmydata.filetree.basenode import BaseDataNode
|
||||||
from allmydata.util import bencode
|
from allmydata.util import bencode
|
||||||
|
|
||||||
@ -33,7 +33,8 @@ class _BaseRedirection(object):
|
|||||||
return self.child_node.serialize_node()
|
return self.child_node.serialize_node()
|
||||||
|
|
||||||
def _populate_from_data(self, data, node_maker):
|
def _populate_from_data(self, data, node_maker):
|
||||||
self.child_node = node_maker(data)
|
assert INodeMaker(node_maker)
|
||||||
|
self.child_node = node_maker.make_node_from_serialized(data)
|
||||||
return self
|
return self
|
||||||
|
|
||||||
|
|
||||||
@ -64,7 +65,9 @@ class LocalFileRedirection(_BaseRedirection):
|
|||||||
# note: we don't cache the contents of the file. TODO: consider
|
# note: we don't cache the contents of the file. TODO: consider
|
||||||
# doing this based upon mtime. It is important that we be able to
|
# doing this based upon mtime. It is important that we be able to
|
||||||
# notice if the file has been changed.
|
# notice if the file has been changed.
|
||||||
return defer.succeed(self._populate_from_data(data, node_maker))
|
d = defer.succeed(data)
|
||||||
|
d.addCallback(self._populate_from_data, node_maker)
|
||||||
|
return d
|
||||||
|
|
||||||
def is_mutable(self):
|
def is_mutable(self):
|
||||||
return True
|
return True
|
||||||
@ -161,10 +164,11 @@ class QueenOrLocalFileRedirection(_BaseRedirection):
|
|||||||
# TODO: queen?
|
# TODO: queen?
|
||||||
# TODO: pubsub so we can cache the queen's results
|
# TODO: pubsub so we can cache the queen's results
|
||||||
d = self._queen.callRemote("lookup_handle", self.handle)
|
d = self._queen.callRemote("lookup_handle", self.handle)
|
||||||
d.addCallback(self._choose_winner, local_version_and_data, node_maker)
|
d.addCallback(self._choose_winner, local_version_and_data)
|
||||||
|
d.addCallback(self._populate_from_data, node_maker)
|
||||||
return d
|
return d
|
||||||
|
|
||||||
def _choose_winner(self, queen_version_and_data, local_version_and_data, node_maker):
|
def _choose_winner(self, queen_version_and_data, local_version_and_data):
|
||||||
queen_version, queen_data = bencode.bdecode(queen_version_and_data)
|
queen_version, queen_data = bencode.bdecode(queen_version_and_data)
|
||||||
local_version, local_data = bencode.bdecode(local_version_and_data)
|
local_version, local_data = bencode.bdecode(local_version_and_data)
|
||||||
if queen_version > local_version:
|
if queen_version > local_version:
|
||||||
@ -174,7 +178,7 @@ class QueenOrLocalFileRedirection(_BaseRedirection):
|
|||||||
data = local_data
|
data = local_data
|
||||||
self.version = local_version
|
self.version = local_version
|
||||||
# NOTE: two layers of bencoding here, TODO
|
# NOTE: two layers of bencoding here, TODO
|
||||||
return self._populate_from_data(data, node_maker)
|
return data
|
||||||
|
|
||||||
def is_mutable(self):
|
def is_mutable(self):
|
||||||
return True
|
return True
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
|
|
||||||
from zope.interface import implements
|
from zope.interface import implements
|
||||||
from allmydata.filetree import opener, directory, redirect
|
from allmydata.filetree import opener, directory, file, redirect
|
||||||
from allmydata.filetree.interfaces import (
|
from allmydata.filetree.interfaces import (
|
||||||
IVirtualDrive, INode, ISubTree, IFileNode, IDirectoryNode,
|
IVirtualDrive, INodeMaker, INode, ISubTree, IFileNode, IDirectoryNode,
|
||||||
NoSuchDirectoryError, NoSuchChildError, PathAlreadyExistsError,
|
NoSuchDirectoryError, NoSuchChildError, PathAlreadyExistsError,
|
||||||
PathDoesNotExistError,
|
PathDoesNotExistError,
|
||||||
)
|
)
|
||||||
@ -12,8 +12,11 @@ from allmydata.upload import IUploadable
|
|||||||
# node specification strings (found inside the serialized form of subtrees)
|
# node specification strings (found inside the serialized form of subtrees)
|
||||||
# into Nodes (which live in the in-RAM form of subtrees).
|
# into Nodes (which live in the in-RAM form of subtrees).
|
||||||
all_node_types = [
|
all_node_types = [
|
||||||
|
directory.LocalFileSubTreeNode,
|
||||||
directory.CHKDirectorySubTreeNode,
|
directory.CHKDirectorySubTreeNode,
|
||||||
directory.SSKDirectorySubTreeNode,
|
directory.SSKDirectorySubTreeNode,
|
||||||
|
file.CHKFileNode,
|
||||||
|
file.SSKFileNode,
|
||||||
redirect.LocalFileRedirectionNode,
|
redirect.LocalFileRedirectionNode,
|
||||||
redirect.QueenRedirectionNode,
|
redirect.QueenRedirectionNode,
|
||||||
redirect.HTTPRedirectionNode,
|
redirect.HTTPRedirectionNode,
|
||||||
@ -21,7 +24,7 @@ all_node_types = [
|
|||||||
]
|
]
|
||||||
|
|
||||||
class VirtualDrive(object):
|
class VirtualDrive(object):
|
||||||
implements(IVirtualDrive)
|
implements(IVirtualDrive, INodeMaker)
|
||||||
|
|
||||||
def __init__(self, workqueue, downloader, root_node):
|
def __init__(self, workqueue, downloader, root_node):
|
||||||
assert INode(root_node)
|
assert INode(root_node)
|
||||||
@ -33,6 +36,8 @@ class VirtualDrive(object):
|
|||||||
self.root_node = root_node
|
self.root_node = root_node
|
||||||
|
|
||||||
# these are called when loading and creating nodes
|
# these are called when loading and creating nodes
|
||||||
|
|
||||||
|
# INodeMaker
|
||||||
def make_node_from_serialized(self, serialized):
|
def make_node_from_serialized(self, serialized):
|
||||||
# this turns a string into an INode, which contains information about
|
# this turns a string into an INode, which contains information about
|
||||||
# the file or directory (like a URI), but does not contain the actual
|
# the file or directory (like a URI), but does not contain the actual
|
||||||
@ -47,14 +52,14 @@ class VirtualDrive(object):
|
|||||||
for node_class in all_node_types:
|
for node_class in all_node_types:
|
||||||
if prefix == node_class.prefix:
|
if prefix == node_class.prefix:
|
||||||
node = node_class()
|
node = node_class()
|
||||||
node.populate_node(body, self.make_node_from_serialized)
|
node.populate_node(body, self)
|
||||||
return node
|
return node
|
||||||
raise RuntimeError("unable to handle subtree type '%s'" % prefix)
|
raise RuntimeError("unable to handle node type '%s'" % prefix)
|
||||||
|
|
||||||
|
# ISubTreeMaker
|
||||||
def make_subtree_from_node(self, node, parent_is_mutable):
|
def make_subtree_from_node(self, node, parent_is_mutable):
|
||||||
assert INode(node)
|
assert INode(node)
|
||||||
return self.opener.open(node, parent_is_mutable,
|
return self.opener.open(node, parent_is_mutable, self)
|
||||||
self.make_subtree_from_node)
|
|
||||||
|
|
||||||
# these methods are used to walk through our subtrees
|
# these methods are used to walk through our subtrees
|
||||||
|
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
|
|
||||||
#from zope.interface import implements
|
#from zope.interface import implements
|
||||||
from twisted.trial import unittest
|
from twisted.trial import unittest
|
||||||
#from twisted.internet import defer
|
from twisted.internet import defer
|
||||||
#from allmydata.filetree.interfaces import IOpener, IDirectoryNode
|
#from allmydata.filetree.interfaces import IOpener, IDirectoryNode
|
||||||
#from allmydata.filetree.directory import (ImmutableDirectorySubTree,
|
#from allmydata.filetree.directory import (ImmutableDirectorySubTree,
|
||||||
# SubTreeNode,
|
# SubTreeNode,
|
||||||
@ -314,8 +314,15 @@ class Redirect(unittest.TestCase):
|
|||||||
|
|
||||||
import os.path
|
import os.path
|
||||||
from allmydata.filetree import directory, redirect, vdrive
|
from allmydata.filetree import directory, redirect, vdrive
|
||||||
from allmydata.filetree.interfaces import (ISubTree, INode, IDirectoryNode)
|
from allmydata.filetree.interfaces import (ISubTree, INode, IDirectoryNode, IFileNode)
|
||||||
from allmydata.filetree.file import CHKFileNode
|
from allmydata.filetree.file import CHKFileNode
|
||||||
|
from allmydata.util import bencode
|
||||||
|
|
||||||
|
class InPairs(unittest.TestCase):
|
||||||
|
def test_in_pairs(self):
|
||||||
|
l = range(8)
|
||||||
|
pairs = list(directory.in_pairs(l))
|
||||||
|
self.failUnlessEqual(pairs, [(0,1), (2,3), (4,5), (6,7)])
|
||||||
|
|
||||||
class Stuff(unittest.TestCase):
|
class Stuff(unittest.TestCase):
|
||||||
|
|
||||||
@ -372,11 +379,14 @@ class Stuff(unittest.TestCase):
|
|||||||
del root, subdir1, subdir2, subdir3, subdir4
|
del root, subdir1, subdir2, subdir3, subdir4
|
||||||
# leaving file1 for later use
|
# leaving file1 for later use
|
||||||
|
|
||||||
# now serialize it and reconstruct it
|
# now serialize it and examine the results
|
||||||
f = StringIO()
|
f = StringIO()
|
||||||
subtree.serialize_subtree_to_file(f)
|
subtree.serialize_subtree_to_file(f)
|
||||||
data = f.getvalue()
|
data = f.getvalue()
|
||||||
#print data
|
#print data
|
||||||
|
unpacked = bencode.bdecode(data)
|
||||||
|
#print unpacked
|
||||||
|
del f, data, unpacked
|
||||||
|
|
||||||
node = subtree.create_node_now()
|
node = subtree.create_node_now()
|
||||||
self.failUnless(isinstance(node, directory.LocalFileSubTreeNode))
|
self.failUnless(isinstance(node, directory.LocalFileSubTreeNode))
|
||||||
@ -384,9 +394,14 @@ class Stuff(unittest.TestCase):
|
|||||||
self.failUnless(isinstance(node_s, str))
|
self.failUnless(isinstance(node_s, str))
|
||||||
self.failUnless(node_s.startswith("LocalFileDirectory:"))
|
self.failUnless(node_s.startswith("LocalFileDirectory:"))
|
||||||
self.failUnless("dirtree.save" in node_s)
|
self.failUnless("dirtree.save" in node_s)
|
||||||
|
del node, node_s
|
||||||
|
|
||||||
|
d = defer.maybeDeferred(subtree.update_now, None)
|
||||||
|
def _updated(node):
|
||||||
|
# now reconstruct it
|
||||||
|
return v.make_subtree_from_node(node, False)
|
||||||
|
d.addCallback(_updated)
|
||||||
|
|
||||||
# now reconstruct it
|
|
||||||
d = v.make_subtree_from_node(node, False)
|
|
||||||
def _opened(new_subtree):
|
def _opened(new_subtree):
|
||||||
res = new_subtree.get_node_for_path([])
|
res = new_subtree.get_node_for_path([])
|
||||||
(found_path, root, remaining_path) = res
|
(found_path, root, remaining_path) = res
|
||||||
@ -396,7 +411,9 @@ class Stuff(unittest.TestCase):
|
|||||||
self.failUnless(IDirectoryNode.providedBy(root))
|
self.failUnless(IDirectoryNode.providedBy(root))
|
||||||
self.failUnlessEqual(root.list(), ["foo.txt", "subdir1"])
|
self.failUnlessEqual(root.list(), ["foo.txt", "subdir1"])
|
||||||
file1a = root.get("foo.txt")
|
file1a = root.get("foo.txt")
|
||||||
self.failUnless(isinstance(CHKFileNode, file1a))
|
self.failUnless(INode(file1a))
|
||||||
|
self.failUnless(isinstance(file1a, CHKFileNode))
|
||||||
|
self.failUnless(IFileNode(file1a))
|
||||||
self.failUnlessEqual(file1a.get_uri(), "uri1")
|
self.failUnlessEqual(file1a.get_uri(), "uri1")
|
||||||
subdir1 = root.get("subdir1")
|
subdir1 = root.get("subdir1")
|
||||||
subdir2 = subdir1.get("subdir2")
|
subdir2 = subdir1.get("subdir2")
|
||||||
@ -405,7 +422,6 @@ class Stuff(unittest.TestCase):
|
|||||||
self.failUnlessEqual(subdir2.list(), [])
|
self.failUnlessEqual(subdir2.list(), [])
|
||||||
d.addCallback(_opened)
|
d.addCallback(_opened)
|
||||||
return d
|
return d
|
||||||
testDirectory.todo = "not working yet"
|
|
||||||
|
|
||||||
def testVdrive(self):
|
def testVdrive(self):
|
||||||
# create some stuff, see if we can import everything
|
# create some stuff, see if we can import everything
|
||||||
|
Loading…
x
Reference in New Issue
Block a user