]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blobdiff - src/allmydata/test/test_system.py
Make platform-detection code tolerate linux-3.0, patch by zooko.
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / test / test_system.py
index c1c9b08a24c58495ed436fc962303b1e438db734..639968ddeeceee0fb4d9b587634dad3cd8316b9c 100644 (file)
@@ -1,35 +1,41 @@
 from base64 import b32encode
-import os, sys, time, re, simplejson
+import os, sys, time, simplejson
 from cStringIO import StringIO
-from zope.interface import implements
 from twisted.trial import unittest
 from twisted.internet import defer
 from twisted.internet import threads # CLI tests use deferToThread
-from twisted.internet.error import ConnectionDone, ConnectionLost
-from twisted.internet.interfaces import IConsumer, IPushProducer
+
 import allmydata
 from allmydata import uri
 from allmydata.storage.mutable import MutableShareFile
 from allmydata.storage.server import si_a2b
-from allmydata.immutable import download, filenode, offloaded, upload
+from allmydata.immutable import offloaded, upload
+from allmydata.immutable.literal import LiteralFileNode
+from allmydata.immutable.filenode import ImmutableFileNode
 from allmydata.util import idlib, mathutil
 from allmydata.util import log, base32
+from allmydata.util.encodingutil import quote_output, unicode_to_argv, get_filesystem_encoding
+from allmydata.util.fileutil import abspath_expanduser_unicode
+from allmydata.util.consumer import MemoryConsumer, download_to_data
 from allmydata.scripts import runner
-from allmydata.interfaces import IDirectoryNode, IFileNode, IFileURI, \
-     NoSuchChildError, NotEnoughSharesError
+from allmydata.interfaces import IDirectoryNode, IFileNode, \
+     NoSuchChildError, NoSharesError
 from allmydata.monitor import Monitor
-from allmydata.mutable.common import NotMutableError
+from allmydata.mutable.common import NotWriteableError
 from allmydata.mutable import layout as mutable_layout
+from allmydata.mutable.publish import MutableData
 from foolscap.api import DeadReferenceError
 from twisted.python.failure import Failure
 from twisted.web.client import getPage
 from twisted.web.error import Error
 
-from allmydata.test.common import SystemTestMixin, MemoryConsumer, \
-     download_to_data
+from allmydata.test.common import SystemTestMixin
+
+# TODO: move this to common or common_util
+from allmydata.test.test_runner import RunBinTahoeMixin
 
 LARGE_DATA = """
-This is some data to publish to the virtual drive, which needs to be large
+This is some data to publish to the remote grid.., which needs to be large
 enough to not fit inside a LIT uri.
 """
 
@@ -46,23 +52,8 @@ class CountingDataUploadable(upload.Data):
                 self.interrupt_after_d.callback(self)
         return upload.Data.read(self, length)
 
-class GrabEverythingConsumer:
-    implements(IConsumer)
-
-    def __init__(self):
-        self.contents = ""
-
-    def registerProducer(self, producer, streaming):
-        assert streaming
-        assert IPushProducer.providedBy(producer)
-
-    def write(self, data):
-        self.contents += data
-
-    def unregisterProducer(self):
-        pass
-
-class SystemTest(SystemTestMixin, unittest.TestCase):
+class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
+    timeout = 3600 # It takes longer than 960 seconds on Zandr's ARM box.
 
     def test_connections(self):
         self.basedir = "system/SystemTest/test_connections"
@@ -72,9 +63,10 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
         def _check(extra_node):
             self.extra_node = extra_node
             for c in self.clients:
-                all_peerids = list(c.get_all_peerids())
+                all_peerids = c.get_storage_broker().get_all_serverids()
                 self.failUnlessEqual(len(all_peerids), self.numclients+1)
-                permuted_peers = list(c.get_permuted_peers("storage", "a"))
+                sb = c.storage_broker
+                permuted_peers = sb.get_servers_for_psi("a")
                 self.failUnlessEqual(len(permuted_peers), self.numclients+1)
 
         d.addCallback(_check)
@@ -84,7 +76,6 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
             return res
         d.addBoth(_shutdown_extra_node)
         return d
-    test_connections.timeout = 300
     # test_connections is subsumed by test_upload_and_download, and takes
     # quite a while to run on a slow machine (because of all the TLS
     # connections that must be established). If we ever rework the introducer
@@ -95,12 +86,10 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
     def test_upload_and_download_random_key(self):
         self.basedir = "system/SystemTest/test_upload_and_download_random_key"
         return self._test_upload_and_download(convergence=None)
-    test_upload_and_download_random_key.timeout = 4800
 
     def test_upload_and_download_convergent(self):
         self.basedir = "system/SystemTest/test_upload_and_download_convergent"
         return self._test_upload_and_download(convergence="some convergence string")
-    test_upload_and_download_convergent.timeout = 4800
 
     def _test_upload_and_download(self, convergence):
         # we use 4000 bytes of data, which will result in about 400k written
@@ -109,9 +98,11 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
         d = self.set_up_nodes()
         def _check_connections(res):
             for c in self.clients:
-                all_peerids = list(c.get_all_peerids())
+                c.DEFAULT_ENCODING_PARAMETERS['happy'] = 5
+                all_peerids = c.get_storage_broker().get_all_serverids()
                 self.failUnlessEqual(len(all_peerids), self.numclients)
