3 from twisted.trial import unittest
4 from twisted.internet import defer, reactor
5 from twisted.application import service
6 from allmydata import client, queen
7 from allmydata.util import idlib
8 from foolscap.eventual import flushEventualQueue
9 from twisted.python import log
10 from twisted.web.client import getPage
12 class SystemTest(unittest.TestCase):
13 # it takes a little while for a disconnected loopback TCP connection to
14 # be noticed by the other side. This is not directly visible to us, so we
15 # have to wait for time to pass rather than just waiting on a deferred.
16 # This is unfortunate, both because it arbitrarily slows down the test
17 # process, and because it is hard to predict what the minimum time
18 # necessary would be (on a slow or heavily loaded system, 100ms might not
20 DISCONNECT_DELAY = 0.1
23 self.sparent = service.MultiService()
24 self.sparent.startService()
26 log.msg("shutting down SystemTest services")
27 d = self.sparent.stopService()
28 d.addCallback(lambda res: flushEventualQueue())
31 reactor.callLater(self.DISCONNECT_DELAY, d1.callback, None)
36 def add_service(self, s):
37 s.setServiceParent(self.sparent)
40 def set_up_nodes(self, NUMCLIENTS=5):
41 self.numclients = NUMCLIENTS
42 if not os.path.isdir("queen"):
44 self.queen = self.add_service(queen.Queen(basedir="queen"))
45 d = self.queen.when_tub_ready()
46 d.addCallback(self._set_up_nodes_2)
49 def _set_up_nodes_2(self, res):
51 self.queen_furl = q.urls["introducer"]
52 self.vdrive_furl = q.urls["vdrive"]
54 for i in range(self.numclients):
55 basedir = "client%d" % i
56 if not os.path.isdir(basedir):
59 open(os.path.join(basedir, "webport"), "w").write("tcp:0:interface=127.0.0.1")
60 open(os.path.join(basedir, "introducer.furl"), "w").write(self.queen_furl)
61 open(os.path.join(basedir, "vdrive.furl"), "w").write(self.vdrive_furl)
62 c = self.add_service(client.Client(basedir=basedir))
63 self.clients.append(c)
65 d = self.wait_for_connections()
67 # now find out where the web port was
68 l = self.clients[0].getServiceNamed("webish").listener
69 port = l._port.getHost().port
70 self.webish_url = "http://localhost:%d/" % port
71 d.addCallback(_connected)
74 def add_extra_node(self, client_num):
75 # this node is *not* parented to our self.sparent, so we can shut it
76 # down separately from the rest, to exercise the connection-lost code
77 basedir = "client%d" % client_num
78 if not os.path.isdir(basedir):
80 open(os.path.join(basedir, "introducer.furl"), "w").write(self.queen_furl)
81 open(os.path.join(basedir, "vdrive.furl"), "w").write(self.vdrive_furl)
83 c = client.Client(basedir=basedir)
84 self.clients.append(c)
87 d = self.wait_for_connections()
88 d.addCallback(lambda res: c)
91 def wait_for_connections(self, ignored=None):
92 for c in self.clients:
93 if (not c.introducer_client or
94 len(list(c.get_all_peerids())) != self.numclients):
96 d.addCallback(self.wait_for_connections)
97 reactor.callLater(0.05, d.callback, None)
99 return defer.succeed(None)
101 def test_connections(self):
102 d = self.set_up_nodes()
103 self.extra_node = None
104 d.addCallback(lambda res: self.add_extra_node(5))
105 def _check(extra_node):
106 self.extra_node = extra_node
107 for c in self.clients:
108 self.failUnlessEqual(len(list(c.get_all_peerids())), 6)
109 d.addCallback(_check)
110 def _shutdown_extra_node(res):
112 d1 = self.extra_node.stopService()
113 d2 = defer.Deferred()
114 reactor.callLater(self.DISCONNECT_DELAY, d2.callback, res)
115 d1.addCallback(lambda ignored: d2)
118 d.addBoth(_shutdown_extra_node)
121 def test_upload_and_download(self):
122 DATA = "Some data to upload\n"
123 d = self.set_up_nodes()
126 u = self.clients[0].getServiceNamed("uploader")
127 d1 = u.upload_data(DATA)
129 d.addCallback(_do_upload)
130 def _upload_done(uri):
131 log.msg("upload finished: uri is %s" % (uri,))
132 dl = self.clients[1].getServiceNamed("downloader")
133 d1 = dl.download_to_data(uri)
135 d.addCallback(_upload_done)
136 def _download_done(data):
137 log.msg("download finished")
138 self.failUnlessEqual(data, DATA)
139 d.addCallback(_download_done)
142 def test_vdrive(self):
143 self.data = DATA = "Some data to publish to the virtual drive\n"
144 d = self.set_up_nodes()
145 def _do_publish(res):
146 log.msg("PUBLISHING")
147 v0 = self.clients[0].getServiceNamed("vdrive")
148 d1 = v0.make_directory("/", "subdir1")
149 d1.addCallback(lambda subdir1:
150 v0.put_file_by_data(subdir1, "mydata567", DATA))
152 d.addCallback(_do_publish)
153 def _publish_done(res):
154 log.msg("publish finished")
155 v1 = self.clients[1].getServiceNamed("vdrive")
156 d1 = v1.get_file_to_data("/subdir1/mydata567")
158 d.addCallback(_publish_done)
160 log.msg("get finished")
161 self.failUnlessEqual(data, DATA)
162 d.addCallback(_get_done)
163 d.addCallback(self._test_web)
166 def _test_web(self, res):
167 base = self.webish_url
169 def _got_welcome(page):
170 expected = "Connected Peers: <span>%d</span>" % (self.numclients)
171 self.failUnless(expected in page,
172 "I didn't see the right 'connected peers' message "
175 expected = "My nodeid: <span>%s</span>" % idlib.b2a(self.clients[0].nodeid)
176 self.failUnless(expected in page,
177 "I didn't see the right 'My nodeid' message "
179 d.addCallback(_got_welcome)
180 d.addCallback(lambda res: getPage(base + "vdrive/subdir1"))
181 def _got_subdir1(page):
182 # there ought to be an href for our file
183 self.failUnless(">mydata567</a>" in page)
184 d.addCallback(_got_subdir1)
185 d.addCallback(lambda res: getPage(base + "vdrive/subdir1/mydata567"))
187 self.failUnlessEqual(page, self.data)
188 d.addCallback(_got_data)