]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/blob - src/allmydata/test/test_client.py
d6cd7d0d45e35a0e77e88693535182875440ecd4
[tahoe-lafs/tahoe-lafs.git] / src / allmydata / test / test_client.py
1 import os, sys
2 from twisted.trial import unittest
3 from twisted.application import service
4
5 import allmydata
6 from allmydata.node import Node, OldConfigError, OldConfigOptionError, MissingConfigEntry, UnescapedHashError
7 from allmydata.frontends.auth import NeedRootcapLookupScheme
8 from allmydata import client
9 from allmydata.storage_client import StorageFarmBroker
10 from allmydata.manhole import AuthorizedKeysManhole
11 from allmydata.util import base32, fileutil
12 from allmydata.interfaces import IFilesystemNode, IFileNode, \
13      IImmutableFileNode, IMutableFileNode, IDirectoryNode
14 from foolscap.api import flushEventualQueue
15 import allmydata.test.common_util as testutil
16
17 import mock
18
19 BASECONFIG = ("[client]\n"
20               "introducer.furl = \n"
21               )
22
23 BASECONFIG_I = ("[client]\n"
24               "introducer.furl = %s\n"
25               )
26
27 class Basic(testutil.ReallyEqualMixin, unittest.TestCase):
28     def test_loadable(self):
29         basedir = "test_client.Basic.test_loadable"
30         os.mkdir(basedir)
31         fileutil.write(os.path.join(basedir, "tahoe.cfg"), \
32                            BASECONFIG)
33         client.Client(basedir)
34
35     def test_comment(self):
36         should_fail = [r"test#test", r"#testtest", r"test\\#test"]
37         should_not_fail = [r"test\#test", r"test\\\#test", r"testtest"]
38
39         basedir = "test_client.Basic.test_comment"
40         os.mkdir(basedir)
41
42         def write_config(s):
43             config = ("[client]\n"
44                       "introducer.furl = %s\n" % s)
45             fileutil.write(os.path.join(basedir, "tahoe.cfg"), config)
46
47         for s in should_fail:
48             self.failUnless(Node._contains_unescaped_hash(s))
49             write_config(s)
50             self.failUnlessRaises(UnescapedHashError, client.Client, basedir)
51
52         for s in should_not_fail:
53             self.failIf(Node._contains_unescaped_hash(s))
54             write_config(s)
55             client.Client(basedir)
56
57
58     @mock.patch('twisted.python.log.msg')
59     def test_error_on_old_config_files(self, mock_log_msg):
60         basedir = "test_client.Basic.test_error_on_old_config_files"
61         os.mkdir(basedir)
62         fileutil.write(os.path.join(basedir, "tahoe.cfg"),
63                        BASECONFIG +
64                        "[storage]\n" +
65                        "enabled = false\n" +
66                        "reserved_space = bogus\n")
67         fileutil.write(os.path.join(basedir, "introducer.furl"), "")
68         fileutil.write(os.path.join(basedir, "no_storage"), "")
69         fileutil.write(os.path.join(basedir, "readonly_storage"), "")
70         fileutil.write(os.path.join(basedir, "debug_discard_storage"), "")
71
72         e = self.failUnlessRaises(OldConfigError, client.Client, basedir)
73         abs_basedir = fileutil.abspath_expanduser_unicode(unicode(basedir)).encode(sys.getfilesystemencoding())
74         self.failUnlessIn(os.path.join(abs_basedir, "introducer.furl"), e.args[0])
75         self.failUnlessIn(os.path.join(abs_basedir, "no_storage"), e.args[0])
76         self.failUnlessIn(os.path.join(abs_basedir, "readonly_storage"), e.args[0])
77         self.failUnlessIn(os.path.join(abs_basedir, "debug_discard_storage"), e.args[0])
78
79         for oldfile in ['introducer.furl', 'no_storage', 'readonly_storage',
80                         'debug_discard_storage']:
81             logged = [ m for m in mock_log_msg.call_args_list if
82                        ("Found pre-Tahoe-LAFS-v1.3 configuration file" in str(m[0][0]) and oldfile in str(m[0][0])) ]
83             self.failUnless(logged, (oldfile, mock_log_msg.call_args_list))
84
85         for oldfile in [
86             'nickname', 'webport', 'keepalive_timeout', 'log_gatherer.furl',
87             'disconnect_timeout', 'advertised_ip_addresses', 'helper.furl',
88             'key_generator.furl', 'stats_gatherer.furl', 'sizelimit',
89             'run_helper']:
90             logged = [ m for m in mock_log_msg.call_args_list if
91                        ("Found pre-Tahoe-LAFS-v1.3 configuration file" in str(m[0][0]) and oldfile in str(m[0][0])) ]
92             self.failIf(logged, oldfile)
93
94     def test_secrets(self):
95         basedir = "test_client.Basic.test_secrets"
96         os.mkdir(basedir)
97         fileutil.write(os.path.join(basedir, "tahoe.cfg"), \
98                            BASECONFIG)
99         c = client.Client(basedir)
100         secret_fname = os.path.join(basedir, "private", "secret")
101         self.failUnless(os.path.exists(secret_fname), secret_fname)
102         renew_secret = c.get_renewal_secret()
103         self.failUnless(base32.b2a(renew_secret))
104         cancel_secret = c.get_cancel_secret()
105         self.failUnless(base32.b2a(cancel_secret))
106
107     def test_nodekey_yes_storage(self):
108         basedir = "test_client.Basic.test_nodekey_yes_storage"
109         os.mkdir(basedir)
110         fileutil.write(os.path.join(basedir, "tahoe.cfg"),
111                        BASECONFIG)
112         c = client.Client(basedir)
113         self.failUnless(c.get_long_nodeid().startswith("v0-"))
114
115     def test_nodekey_no_storage(self):
116         basedir = "test_client.Basic.test_nodekey_no_storage"
117         os.mkdir(basedir)
118         fileutil.write(os.path.join(basedir, "tahoe.cfg"),
119                        BASECONFIG + "[storage]\n" + "enabled = false\n")
120         c = client.Client(basedir)
121         self.failUnless(c.get_long_nodeid().startswith("v0-"))
122
123     def test_reserved_1(self):
124         basedir = "client.Basic.test_reserved_1"
125         os.mkdir(basedir)
126         fileutil.write(os.path.join(basedir, "tahoe.cfg"), \
127                            BASECONFIG + \
128                            "[storage]\n" + \
129                            "enabled = true\n" + \
130                            "reserved_space = 1000\n")
131         c = client.Client(basedir)
132         self.failUnlessEqual(c.getServiceNamed("storage").reserved_space, 1000)
133
134     def test_reserved_2(self):
135         basedir = "client.Basic.test_reserved_2"
136         os.mkdir(basedir)
137         fileutil.write(os.path.join(basedir, "tahoe.cfg"),  \
138                            BASECONFIG + \
139                            "[storage]\n" + \
140                            "enabled = true\n" + \
141                            "reserved_space = 10K\n")
142         c = client.Client(basedir)
143         self.failUnlessEqual(c.getServiceNamed("storage").reserved_space, 10*1000)
144
145     def test_reserved_3(self):
146         basedir = "client.Basic.test_reserved_3"
147         os.mkdir(basedir)
148         fileutil.write(os.path.join(basedir, "tahoe.cfg"), \
149                            BASECONFIG + \
150                            "[storage]\n" + \
151                            "enabled = true\n" + \
152                            "reserved_space = 5mB\n")
153         c = client.Client(basedir)
154         self.failUnlessEqual(c.getServiceNamed("storage").reserved_space,
155                              5*1000*1000)
156
157     def test_reserved_4(self):
158         basedir = "client.Basic.test_reserved_4"
159         os.mkdir(basedir)
160         fileutil.write(os.path.join(basedir, "tahoe.cfg"), \
161                            BASECONFIG + \
162                            "[storage]\n" + \
163                            "enabled = true\n" + \
164                            "reserved_space = 78Gb\n")
165         c = client.Client(basedir)
166         self.failUnlessEqual(c.getServiceNamed("storage").reserved_space,
167                              78*1000*1000*1000)
168
169     def test_reserved_bad(self):
170         basedir = "client.Basic.test_reserved_bad"
171         os.mkdir(basedir)
172         fileutil.write(os.path.join(basedir, "tahoe.cfg"), \
173                            BASECONFIG + \
174                            "[storage]\n" + \
175                            "enabled = true\n" + \
176                            "reserved_space = bogus\n")
177         self.failUnlessRaises(ValueError, client.Client, basedir)
178
179     def test_web_staticdir(self):
180         basedir = u"client.Basic.test_web_staticdir"
181         os.mkdir(basedir)
182         fileutil.write(os.path.join(basedir, "tahoe.cfg"),
183                        BASECONFIG +
184                        "[node]\n" +
185                        "web.port = tcp:0:interface=127.0.0.1\n" +
186                        "web.static = relative\n")
187         c = client.Client(basedir)
188         w = c.getServiceNamed("webish")
189         abs_basedir = fileutil.abspath_expanduser_unicode(basedir)
190         expected = fileutil.abspath_expanduser_unicode(u"relative", abs_basedir)
191         self.failUnlessReallyEqual(w.staticdir, expected)
192
193     def test_manhole_keyfile(self):
194         basedir = u"client.Basic.test_manhole_keyfile"
195         os.mkdir(basedir)
196         fileutil.write(os.path.join(basedir, "tahoe.cfg"),
197                        BASECONFIG +
198                        "[node]\n" +
199                        "ssh.port = tcp:0:interface=127.0.0.1\n" +
200                        "ssh.authorized_keys_file = relative\n")
201         c = client.Client(basedir)
202         m = [s for s in c if isinstance(s, AuthorizedKeysManhole)][0]
203         abs_basedir = fileutil.abspath_expanduser_unicode(basedir)
204         expected = fileutil.abspath_expanduser_unicode(u"relative", abs_basedir)
205         self.failUnlessReallyEqual(m.keyfile, expected)
206
207     # TODO: also test config options for SFTP.
208
209     def test_ftp_auth_keyfile(self):
210         basedir = u"client.Basic.test_ftp_auth_keyfile"
211         os.mkdir(basedir)
212         fileutil.write(os.path.join(basedir, "tahoe.cfg"),
213                        (BASECONFIG +
214                         "[ftpd]\n"
215                         "enabled = true\n"
216                         "port = tcp:0:interface=127.0.0.1\n"
217                         "accounts.file = private/accounts\n"))
218         os.mkdir(os.path.join(basedir, "private"))
219         fileutil.write(os.path.join(basedir, "private", "accounts"), "\n")
220         c = client.Client(basedir) # just make sure it can be instantiated
221         del c
222
223     def test_ftp_auth_url(self):
224         basedir = u"client.Basic.test_ftp_auth_url"
225         os.mkdir(basedir)
226         fileutil.write(os.path.join(basedir, "tahoe.cfg"),
227                        (BASECONFIG +
228                         "[ftpd]\n"
229                         "enabled = true\n"
230                         "port = tcp:0:interface=127.0.0.1\n"
231                         "accounts.url = http://0.0.0.0/\n"))
232         c = client.Client(basedir) # just make sure it can be instantiated
233         del c
234
235     def test_ftp_auth_no_accountfile_or_url(self):
236         basedir = u"client.Basic.test_ftp_auth_no_accountfile_or_url"
237         os.mkdir(basedir)
238         fileutil.write(os.path.join(basedir, "tahoe.cfg"),
239                        (BASECONFIG +
240                         "[ftpd]\n"
241                         "enabled = true\n"
242                         "port = tcp:0:interface=127.0.0.1\n"))
243         self.failUnlessRaises(NeedRootcapLookupScheme, client.Client, basedir)
244
245     def _permute(self, sb, key):
246         return [ s.get_longname() for s in sb.get_servers_for_psi(key) ]
247
248     def test_permute(self):
249         sb = StorageFarmBroker(None, True)
250         for k in ["%d" % i for i in range(5)]:
251             ann = {"anonymous-storage-FURL": "pb://abcde@nowhere/fake",
252                    "permutation-seed-base32": base32.b2a(k) }
253             sb.test_add_rref(k, "rref", ann)
254
255         self.failUnlessReallyEqual(self._permute(sb, "one"), ['3','1','0','4','2'])
256         self.failUnlessReallyEqual(self._permute(sb, "two"), ['0','4','2','1','3'])
257         sb.servers.clear()
258         self.failUnlessReallyEqual(self._permute(sb, "one"), [])
259
260     def test_versions(self):
261         basedir = "test_client.Basic.test_versions"
262         os.mkdir(basedir)
263         fileutil.write(os.path.join(basedir, "tahoe.cfg"), \
264                            BASECONFIG + \
265                            "[storage]\n" + \
266                            "enabled = true\n")
267         c = client.Client(basedir)
268         ss = c.getServiceNamed("storage")
269         verdict = ss.remote_get_version()
270         self.failUnlessReallyEqual(verdict["application-version"],
271                                    str(allmydata.__full_version__))
272         self.failIfEqual(str(allmydata.__version__), "unknown")
273         self.failUnless("." in str(allmydata.__full_version__),
274                         "non-numeric version in '%s'" % allmydata.__version__)
275         all_versions = allmydata.get_package_versions_string()
276         self.failUnless(allmydata.__appname__ in all_versions)
277         # also test stats
278         stats = c.get_stats()
279         self.failUnless("node.uptime" in stats)
280         self.failUnless(isinstance(stats["node.uptime"], float))
281
282     def test_helper_furl(self):
283         basedir = "test_client.Basic.test_helper_furl"
284         os.mkdir(basedir)
285
286         def _check(config, expected_furl):
287             fileutil.write(os.path.join(basedir, "tahoe.cfg"),
288                            BASECONFIG + config)
289             c = client.Client(basedir)
290             uploader = c.getServiceNamed("uploader")
291             furl, connected = uploader.get_helper_info()
292             self.failUnlessEqual(furl, expected_furl)
293
294         _check("", None)
295         _check("helper.furl =\n", None)
296         _check("helper.furl = \n", None)
297         _check("helper.furl = None", None)
298         _check("helper.furl = pb://blah\n", "pb://blah")
299
300     @mock.patch('allmydata.util.log.msg')
301     @mock.patch('allmydata.frontends.drop_upload.DropUploader')
302     def test_create_drop_uploader(self, mock_drop_uploader, mock_log_msg):
303         class MockDropUploader(service.MultiService):
304             name = 'drop-upload'
305
306             def __init__(self, client, upload_dircap, local_dir_utf8, inotify=None):
307                 service.MultiService.__init__(self)
308                 self.client = client
309                 self.upload_dircap = upload_dircap
310                 self.local_dir_utf8 = local_dir_utf8
311                 self.inotify = inotify
312
313         mock_drop_uploader.side_effect = MockDropUploader
314
315         upload_dircap = "URI:DIR2:blah"
316         local_dir_utf8 = u"loc\u0101l_dir".encode('utf-8')
317         config = (BASECONFIG +
318                   "[storage]\n" +
319                   "enabled = false\n" +
320                   "[drop_upload]\n" +
321                   "enabled = true\n")
322
323         basedir1 = "test_client.Basic.test_create_drop_uploader1"
324         os.mkdir(basedir1)
325         fileutil.write(os.path.join(basedir1, "tahoe.cfg"),
326                        config + "local.directory = " + local_dir_utf8 + "\n")
327         self.failUnlessRaises(MissingConfigEntry, client.Client, basedir1)
328
329         fileutil.write(os.path.join(basedir1, "tahoe.cfg"), config)
330         fileutil.write(os.path.join(basedir1, "private", "drop_upload_dircap"), "URI:DIR2:blah")
331         self.failUnlessRaises(MissingConfigEntry, client.Client, basedir1)
332
333         fileutil.write(os.path.join(basedir1, "tahoe.cfg"),
334                        config + "upload.dircap = " + upload_dircap + "\n")
335         self.failUnlessRaises(OldConfigOptionError, client.Client, basedir1)
336
337         fileutil.write(os.path.join(basedir1, "tahoe.cfg"),
338                        config + "local.directory = " + local_dir_utf8 + "\n")
339         c1 = client.Client(basedir1)
340         uploader = c1.getServiceNamed('drop-upload')
341         self.failUnless(isinstance(uploader, MockDropUploader), uploader)
342         self.failUnlessReallyEqual(uploader.client, c1)
343         self.failUnlessReallyEqual(uploader.upload_dircap, upload_dircap)
344         self.failUnlessReallyEqual(uploader.local_dir_utf8, local_dir_utf8)
345         self.failUnless(uploader.inotify is None, uploader.inotify)
346         self.failUnless(uploader.running)
347
348         class Boom(Exception):
349             pass
350         mock_drop_uploader.side_effect = Boom()
351
352         basedir2 = "test_client.Basic.test_create_drop_uploader2"
353         os.mkdir(basedir2)
354         os.mkdir(os.path.join(basedir2, "private"))
355         fileutil.write(os.path.join(basedir2, "tahoe.cfg"),
356                        BASECONFIG +
357                        "[drop_upload]\n" +
358                        "enabled = true\n" +
359                        "local.directory = " + local_dir_utf8 + "\n")
360         fileutil.write(os.path.join(basedir2, "private", "drop_upload_dircap"), "URI:DIR2:blah")
361         c2 = client.Client(basedir2)
362         self.failUnlessRaises(KeyError, c2.getServiceNamed, 'drop-upload')
363         self.failUnless([True for arg in mock_log_msg.call_args_list if "Boom" in repr(arg)],
364                         mock_log_msg.call_args_list)
365
366
367 def flush_but_dont_ignore(res):
368     d = flushEventualQueue()
369     def _done(ignored):
370         return res
371     d.addCallback(_done)
372     return d
373
374 class Run(unittest.TestCase, testutil.StallMixin):
375
376     def setUp(self):
377         self.sparent = service.MultiService()
378         self.sparent.startService()
379     def tearDown(self):
380         d = self.sparent.stopService()
381         d.addBoth(flush_but_dont_ignore)
382         return d
383
384     def test_loadable(self):
385         basedir = "test_client.Run.test_loadable"
386         os.mkdir(basedir)
387         dummy = "pb://wl74cyahejagspqgy4x5ukrvfnevlknt@127.0.0.1:58889/bogus"
388         fileutil.write(os.path.join(basedir, "tahoe.cfg"), BASECONFIG_I % dummy)
389         fileutil.write(os.path.join(basedir, client.Client.EXIT_TRIGGER_FILE), "")
390         client.Client(basedir)
391
392     def test_reloadable(self):
393         basedir = "test_client.Run.test_reloadable"
394         os.mkdir(basedir)
395         dummy = "pb://wl74cyahejagspqgy4x5ukrvfnevlknt@127.0.0.1:58889/bogus"
396         fileutil.write(os.path.join(basedir, "tahoe.cfg"), BASECONFIG_I % dummy)
397         c1 = client.Client(basedir)
398         c1.setServiceParent(self.sparent)
399
400         # delay to let the service start up completely. I'm not entirely sure
401         # this is necessary.
402         d = self.stall(delay=2.0)
403         d.addCallback(lambda res: c1.disownServiceParent())
404         # the cygwin buildslave seems to need more time to let the old
405         # service completely shut down. When delay=0.1, I saw this test fail,
406         # probably due to the logport trying to reclaim the old socket
407         # number. This suggests that either we're dropping a Deferred
408         # somewhere in the shutdown sequence, or that cygwin is just cranky.
409         d.addCallback(self.stall, delay=2.0)
410         def _restart(res):
411             # TODO: pause for slightly over one second, to let
412             # Client._check_exit_trigger poll the file once. That will exercise
413             # another few lines. Then add another test in which we don't
414             # update the file at all, and watch to see the node shutdown.
415             # (To do this, use a modified node which overrides Node.shutdown(),
416             # also change _check_exit_trigger to use it instead of a raw
417             # reactor.stop, also instrument the shutdown event in an
418             # attribute that we can check.)
419             c2 = client.Client(basedir)
420             c2.setServiceParent(self.sparent)
421             return c2.disownServiceParent()
422         d.addCallback(_restart)
423         return d
424
425 class NodeMaker(testutil.ReallyEqualMixin, unittest.TestCase):
426     def test_maker(self):
427         basedir = "client/NodeMaker/maker"
428         fileutil.make_dirs(basedir)
429         fileutil.write(os.path.join(basedir, "tahoe.cfg"), BASECONFIG)
430         c = client.Client(basedir)
431
432         n = c.create_node_from_uri("URI:CHK:6nmrpsubgbe57udnexlkiwzmlu:bjt7j6hshrlmadjyr7otq3dc24end5meo5xcr5xe5r663po6itmq:3:10:7277")
433         self.failUnless(IFilesystemNode.providedBy(n))
434         self.failUnless(IFileNode.providedBy(n))
435         self.failUnless(IImmutableFileNode.providedBy(n))
436         self.failIf(IMutableFileNode.providedBy(n))
437         self.failIf(IDirectoryNode.providedBy(n))
438         self.failUnless(n.is_readonly())
439         self.failIf(n.is_mutable())
440
441         # Testing #1679. There was a bug that would occur when downloader was
442         # downloading the same readcap more than once concurrently, so the
443         # filenode object was cached, and there was a failure from one of the
444         # servers in one of the download attempts. No subsequent download
445         # attempt would attempt to use that server again, which would lead to
446         # the file being undownloadable until the gateway was restarted. The
447         # current fix for this (hopefully to be superceded by a better fix
448         # eventually) is to prevent re-use of filenodes, so the NodeMaker is
449         # hereby required *not* to cache and re-use filenodes for CHKs.
450         other_n = c.create_node_from_uri("URI:CHK:6nmrpsubgbe57udnexlkiwzmlu:bjt7j6hshrlmadjyr7otq3dc24end5meo5xcr5xe5r663po6itmq:3:10:7277")
451         self.failIf(n is other_n, (n, other_n))
452
453         n = c.create_node_from_uri("URI:LIT:n5xgk")
454         self.failUnless(IFilesystemNode.providedBy(n))
455         self.failUnless(IFileNode.providedBy(n))
456         self.failUnless(IImmutableFileNode.providedBy(n))
457         self.failIf(IMutableFileNode.providedBy(n))
458         self.failIf(IDirectoryNode.providedBy(n))
459         self.failUnless(n.is_readonly())
460         self.failIf(n.is_mutable())
461
462         n = c.create_node_from_uri("URI:SSK:n6x24zd3seu725yluj75q5boaa:mm6yoqjhl6ueh7iereldqxue4nene4wl7rqfjfybqrehdqmqskvq")
463         self.failUnless(IFilesystemNode.providedBy(n))
464         self.failUnless(IFileNode.providedBy(n))
465         self.failIf(IImmutableFileNode.providedBy(n))
466         self.failUnless(IMutableFileNode.providedBy(n))
467         self.failIf(IDirectoryNode.providedBy(n))
468         self.failIf(n.is_readonly())
469         self.failUnless(n.is_mutable())
470
471         n = c.create_node_from_uri("URI:SSK-RO:b7sr5qsifnicca7cbk3rhrhbvq:mm6yoqjhl6ueh7iereldqxue4nene4wl7rqfjfybqrehdqmqskvq")
472         self.failUnless(IFilesystemNode.providedBy(n))
473         self.failUnless(IFileNode.providedBy(n))
474         self.failIf(IImmutableFileNode.providedBy(n))
475         self.failUnless(IMutableFileNode.providedBy(n))
476         self.failIf(IDirectoryNode.providedBy(n))
477         self.failUnless(n.is_readonly())
478         self.failUnless(n.is_mutable())
479
480         n = c.create_node_from_uri("URI:DIR2:n6x24zd3seu725yluj75q5boaa:mm6yoqjhl6ueh7iereldqxue4nene4wl7rqfjfybqrehdqmqskvq")
481         self.failUnless(IFilesystemNode.providedBy(n))
482         self.failIf(IFileNode.providedBy(n))
483         self.failIf(IImmutableFileNode.providedBy(n))
484         self.failIf(IMutableFileNode.providedBy(n))
485         self.failUnless(IDirectoryNode.providedBy(n))
486         self.failIf(n.is_readonly())
487         self.failUnless(n.is_mutable())
488
489         n = c.create_node_from_uri("URI:DIR2-RO:b7sr5qsifnicca7cbk3rhrhbvq:mm6yoqjhl6ueh7iereldqxue4nene4wl7rqfjfybqrehdqmqskvq")
490         self.failUnless(IFilesystemNode.providedBy(n))
491         self.failIf(IFileNode.providedBy(n))
492         self.failIf(IImmutableFileNode.providedBy(n))
493         self.failIf(IMutableFileNode.providedBy(n))
494         self.failUnless(IDirectoryNode.providedBy(n))
495         self.failUnless(n.is_readonly())
496         self.failUnless(n.is_mutable())
497
498         unknown_rw = "lafs://from_the_future"
499         unknown_ro = "lafs://readonly_from_the_future"
500         n = c.create_node_from_uri(unknown_rw, unknown_ro)
501         self.failUnless(IFilesystemNode.providedBy(n))
502         self.failIf(IFileNode.providedBy(n))
503         self.failIf(IImmutableFileNode.providedBy(n))
504         self.failIf(IMutableFileNode.providedBy(n))
505         self.failIf(IDirectoryNode.providedBy(n))
506         self.failUnless(n.is_unknown())
507         self.failUnlessReallyEqual(n.get_uri(), unknown_rw)
508         self.failUnlessReallyEqual(n.get_write_uri(), unknown_rw)
509         self.failUnlessReallyEqual(n.get_readonly_uri(), "ro." + unknown_ro)
510
511         # Note: it isn't that we *intend* to deploy non-ASCII caps in
512         # the future, it is that we want to make sure older Tahoe-LAFS
513         # versions wouldn't choke on them if we were to do so. See
514         # #1051 and wiki:NewCapDesign for details.
515         unknown_rw = u"lafs://from_the_future_rw_\u263A".encode('utf-8')
516         unknown_ro = u"lafs://readonly_from_the_future_ro_\u263A".encode('utf-8')
517         n = c.create_node_from_uri(unknown_rw, unknown_ro)
518         self.failUnless(IFilesystemNode.providedBy(n))
519         self.failIf(IFileNode.providedBy(n))
520         self.failIf(IImmutableFileNode.providedBy(n))
521         self.failIf(IMutableFileNode.providedBy(n))
522         self.failIf(IDirectoryNode.providedBy(n))
523         self.failUnless(n.is_unknown())
524         self.failUnlessReallyEqual(n.get_uri(), unknown_rw)
525         self.failUnlessReallyEqual(n.get_write_uri(), unknown_rw)
526         self.failUnlessReallyEqual(n.get_readonly_uri(), "ro." + unknown_ro)