-                permuted_peers = list(c.get_permuted_peers("storage", "a"))
+                sb = c.storage_broker
+                permuted_peers = sb.get_servers_for_psi("a")
                 self.failUnlessEqual(len(permuted_peers), self.numclients)
         d.addCallback(_check_connections)
 
@@ -135,8 +126,8 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
             log.msg("upload finished: uri is %s" % (theuri,))
             self.uri = theuri
             assert isinstance(self.uri, str), self.uri
-            dl = self.clients[1].getServiceNamed("downloader")
-            self.downloader = dl
+            self.cap = uri.from_string(self.uri)
+            self.n = self.clients[1].create_node_from_uri(self.uri)
         d.addCallback(_upload_done)
 
         def _upload_again(res):
@@ -147,47 +138,18 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
             log.msg("UPLOADING AGAIN")
             up = upload.Data(DATA, convergence=convergence)
             up.max_segment_size = 1024
-            d1 = self.uploader.upload(up)
+            return self.uploader.upload(up)
         d.addCallback(_upload_again)
 
         def _download_to_data(res):
             log.msg("DOWNLOADING")
-            return self.downloader.download_to_data(self.uri)
+            return download_to_data(self.n)
         d.addCallback(_download_to_data)
         def _download_to_data_done(data):
             log.msg("download finished")
             self.failUnlessEqual(data, DATA)
         d.addCallback(_download_to_data_done)
 
-        target_filename = os.path.join(self.basedir, "download.target")
-        def _download_to_filename(res):
-            return self.downloader.download_to_filename(self.uri,
-                                                        target_filename)
-        d.addCallback(_download_to_filename)
-        def _download_to_filename_done(res):
-            newdata = open(target_filename, "rb").read()
-            self.failUnlessEqual(newdata, DATA)
-        d.addCallback(_download_to_filename_done)
-
-        target_filename2 = os.path.join(self.basedir, "download.target2")
-        def _download_to_filehandle(res):
-            fh = open(target_filename2, "wb")
-            return self.downloader.download_to_filehandle(self.uri, fh)
-        d.addCallback(_download_to_filehandle)
-        def _download_to_filehandle_done(fh):
-            fh.close()
-            newdata = open(target_filename2, "rb").read()
-            self.failUnlessEqual(newdata, DATA)
-        d.addCallback(_download_to_filehandle_done)
-
-        consumer = GrabEverythingConsumer()
-        ct = download.ConsumerAdapter(consumer)
-        d.addCallback(lambda res:
-                      self.downloader.download(self.uri, ct))
-        def _download_to_consumer_done(ign):
-            self.failUnlessEqual(consumer.contents, DATA)
-        d.addCallback(_download_to_consumer_done)
-
         def _test_read(res):
             n = self.clients[1].create_node_from_uri(self.uri)
             d = download_to_data(n)
@@ -219,7 +181,7 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
             bad_n = self.clients[1].create_node_from_uri(bad_u.to_string())
             # this should cause an error during download
 
-            d = self.shouldFail2(NotEnoughSharesError, "'download bad node'",
+            d = self.shouldFail2(NoSharesError, "'download bad node'",
                                  None,
                                  bad_n.read, MemoryConsumer(), offset=2)
             return d
@@ -227,19 +189,16 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
 
         def _download_nonexistent_uri(res):
             baduri = self.mangle_uri(self.uri)
+            badnode = self.clients[1].create_node_from_uri(baduri)
             log.msg("about to download non-existent URI", level=log.UNUSUAL,
                     facility="tahoe.tests")
-            d1 = self.downloader.download_to_data(baduri)
+            d1 = download_to_data(badnode)
             def _baduri_should_fail(res):
                 log.msg("finished downloading non-existend URI",
                         level=log.UNUSUAL, facility="tahoe.tests")
                 self.failUnless(isinstance(res, Failure))
-                self.failUnless(res.check(NotEnoughSharesError),
-                                "expected NotEnoughSharesError, got %s" % res)
-                # TODO: files that have zero peers should get a special kind
-                # of NotEnoughSharesError, which can be used to suggest that
-                # the URI might be wrong or that they've never uploaded the
-                # file in the first place.
+                self.failUnless(res.check(NoSharesError),
+                                "expected NoSharesError, got %s" % res)
             d1.addBoth(_baduri_should_fail)
             return d1
         d.addCallback(_download_nonexistent_uri)
@@ -251,6 +210,7 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
                                                       add_to_sparent=True))
         def _added(extra_node):
             self.extra_node = extra_node
+            self.extra_node.DEFAULT_ENCODING_PARAMETERS['happy'] = 5
         d.addCallback(_added)
 
         HELPER_DATA = "Data that needs help to upload" * 1000
@@ -258,8 +218,8 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
             u = upload.Data(HELPER_DATA, convergence=convergence)
             d = self.extra_node.upload(u)
             def _uploaded(results):
-                uri = results.uri
-                return self.downloader.download_to_data(uri)
+                n = self.clients[1].create_node_from_uri(results.uri)
+                return download_to_data(n)
             d.addCallback(_uploaded)
             def _check(newdata):
                 self.failUnlessEqual(newdata, HELPER_DATA)
@@ -272,8 +232,8 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
             u.debug_stash_RemoteEncryptedUploadable = True
             d = self.extra_node.upload(u)
             def _uploaded(results):
-                uri = results.uri
-                return self.downloader.download_to_data(uri)
+                n = self.clients[1].create_node_from_uri(results.uri)
+                return download_to_data(n)
             d.addCallback(_uploaded)
             def _check(newdata):
                 self.failUnlessEqual(newdata, HELPER_DATA)
@@ -311,7 +271,7 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
                 self.fail("interrupted upload should have failed, not finished"
                           " with result %s" % (res,))
             def _interrupted(f):
-                f.trap(ConnectionLost, ConnectionDone, DeadReferenceError)
+                f.trap(DeadReferenceError)
 
                 # make sure we actually interrupted it before finishing the
                 # file
@@ -360,13 +320,14 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
             d.addCallback(lambda res: self.extra_node.upload(u2))
 
             def _uploaded(results):
-                uri = results.uri
+                cap = results.uri
                 log.msg("Second upload complete", level=log.NOISY,
                         facility="tahoe.test.test_system")
 
                 # this is really bytes received rather than sent, but it's
                 # convenient and basically measures the same thing
                 bytes_sent = results.ciphertext_fetched
+                self.failUnless(isinstance(bytes_sent, (int, long)), bytes_sent)
 
                 # We currently don't support resumption of upload if the data is
                 # encrypted with a random key.  (Because that would require us
@@ -377,18 +338,19 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
                     # Make sure we did not have to read the whole file the
                     # second time around .
                     self.failUnless(bytes_sent < len(DATA),
-                                "resumption didn't save us any work:"
-                                " read %d bytes out of %d total" %
-                                (bytes_sent, len(DATA)))
+                                    "resumption didn't save us any work:"
+                                    " read %r bytes out of %r total" %
+                                    (bytes_sent, len(DATA)))
                 else:
                     # Make sure we did have to read the whole file the second
                     # time around -- because the one that we partially uploaded
                     # earlier was encrypted with a different random key.
                     self.failIf(bytes_sent < len(DATA),
                                 "resumption saved us some work even though we were using random keys:"
-                                " read %d bytes out of %d total" %
+                                " read %r bytes out of %r total" %
                                 (bytes_sent, len(DATA)))
-                return self.downloader.download_to_data(uri)
+                n = self.clients[1].create_node_from_uri(cap)
+                return download_to_data(n)
             d.addCallback(_uploaded)
 
             def _check(newdata):
@@ -429,7 +391,7 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
 
         return d
 
-    def _find_shares(self, basedir):
+    def _find_all_shares(self, basedir):
         shares = []
         for (dirpath, dirnames, filenames) in os.walk(basedir):
             if "storage" not in dirpath:
@@ -502,19 +464,21 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
     def test_mutable(self):
         self.basedir = "system/SystemTest/test_mutable"
         DATA = "initial contents go here."  # 25 bytes % 3 != 0
+        DATA_uploadable = MutableData(DATA)
         NEWDATA = "new contents yay"
+        NEWDATA_uploadable = MutableData(NEWDATA)
         NEWERDATA = "this is getting old"
+        NEWERDATA_uploadable = MutableData(NEWERDATA)
 
         d = self.set_up_nodes(use_key_generator=True)
 
         def _create_mutable(res):
             c = self.clients[0]
             log.msg("starting create_mutable_file")
-            d1 = c.create_mutable_file(DATA)
+            d1 = c.create_mutable_file(DATA_uploadable)
             def _done(res):
                 log.msg("DONE: %s" % (res,))
                 self._mutable_node_1 = res
-                uri = res.get_uri()
             d1.addCallback(_done)
             return d1
         d.addCallback(_create_mutable)
@@ -522,7 +486,7 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
         def _test_debug(res):
             # find a share. It is important to run this while there is only
             # one slot in the grid.
-            shares = self._find_shares(self.basedir)
+            shares = self._find_all_shares(self.basedir)
             (client_num, storage_index, filename, shnum) = shares[0]
             log.msg("test_system.SystemTest.test_mutable._test_debug using %s"
                     % filename)
@@ -540,16 +504,6 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
                 peerid = idlib.nodeid_b2a(self.clients[client_num].nodeid)
                 self.failUnless(" WE for nodeid: %s\n" % peerid in output)
                 self.failUnless(" num_extra_leases: 0\n" in output)
-                # the pubkey size can vary by a byte, so the container might
-                # be a bit larger on some runs.
-                m = re.search(r'^ container_size: (\d+)$', output, re.M)
-                self.failUnless(m)
-                container_size = int(m.group(1))
-                self.failUnless(2037 <= container_size <= 2049, container_size)
-                m = re.search(r'^ data_length: (\d+)$', output, re.M)
-                self.failUnless(m)
-                data_length = int(m.group(1))
-                self.failUnless(2037 <= data_length <= 2049, data_length)
                 self.failUnless("  secrets are for nodeid: %s\n" % peerid
                                 in output)
                 self.failUnless(" SDMF contents:\n" in output)
@@ -608,7 +562,7 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
             self.failUnlessEqual(res, DATA)
             # replace the data
             log.msg("starting replace1")
-            d1 = newnode.overwrite(NEWDATA)
+            d1 = newnode.overwrite(NEWDATA_uploadable)
             d1.addCallback(lambda res: newnode.download_best_version())
             return d1
         d.addCallback(_check_download_3)
@@ -622,7 +576,7 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
             newnode2 = self.clients[3].create_node_from_uri(uri)
             self._newnode3 = self.clients[3].create_node_from_uri(uri)
             log.msg("starting replace2")
-            d1 = newnode1.overwrite(NEWERDATA)
+            d1 = newnode1.overwrite(NEWERDATA_uploadable)
             d1.addCallback(lambda res: newnode2.download_best_version())
             return d1
         d.addCallback(_check_download_4)
@@ -635,7 +589,7 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
         def _corrupt_shares(res):
             # run around and flip bits in all but k of the shares, to test
             # the hash checks
-            shares = self._find_shares(self.basedir)
+            shares = self._find_all_shares(self.basedir)
             ## sort by share number
             #shares.sort( lambda a,b: cmp(a[3], b[3]) )
             where = dict([ (shnum, filename)
@@ -692,13 +646,13 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
         def _check_empty_file(res):
             # make sure we can create empty files, this usually screws up the
             # segsize math
-            d1 = self.clients[2].create_mutable_file("")
+            d1 = self.clients[2].create_mutable_file(MutableData(""))
             d1.addCallback(lambda newnode: newnode.download_best_version())
             d1.addCallback(lambda res: self.failUnlessEqual("", res))
             return d1
         d.addCallback(_check_empty_file)
 
-        d.addCallback(lambda res: self.clients[0].create_empty_dirnode())
+        d.addCallback(lambda res: self.clients[0].create_dirnode())
         def _created_dirnode(dnode):
             log.msg("_created_dirnode(%s)" % (dnode,))
             d1 = dnode.list()
@@ -723,18 +677,17 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
                                  self.key_generator_svc.key_generator.pool_size + size_delta)
 
         d.addCallback(check_kg_poolsize, 0)
-        d.addCallback(lambda junk: self.clients[3].create_mutable_file('hello, world'))
+        d.addCallback(lambda junk:
+            self.clients[3].create_mutable_file(MutableData('hello, world')))
         d.addCallback(check_kg_poolsize, -1)
-        d.addCallback(lambda junk: self.clients[3].create_empty_dirnode())
+        d.addCallback(lambda junk: self.clients[3].create_dirnode())
         d.addCallback(check_kg_poolsize, -2)
         # use_helper induces use of clients[3], which is the using-key_gen client
-        d.addCallback(lambda junk: self.POST("uri", use_helper=True, t="mkdir", name='george'))
+        d.addCallback(lambda junk:
+                      self.POST("uri?t=mkdir&name=george", use_helper=True))
         d.addCallback(check_kg_poolsize, -3)
 
         return d
-    # The default 120 second timeout went off when running it under valgrind
-    # on my old Windows laptop, so I'm bumping up the timeout.
-    test_mutable.timeout = 240
 
     def flip_bit(self, good):
         return good[:-1] + chr(ord(good[-1]) ^ 0x01)
@@ -742,7 +695,7 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
     def mangle_uri(self, gooduri):
         # change the key, which changes the storage index, which means we'll
         # be asking about the wrong file, so nobody will have any shares
-        u = IFileURI(gooduri)
+        u = uri.from_string(gooduri)
         u2 = uri.CHKFileURI(key=self.flip_bit(u.key),
                             uri_extension_hash=u.uri_extension_hash,
                             needed_shares=u.needed_shares,
@@ -758,10 +711,14 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
     # the key, which should cause the download to fail the post-download
     # plaintext_hash check.
 
-    def test_vdrive(self):
-        self.basedir = "system/SystemTest/test_vdrive"
+    def test_filesystem(self):
+        self.basedir = "system/SystemTest/test_filesystem"
         self.data = LARGE_DATA
         d = self.set_up_nodes(use_stats_gatherer=True)
+        def _new_happy_semantics(ign):
+            for c in self.clients:
+                c.DEFAULT_ENCODING_PARAMETERS['happy'] = 1
+        d.addCallback(_new_happy_semantics)
         d.addCallback(self._test_introweb)
         d.addCallback(self.log, "starting publish")
         d.addCallback(self._do_publish1)
@@ -800,14 +757,32 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
         # P/test_put/  (empty)
         d.addCallback(self._test_checker)
         return d
-    test_vdrive.timeout = 1100
 
     def _test_introweb(self, res):
         d = getPage(self.introweb_url, method="GET", followRedirect=True)
         def _check(res):
             try:
-                self.failUnless("allmydata-tahoe: %s" % str(allmydata.__version__)
-                                in res)
+                self.failUnless("%s: %s" % (allmydata.__appname__, allmydata.__version__) in res)
+                verstr = str(allmydata.__version__)
+
+                # The Python "rational version numbering" convention
+                # disallows "-r$REV" but allows ".post$REV"
+                # instead. Eventually we'll probably move to
+                # that. When we do, this test won't go red:
+                ix = verstr.rfind('-r')
+                if ix != -1:
+                    altverstr = verstr[:ix] + '.post' + verstr[ix+2:]
+                else:
+                    ix = verstr.rfind('.post')
+                    if ix != -1:
+                        altverstr = verstr[:ix] + '-r' + verstr[ix+5:]
+                    else:
+                        altverstr = verstr
+
+                appverstr = "%s: %s" % (allmydata.__appname__, verstr)
+                newappverstr = "%s: %s" % (allmydata.__appname__, altverstr)
+
+                self.failUnless((appverstr in res) or (newappverstr in res), (appverstr, newappverstr, res))
                 self.failUnless("Announcement Summary: storage: 5, stub_client: 5" in res)
                 self.failUnless("Subscription Summary: storage: 5" in res)
             except unittest.FailTest:
@@ -839,12 +814,12 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
     def _do_publish1(self, res):
         ut = upload.Data(self.data, convergence=None)
         c0 = self.clients[0]
-        d = c0.create_empty_dirnode()
+        d = c0.create_dirnode()
         def _made_root(new_dirnode):
             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)
@@ -859,7 +834,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
 
@@ -871,18 +846,22 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
     def _do_publish_private(self, res):
         self.smalldata = "sssh, very secret stuff"
         ut = upload.Data(self.smalldata, convergence=None)
-        d = self.clients[0].create_empty_dirnode()
+        d = self.clients[0].create_dirnode()
         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")
             d1.addCallback(lambda res: rootnode.get_child_at_path([u"subdir1", u"subdir2"]))
             def _got_s2(s2node):
-                d2 = privnode.set_uri(u"s2-rw", s2node.get_uri())
-                d2.addCallback(lambda node: privnode.set_uri(u"s2-ro", s2node.get_readonly_uri()))
+                d2 = privnode.set_uri(u"s2-rw", s2node.get_uri(),
+                                      s2node.get_readonly_uri())
+                d2.addCallback(lambda node:
+                               privnode.set_uri(u"s2-ro",
+                                                s2node.get_readonly_uri(),
+                                                s2node.get_readonly_uri()))
                 return d2
             d1.addCallback(_got_s2)
             d1.addCallback(lambda res: privnode)
@@ -897,7 +876,7 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
         d.addCallback(self.log, "check_publish1 got /")
         d.addCallback(lambda root: root.get(u"subdir1"))
         d.addCallback(lambda subdir1: subdir1.get(u"mydata567"))
-        d.addCallback(lambda filenode: filenode.download_to_data())
+        d.addCallback(lambda filenode: download_to_data(filenode))
         d.addCallback(self.log, "get finished")
         def _get_done(data):
             self.failUnlessEqual(data, self.data)
@@ -911,7 +890,7 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
         d.addCallback(lambda dirnode:
                       self.failUnless(IDirectoryNode.providedBy(dirnode)))
         d.addCallback(lambda res: rootnode.get_child_at_path(u"subdir1/mydata567"))
-        d.addCallback(lambda filenode: filenode.download_to_data())
+        d.addCallback(lambda filenode: download_to_data(filenode))
         d.addCallback(lambda data: self.failUnlessEqual(data, self.data))
 
         d.addCallback(lambda res: rootnode.get_child_at_path(u"subdir1/mydata567"))
@@ -937,7 +916,7 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
             return self._private_node.get_child_at_path(path)
 
         d.addCallback(lambda res: get_path(u"personal/sekrit data"))
-        d.addCallback(lambda filenode: filenode.download_to_data())
+        d.addCallback(lambda filenode: download_to_data(filenode))
         d.addCallback(lambda data: self.failUnlessEqual(data, self.smalldata))
         d.addCallback(lambda res: get_path(u"s2-rw"))
         d.addCallback(lambda dirnode: self.failUnless(dirnode.is_mutable()))
@@ -949,11 +928,11 @@ 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(NotWriteableError, "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)")
-            d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "add_file(nope)", None, dirnode.add_file, u"hope", ut))
+            d1.addCallback(lambda res: self.shouldFail2(NotWriteableError, "add_file(nope)", None, dirnode.add_file, u"hope", ut))
 
             d1.addCallback(self.log, "doing get(ro)")
             d1.addCallback(lambda res: dirnode.get(u"mydata992"))
@@ -961,17 +940,17 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
                            self.failUnless(IFileNode.providedBy(filenode)))
 
             d1.addCallback(self.log, "doing delete(ro)")
-            d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "delete(nope)", None, dirnode.delete, u"mydata992"))
+            d1.addCallback(lambda res: self.shouldFail2(NotWriteableError, "delete(nope)", None, dirnode.delete, u"mydata992"))
 
-            d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "set_uri(nope)", None, dirnode.set_uri, u"hopeless", self.uri))
+            d1.addCallback(lambda res: self.shouldFail2(NotWriteableError, "set_uri(nope)", None, dirnode.set_uri, u"hopeless", self.uri, self.uri))
 
             d1.addCallback(lambda res: self.shouldFail2(NoSuchChildError, "get(missing)", "missing", dirnode.get, u"missing"))
 
             personal = self._personal_node
-            d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "mv from readonly", None, dirnode.move_child_to, u"mydata992", personal, u"nope"))
+            d1.addCallback(lambda res: self.shouldFail2(NotWriteableError, "mv from readonly", None, dirnode.move_child_to, u"mydata992", personal, u"nope"))
 
             d1.addCallback(self.log, "doing move_child_to(ro)2")
-            d1.addCallback(lambda res: self.shouldFail2(NotMutableError, "mv to readonly", None, personal.move_child_to, u"sekrit data", dirnode, u"nope"))
+            d1.addCallback(lambda res: self.shouldFail2(NotWriteableError, "mv to readonly", None, personal.move_child_to, u"sekrit data", dirnode, u"nope"))
 
             d1.addCallback(self.log, "finished with _got_s2ro")
             return d1
@@ -1067,10 +1046,6 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
         return getPage(url, method="GET", followRedirect=followRedirect)
 
     def POST(self, urlpath, followRedirect=False, use_helper=False, **fields):
-        if use_helper:
-            url = self.helper_webish_url + urlpath
-        else:
-            url = self.webish_url + urlpath
         sepbase = "boogabooga"
         sep = "--" + sepbase
         form = []
@@ -1090,23 +1065,34 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
             form.append(str(value))
             form.append(sep)
         form[-1] += "--"
-        body = "\r\n".join(form) + "\r\n"
-        headers = {"content-type": "multipart/form-data; boundary=%s" % sepbase,
-                   }
-        return getPage(url, method="POST", postdata=body,
-                       headers=headers, followRedirect=followRedirect)
+        body = ""
+        headers = {}
+        if fields:
+            body = "\r\n".join(form) + "\r\n"
+            headers["content-type"] = "multipart/form-data; boundary=%s" % sepbase
+        return self.POST2(urlpath, body, headers, followRedirect, use_helper)
+
+    def POST2(self, urlpath, body="", headers={}, followRedirect=False,
+              use_helper=False):
+        if use_helper:
+            url = self.helper_webish_url + urlpath
+        else:
+            url = self.webish_url + urlpath
+        return getPage(url, method="POST", postdata=body, headers=headers,
+                       followRedirect=followRedirect)
 
     def _test_web(self, res):
         base = self.webish_url
         public = "uri/" + self._root_directory_uri
         d = getPage(base)
         def _got_welcome(page):
-            expected = "Connected Storage Servers: <span>%d</span>" % (self.numclients)
+            # XXX This test is oversensitive to formatting
+            expected = "Connected to <span>%d</span>\n     of <span>%d</span> known storage servers:" % (self.numclients, self.numclients)
             self.failUnless(expected in page,
                             "I didn't see the right 'connected storage servers'"
                             " message in: %s" % page
                             )
-            expected = "My nodeid: <span>%s</span>" % (b32encode(self.clients[0].nodeid).lower(),)
+            expected = "<th>My nodeid:</th> <td class=\"nodeid mine data-chars\">%s</td>" % (b32encode(self.clients[0].nodeid).lower(),)
             self.failUnless(expected in page,
                             "I didn't see the right 'My nodeid' message "
                             "in: %s" % page)
@@ -1126,7 +1112,7 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
         d.addCallback(lambda res: getPage(base + public + "/subdir1"))
         def _got_subdir1(page):
             # there ought to be an href for our file
-            self.failUnless(("<td>%d</td>" % len(self.data)) in page)
+            self.failUnlessIn('<td align="right">%d</td>' % len(self.data), page)
             self.failUnless(">mydata567</a>" in page)
         d.addCallback(_got_subdir1)
         d.addCallback(self.log, "done with _got_subdir1")
@@ -1172,6 +1158,11 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
         d.addCallback(self.failUnlessEqual, "new.txt contents")
         # and again with something large enough to use multiple segments,
         # and hopefully trigger pauseProducing too
+        def _new_happy_semantics(ign):
+            for c in self.clients:
+                # these get reset somewhere? Whatever.
+                c.DEFAULT_ENCODING_PARAMETERS['happy'] = 1
+        d.addCallback(_new_happy_semantics)
         d.addCallback(lambda res: self.PUT(public + "/subdir3/big.txt",
                                            "big" * 500000)) # 1.5MB
         d.addCallback(lambda res: self.GET(public + "/subdir3/big.txt"))
@@ -1196,17 +1187,18 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
         def _got_status(res):
             # find an interesting upload and download to look at. LIT files
             # are not interesting.
-            for ds in self.clients[0].list_all_download_statuses():
+            h = self.clients[0].get_history()
+            for ds in h.list_all_download_statuses():
                 if ds.get_size() > 200:
                     self._down_status = ds.get_counter()
-            for us in self.clients[0].list_all_upload_statuses():
+            for us in h.list_all_upload_statuses():
                 if us.get_size() > 200:
                     self._up_status = us.get_counter()
-            rs = list(self.clients[0].list_all_retrieve_statuses())[0]
+            rs = list(h.list_all_retrieve_statuses())[0]
             self._retrieve_status = rs.get_counter()
-            ps = list(self.clients[0].list_all_publish_statuses())[0]
+            ps = list(h.list_all_publish_statuses())[0]
             self._publish_status = ps.get_counter()
-            us = list(self.clients[0].list_all_mapupdate_statuses())[0]
+            us = list(h.list_all_mapupdate_statuses())[0]
             self._update_status = us.get_counter()
 
             # and that there are some upload- and download- status pages
@@ -1307,7 +1299,7 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
         # exercise some of the diagnostic tools in runner.py
 
         # find a share
-        for (dirpath, dirnames, filenames) in os.walk(self.basedir):
+        for (dirpath, dirnames, filenames) in os.walk(unicode(self.basedir)):
             if "storage" not in dirpath:
                 continue
             if not filenames:
@@ -1324,25 +1316,25 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
                 if magic == '\x00\x00\x00\x01':
                     break
         else:
-            self.fail("unable to find any uri_extension files in %s"
+            self.fail("unable to find any uri_extension files in %r"
                       % self.basedir)
-        log.msg("test_system.SystemTest._test_runner using %s" % filename)
+        log.msg("test_system.SystemTest._test_runner using %r" % filename)
 
         out,err = StringIO(), StringIO()
         rc = runner.runner(["debug", "dump-share", "--offsets",
-                            filename],
+                            unicode_to_argv(filename)],
                            stdout=out, stderr=err)
         output = out.getvalue()
         self.failUnlessEqual(rc, 0)
 
         # we only upload a single file, so we can assert some things about
         # its size and shares.
-        self.failUnless(("share filename: %s" % filename) in output)
-        self.failUnless("size: %d\n" % len(self.data) in output)
-        self.failUnless("num_segments: 1\n" in output)
+        self.failUnlessIn("share filename: %s" % quote_output(abspath_expanduser_unicode(filename)), output)
+        self.failUnlessIn("size: %d\n" % len(self.data), output)
+        self.failUnlessIn("num_segments: 1\n", output)
         # segment_size is always a multiple of needed_shares
-        self.failUnless("segment_size: %d\n" % mathutil.next_multiple(len(self.data), 3) in output)
-        self.failUnless("total_shares: 10\n" in output)
+        self.failUnlessIn("segment_size: %d\n" % mathutil.next_multiple(len(self.data), 3), output)
+        self.failUnlessIn("total_shares: 10\n", output)
         # keys which are supposed to be present
         for key in ("size", "num_segments", "segment_size",
                     "needed_shares", "total_shares",
@@ -1350,13 +1342,14 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
                     #"plaintext_hash", "plaintext_root_hash",
                     "crypttext_hash", "crypttext_root_hash",
                     "share_root_hash", "UEB_hash"):
-            self.failUnless("%s: " % key in output, key)
-        self.failUnless("  verify-cap: URI:CHK-Verifier:" in output)
+            self.failUnlessIn("%s: " % key, output)
+        self.failUnlessIn("  verify-cap: URI:CHK-Verifier:", output)
 
         # now use its storage index to find the other shares using the
         # 'find-shares' tool
         sharedir, shnum = os.path.split(filename)
         storagedir, storage_index_s = os.path.split(sharedir)
+        storage_index_s = str(storage_index_s)
         out,err = StringIO(), StringIO()
         nodedirs = [self.getdir("client%d" % i) for i in range(self.numclients)]
         cmd = ["debug", "find-shares", storage_index_s] + nodedirs
@@ -1392,8 +1385,9 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
         d.addCallback(self._test_control2, control_furl_file)
         return d
     def _test_control2(self, rref, filename):
-        d = rref.callRemote("upload_from_file_to_uri", filename, convergence=None)
-        downfile = os.path.join(self.basedir, "control.downfile")
+        d = rref.callRemote("upload_from_file_to_uri",
+                            filename.encode(get_filesystem_encoding()), convergence=None)
+        downfile = os.path.join(self.basedir, "control.downfile").encode(get_filesystem_encoding())
         d.addCallback(lambda uri:
                       rref.callRemote("download_from_uri_to_file",
                                       uri, downfile))
@@ -1404,7 +1398,7 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
             self.failUnlessEqual(data, expected_data)
         d.addCallback(_check)
         d.addCallback(lambda res: rref.callRemote("speed_test", 1, 200, False))
-        if sys.platform == "linux2":
+        if sys.platform in ("linux2", "linux3"):
             d.addCallback(lambda res: rref.callRemote("get_memory_usage"))
         d.addCallback(lambda res: rref.callRemote("measure_peer_response_time"))
         return d
@@ -1414,13 +1408,11 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
         # network calls)
 
         private_uri = self._private_node.get_uri()
-        some_uri = self._root_directory_uri
         client0_basedir = self.getdir("client0")
 
         nodeargs = [
             "--node-directory", client0_basedir,
             ]
-        TESTDATA = "I will not write the same thing over and over.\n" * 100
 
         d = defer.succeed(None)
 
@@ -1458,7 +1450,7 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
         d.addCallback(run, "list-aliases")
         def _check_aliases_1((out,err)):
             self.failUnlessEqual(err, "")
-            self.failUnlessEqual(out, "tahoe: %s\n" % private_uri)
+            self.failUnlessEqual(out.strip(" \n"), "tahoe: %s" % private_uri)
         d.addCallback(_check_aliases_1)
 
         # now that that's out of the way, remove root_dir.cap and work with
@@ -1751,6 +1743,89 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
 
         return d
 
+    def test_filesystem_with_cli_in_subprocess(self):
+        # We do this in a separate test so that test_filesystem doesn't skip if we can't run bin/tahoe.
+
+        self.basedir = "system/SystemTest/test_filesystem_with_cli_in_subprocess"
+        d = self.set_up_nodes()
+        def _new_happy_semantics(ign):
+            for c in self.clients:
+                c.DEFAULT_ENCODING_PARAMETERS['happy'] = 1
+        d.addCallback(_new_happy_semantics)
+
+        def _run_in_subprocess(ignored, verb, *args, **kwargs):
+            stdin = kwargs.get("stdin")
+            env = kwargs.get("env")
+            newargs = [verb, "--node-directory", self.getdir("client0")] + list(args)
+            return self.run_bintahoe(newargs, stdin=stdin, env=env)
+
+        def _check_succeeded(res, check_stderr=True):
+            out, err, rc_or_sig = res
+            self.failUnlessEqual(rc_or_sig, 0, str(res))
+            if check_stderr:
+                self.failUnlessEqual(err, "")
+
+        d.addCallback(_run_in_subprocess, "create-alias", "newalias")
+        d.addCallback(_check_succeeded)
+
+        STDIN_DATA = "This is the file to upload from stdin."
+        d.addCallback(_run_in_subprocess, "put", "-", "newalias:tahoe-file", stdin=STDIN_DATA)
+        d.addCallback(_check_succeeded, check_stderr=False)
+
+        def _mv_with_http_proxy(ign):
+            env = os.environ
+            env['http_proxy'] = env['HTTP_PROXY'] = "http://127.0.0.0:12345"  # invalid address
+            return _run_in_subprocess(None, "mv", "newalias:tahoe-file", "newalias:tahoe-moved", env=env)
+        d.addCallback(_mv_with_http_proxy)
+        d.addCallback(_check_succeeded)
+
+        d.addCallback(_run_in_subprocess, "ls", "newalias:")
+        def _check_ls(res):
+            out, err, rc_or_sig = res
+            self.failUnlessEqual(rc_or_sig, 0, str(res))
+            self.failUnlessEqual(err, "", str(res))
+            self.failUnlessIn("tahoe-moved", out)
+            self.failIfIn("tahoe-file", out)
+        d.addCallback(_check_ls)
+        return d
+
+    def test_debug_trial(self):
+        def _check_for_line(lines, result, test):
+            for l in lines:
+                if result in l and test in l:
+                    return
+            self.fail("output (prefixed with '##') does not have a line containing both %r and %r:\n## %s"
+                      % (result, test, "\n## ".join(lines)))
+
+        def _check_for_outcome(lines, out, outcome):
+            self.failUnlessIn(outcome, out, "output (prefixed with '##') does not contain %r:\n## %s"
+                                            % (outcome, "\n## ".join(lines)))
+
+        d = self.run_bintahoe(['debug', 'trial', '--reporter=verbose',
+                               'allmydata.test.trialtest'])
+        def _check_failure( (out, err, rc) ):
+            self.failUnlessEqual(rc, 1)
+            lines = out.split('\n')
+            _check_for_line(lines, "[SKIPPED]", "test_skip")
+            _check_for_line(lines, "[TODO]",    "test_todo")
+            _check_for_line(lines, "[FAIL]",    "test_fail")
+            _check_for_line(lines, "[ERROR]",   "test_deferred_error")
+            _check_for_line(lines, "[ERROR]",   "test_error")
+            _check_for_outcome(lines, out, "FAILED")
+        d.addCallback(_check_failure)
+
+        # the --quiet argument regression-tests a problem in finding which arguments to pass to trial
+        d.addCallback(lambda ign: self.run_bintahoe(['--quiet', 'debug', 'trial', '--reporter=verbose',
+                                                     'allmydata.test.trialtest.Success']))
+        def _check_success( (out, err, rc) ):
+            self.failUnlessEqual(rc, 0)
+            lines = out.split('\n')
+            _check_for_line(lines, "[SKIPPED]", "test_skip")
+            _check_for_line(lines, "[TODO]",    "test_todo")
+            _check_for_outcome(lines, out, "PASSED")
+        d.addCallback(_check_success)
+        return d
+
     def _run_cli(self, argv, stdin=""):
         #print "CLI:", argv
         stdout, stderr = StringIO(), StringIO()
@@ -1775,7 +1850,7 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
 
         d.addCallback(lambda res: self._personal_node.get(u"big file"))
         def _got_chk_filenode(n):
-            self.failUnless(isinstance(n, filenode.FileNode))
+            self.failUnless(isinstance(n, ImmutableFileNode))
             d = n.check(Monitor())
             def _check_filenode_results(r):
                 self.failUnless(r.is_healthy())
@@ -1787,7 +1862,7 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
 
         d.addCallback(lambda res: self._personal_node.get(u"sekrit data"))
         def _got_lit_filenode(n):
-            self.failUnless(isinstance(n, filenode.LiteralFileNode))
+            self.failUnless(isinstance(n, LiteralFileNode))
             d = n.check(Monitor())
             def _check_lit_filenode_results(r):
                 self.failUnlessEqual(r, None)
@@ -1797,4 +1872,3 @@ class SystemTest(SystemTestMixin, unittest.TestCase):
             return d
         d.addCallback(_got_lit_filenode)
         return d
-