1 | # -*- coding: utf-8 -*- |
---|
2 | |
---|
3 | from twisted.trial import unittest |
---|
4 | |
---|
5 | from twisted.python import usage, runtime |
---|
6 | from twisted.internet import utils |
---|
7 | import os.path, re, sys |
---|
8 | from cStringIO import StringIO |
---|
9 | from allmydata.util import fileutil, pollmixin |
---|
10 | from allmydata.util.encodingutil import unicode_to_argv, unicode_to_output |
---|
11 | from allmydata.scripts import runner |
---|
12 | |
---|
13 | from allmydata.test import common_util |
---|
14 | import allmydata |
---|
15 | |
---|
16 | bintahoe = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(allmydata.__file__))), 'bin', 'tahoe') |
---|
17 | if sys.platform == "win32": # TODO: should this include cygwin? |
---|
18 | bintahoe += ".exe" |
---|
19 | |
---|
20 | |
---|
21 | class SkipMixin: |
---|
22 | def skip_if_cannot_run_bintahoe(self): |
---|
23 | if "cygwin" in sys.platform.lower(): |
---|
24 | raise unittest.SkipTest("We don't know how to make this test work on cygwin: spawnProcess seems to hang forever. We don't know if 'bin/tahoe start' can be run on cygwin.") |
---|
25 | if not os.path.exists(bintahoe): |
---|
26 | raise unittest.SkipTest("The bin/tahoe script isn't to be found in the expected location (%s), and I don't want to test a 'tahoe' executable that I find somewhere else, in case it isn't the right executable for this version of Tahoe. Perhaps running 'setup.py build' again will help." % (bintahoe,)) |
---|
27 | |
---|
28 | def skip_if_cannot_daemonize(self): |
---|
29 | self.skip_if_cannot_run_bintahoe() |
---|
30 | if runtime.platformType == "win32": |
---|
31 | # twistd on windows doesn't daemonize. cygwin should work normally. |
---|
32 | raise unittest.SkipTest("twistd does not fork under windows") |
---|
33 | |
---|
34 | |
---|
35 | class BinTahoe(common_util.SignalMixin, unittest.TestCase, SkipMixin): |
---|
36 | def test_path(self): |
---|
37 | self.skip_if_cannot_run_bintahoe() |
---|
38 | d = utils.getProcessOutputAndValue(bintahoe, args=["--version-and-path"], env=os.environ) |
---|
39 | def _cb(res): |
---|
40 | out, err, rc_or_sig = res |
---|
41 | self.failUnlessEqual(rc_or_sig, 0, str(res)) |
---|
42 | |
---|
43 | # Fail unless the package is *this* version *and* was loaded from *this* source directory. |
---|
44 | ad = os.path.dirname(os.path.dirname(os.path.realpath(allmydata.__file__))) |
---|
45 | required_ver_and_path = "%s: %s (%s)" % (allmydata.__appname__, allmydata.__version__, ad) |
---|
46 | self.failUnless(out.startswith(required_ver_and_path), |
---|
47 | str((out, err, rc_or_sig, required_ver_and_path))) |
---|
48 | d.addCallback(_cb) |
---|
49 | return d |
---|
50 | |
---|
51 | def test_unicode_arguments_and_output(self): |
---|
52 | self.skip_if_cannot_run_bintahoe() |
---|
53 | |
---|
54 | tricky = u"\u2621" |
---|
55 | try: |
---|
56 | tricky_arg = unicode_to_argv(tricky, mangle=True) |
---|
57 | tricky_out = unicode_to_output(tricky) |
---|
58 | except UnicodeEncodeError: |
---|
59 | raise unittest.SkipTest("A non-ASCII argument/output could not be encoded on this platform.") |
---|
60 | |
---|
61 | d = utils.getProcessOutputAndValue(bintahoe, args=[tricky_arg], env=os.environ) |
---|
62 | def _cb(res): |
---|
63 | out, err, rc_or_sig = res |
---|
64 | self.failUnlessEqual(rc_or_sig, 1, str((out, err, rc_or_sig))) |
---|
65 | self.failUnlessIn("Unknown command: "+tricky_out, out) |
---|
66 | d.addCallback(_cb) |
---|
67 | return d |
---|
68 | |
---|
69 | def test_version_no_noise(self): |
---|
70 | self.skip_if_cannot_run_bintahoe() |
---|
71 | import pkg_resources |
---|
72 | try: |
---|
73 | pkg_resources.require("Twisted>=9.0.0") |
---|
74 | except pkg_resources.VersionConflict: |
---|
75 | raise unittest.SkipTest("We pass this test only with Twisted >= v9.0.0") |
---|
76 | |
---|
77 | d = utils.getProcessOutputAndValue(bintahoe, args=["--version"], env=os.environ) |
---|
78 | def _cb(res): |
---|
79 | out, err, rc_or_sig = res |
---|
80 | self.failUnlessEqual(rc_or_sig, 0, str(res)) |
---|
81 | self.failUnless(out.startswith(allmydata.__appname__+':'), str(res)) |
---|
82 | self.failIfIn("DeprecationWarning", out, str(res)) |
---|
83 | self.failUnlessEqual(err, "", str(res)) |
---|
84 | d.addCallback(_cb) |
---|
85 | return d |
---|
86 | |
---|
87 | |
---|
88 | class CreateNode(unittest.TestCase): |
---|
89 | # exercise "tahoe create-node", create-introducer, |
---|
90 | # create-key-generator, and create-stats-gatherer, by calling the |
---|
91 | # corresponding code as a subroutine. |
---|
92 | |
---|
93 | def workdir(self, name): |
---|
94 | basedir = os.path.join("test_runner", "CreateNode", name) |
---|
95 | fileutil.make_dirs(basedir) |
---|
96 | return basedir |
---|
97 | |
---|
98 | def run_tahoe(self, argv): |
---|
99 | out,err = StringIO(), StringIO() |
---|
100 | rc = runner.runner(argv, stdout=out, stderr=err) |
---|
101 | return rc, out.getvalue(), err.getvalue() |
---|
102 | |
---|
103 | def do_create(self, command, basedir): |
---|
104 | c1 = os.path.join(basedir, command + "-c1") |
---|
105 | argv = ["--quiet", command, "--basedir", c1] |
---|
106 | rc, out, err = self.run_tahoe(argv) |
---|
107 | self.failUnlessEqual(err, "") |
---|
108 | self.failUnlessEqual(out, "") |
---|
109 | self.failUnlessEqual(rc, 0) |
---|
110 | self.failUnless(os.path.exists(c1)) |
---|
111 | self.failUnless(os.path.exists(os.path.join(c1, "tahoe-client.tac"))) |
---|
112 | |
---|
113 | # tahoe.cfg should exist, and should have storage enabled for |
---|
114 | # 'create-node', and disabled for 'create-client'. |
---|
115 | tahoe_cfg = os.path.join(c1, "tahoe.cfg") |
---|
116 | self.failUnless(os.path.exists(tahoe_cfg)) |
---|
117 | content = open(tahoe_cfg).read() |
---|
118 | if command == "create-client": |
---|
119 | self.failUnless("\n[storage]\nenabled = false\n" in content) |
---|
120 | else: |
---|
121 | self.failUnless("\n[storage]\nenabled = true\n" in content) |
---|
122 | |
---|
123 | # creating the client a second time should be rejected |
---|
124 | rc, out, err = self.run_tahoe(argv) |
---|
125 | self.failIfEqual(rc, 0, str((out, err, rc))) |
---|
126 | self.failUnlessEqual(out, "") |
---|
127 | self.failUnless("is not empty." in err) |
---|
128 | |
---|
129 | # Fail if there is a non-empty line that doesn't end with a |
---|
130 | # punctuation mark. |
---|
131 | for line in err.splitlines(): |
---|
132 | self.failIf(re.search("[\S][^\.!?]$", line), (line,)) |
---|
133 | |
---|
134 | # test that the non --basedir form works too |
---|
135 | c2 = os.path.join(basedir, command + "c2") |
---|
136 | argv = ["--quiet", command, c2] |
---|
137 | rc, out, err = self.run_tahoe(argv) |
---|
138 | self.failUnless(os.path.exists(c2)) |
---|
139 | self.failUnless(os.path.exists(os.path.join(c2, "tahoe-client.tac"))) |
---|
140 | |
---|
141 | # make sure it rejects too many arguments |
---|
142 | argv = [command, "basedir", "extraarg"] |
---|
143 | self.failUnlessRaises(usage.UsageError, |
---|
144 | runner.runner, argv, |
---|
145 | run_by_human=False) |
---|
146 | |
---|
147 | def test_node(self): |
---|
148 | basedir = self.workdir("test_node") |
---|
149 | self.do_create("create-node", basedir) |
---|
150 | |
---|
151 | def test_client(self): |
---|
152 | # create-client should behave like create-node --no-storage. |
---|
153 | basedir = self.workdir("test_client") |
---|
154 | self.do_create("create-client", basedir) |
---|
155 | |
---|
156 | def test_introducer(self): |
---|
157 | basedir = self.workdir("test_introducer") |
---|
158 | c1 = os.path.join(basedir, "c1") |
---|
159 | argv = ["--quiet", "create-introducer", "--basedir", c1] |
---|
160 | rc, out, err = self.run_tahoe(argv) |
---|
161 | self.failUnlessEqual(err, "", err) |
---|
162 | self.failUnlessEqual(out, "") |
---|
163 | self.failUnlessEqual(rc, 0) |
---|
164 | self.failUnless(os.path.exists(c1)) |
---|
165 | self.failUnless(os.path.exists(os.path.join(c1,"tahoe-introducer.tac"))) |
---|
166 | |
---|
167 | # creating the introducer a second time should be rejected |
---|
168 | rc, out, err = self.run_tahoe(argv) |
---|
169 | self.failIfEqual(rc, 0) |
---|
170 | self.failUnlessEqual(out, "") |
---|
171 | self.failUnless("is not empty" in err) |
---|
172 | |
---|
173 | # Fail if there is a non-empty line that doesn't end with a |
---|
174 | # punctuation mark. |
---|
175 | for line in err.splitlines(): |
---|
176 | self.failIf(re.search("[\S][^\.!?]$", line), (line,)) |
---|
177 | |
---|
178 | # test the non --basedir form |
---|
179 | c2 = os.path.join(basedir, "c2") |
---|
180 | argv = ["--quiet", "create-introducer", c2] |
---|
181 | rc, out, err = self.run_tahoe(argv) |
---|
182 | self.failUnlessEqual(err, "", err) |
---|
183 | self.failUnlessEqual(out, "") |
---|
184 | self.failUnlessEqual(rc, 0) |
---|
185 | self.failUnless(os.path.exists(c2)) |
---|
186 | self.failUnless(os.path.exists(os.path.join(c2,"tahoe-introducer.tac"))) |
---|
187 | |
---|
188 | # reject extra arguments |
---|
189 | argv = ["create-introducer", "basedir", "extraarg"] |
---|
190 | self.failUnlessRaises(usage.UsageError, |
---|
191 | runner.runner, argv, |
---|
192 | run_by_human=False) |
---|
193 | # and require basedir to be provided in some form |
---|
194 | argv = ["create-introducer"] |
---|
195 | self.failUnlessRaises(usage.UsageError, |
---|
196 | runner.runner, argv, |
---|
197 | run_by_human=False) |
---|
198 | |
---|
199 | def test_key_generator(self): |
---|
200 | basedir = self.workdir("test_key_generator") |
---|
201 | kg1 = os.path.join(basedir, "kg1") |
---|
202 | argv = ["--quiet", "create-key-generator", "--basedir", kg1] |
---|
203 | rc, out, err = self.run_tahoe(argv) |
---|
204 | self.failUnlessEqual(err, "") |
---|
205 | self.failUnlessEqual(out, "") |
---|
206 | self.failUnlessEqual(rc, 0) |
---|
207 | self.failUnless(os.path.exists(kg1)) |
---|
208 | self.failUnless(os.path.exists(os.path.join(kg1, "tahoe-key-generator.tac"))) |
---|
209 | |
---|
210 | # creating it a second time should be rejected |
---|
211 | rc, out, err = self.run_tahoe(argv) |
---|
212 | self.failIfEqual(rc, 0, str((out, err, rc))) |
---|
213 | self.failUnlessEqual(out, "") |
---|
214 | self.failUnlessIn("is not empty.", err) |
---|
215 | |
---|
216 | # make sure it rejects too many arguments |
---|
217 | argv = ["create-key-generator", "basedir", "extraarg"] |
---|
218 | self.failUnlessRaises(usage.UsageError, |
---|
219 | runner.runner, argv, |
---|
220 | run_by_human=False) |
---|
221 | |
---|
222 | # make sure it rejects a missing basedir specification |
---|
223 | argv = ["create-key-generator"] |
---|
224 | self.failUnlessRaises(usage.UsageError, |
---|
225 | runner.runner, argv, |
---|
226 | run_by_human=False) |
---|
227 | |
---|
228 | def test_stats_gatherer(self): |
---|
229 | basedir = self.workdir("test_stats_gatherer") |
---|
230 | sg1 = os.path.join(basedir, "sg1") |
---|
231 | argv = ["--quiet", "create-stats-gatherer", "--basedir", sg1] |
---|
232 | rc, out, err = self.run_tahoe(argv) |
---|
233 | self.failUnlessEqual(err, "") |
---|
234 | self.failUnlessEqual(out, "") |
---|
235 | self.failUnlessEqual(rc, 0) |
---|
236 | self.failUnless(os.path.exists(sg1)) |
---|
237 | self.failUnless(os.path.exists(os.path.join(sg1, "tahoe-stats-gatherer.tac"))) |
---|
238 | |
---|
239 | # creating it a second time should be rejected |
---|
240 | rc, out, err = self.run_tahoe(argv) |
---|
241 | self.failIfEqual(rc, 0, str((out, err, rc))) |
---|
242 | self.failUnlessEqual(out, "") |
---|
243 | self.failUnless("is not empty." in err) |
---|
244 | |
---|
245 | # test the non --basedir form |
---|
246 | kg2 = os.path.join(basedir, "kg2") |
---|
247 | argv = ["--quiet", "create-stats-gatherer", kg2] |
---|
248 | rc, out, err = self.run_tahoe(argv) |
---|
249 | self.failUnlessEqual(err, "", err) |
---|
250 | self.failUnlessEqual(out, "") |
---|
251 | self.failUnlessEqual(rc, 0) |
---|
252 | self.failUnless(os.path.exists(kg2)) |
---|
253 | self.failUnless(os.path.exists(os.path.join(kg2,"tahoe-stats-gatherer.tac"))) |
---|
254 | |
---|
255 | # make sure it rejects too many arguments |
---|
256 | argv = ["create-stats-gatherer", "basedir", "extraarg"] |
---|
257 | self.failUnlessRaises(usage.UsageError, |
---|
258 | runner.runner, argv, |
---|
259 | run_by_human=False) |
---|
260 | |
---|
261 | # make sure it rejects a missing basedir specification |
---|
262 | argv = ["create-stats-gatherer"] |
---|
263 | rc, out, err = self.run_tahoe(argv) |
---|
264 | self.failIfEqual(rc, 0, str((out, err, rc))) |
---|
265 | self.failUnlessEqual(out, "") |
---|
266 | self.failUnless("a basedir was not provided" in err) |
---|
267 | |
---|
268 | def test_subcommands(self): |
---|
269 | # no arguments should trigger a command listing, via UsageError |
---|
270 | self.failUnlessRaises(usage.UsageError, |
---|
271 | runner.runner, |
---|
272 | [], |
---|
273 | run_by_human=False) |
---|
274 | |
---|
275 | |
---|
276 | class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin, |
---|
277 | SkipMixin): |
---|
278 | # exercise "tahoe start", for both introducer, client node, and |
---|
279 | # key-generator, by spawning "tahoe start" as a subprocess. This doesn't |
---|
280 | # get us figleaf-based line-level coverage, but it does a better job of |
---|
281 | # confirming that the user can actually run "./bin/tahoe start" and |
---|
282 | # expect it to work. This verifies that bin/tahoe sets up PYTHONPATH and |
---|
283 | # the like correctly. |
---|
284 | |
---|
285 | # This doesn't work on cygwin (it hangs forever), so we skip this test |
---|
286 | # when we're on cygwin. It is likely that "tahoe start" itself doesn't |
---|
287 | # work on cygwin: twisted seems unable to provide a version of |
---|
288 | # spawnProcess which really works there. |
---|
289 | |
---|
290 | def workdir(self, name): |
---|
291 | basedir = os.path.join("test_runner", "RunNode", name) |
---|
292 | fileutil.make_dirs(basedir) |
---|
293 | return basedir |
---|
294 | |
---|
295 | def test_introducer(self): |
---|
296 | self.skip_if_cannot_daemonize() |
---|
297 | basedir = self.workdir("test_introducer") |
---|
298 | c1 = os.path.join(basedir, "c1") |
---|
299 | HOTLINE_FILE = os.path.join(c1, "suicide_prevention_hotline") |
---|
300 | TWISTD_PID_FILE = os.path.join(c1, "twistd.pid") |
---|
301 | INTRODUCER_FURL_FILE = os.path.join(c1, "introducer.furl") |
---|
302 | |
---|
303 | d = utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "create-introducer", "--basedir", c1], env=os.environ) |
---|
304 | def _cb(res): |
---|
305 | out, err, rc_or_sig = res |
---|
306 | self.failUnlessEqual(rc_or_sig, 0) |
---|
307 | # by writing this file, we get ten seconds before the node will |
---|
308 | # exit. This insures that even if the test fails (and the 'stop' |
---|
309 | # command doesn't work), the client should still terminate. |
---|
310 | open(HOTLINE_FILE, "w").write("") |
---|
311 | # now it's safe to start the node |
---|
312 | d.addCallback(_cb) |
---|
313 | |
---|
314 | def _then_start_the_node(res): |
---|
315 | return utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "start", c1], env=os.environ) |
---|
316 | d.addCallback(_then_start_the_node) |
---|
317 | |
---|
318 | def _cb2(res): |
---|
319 | out, err, rc_or_sig = res |
---|
320 | |
---|
321 | open(HOTLINE_FILE, "w").write("") |
---|
322 | errstr = "rc=%d, OUT: '%s', ERR: '%s'" % (rc_or_sig, out, err) |
---|
323 | self.failUnlessEqual(rc_or_sig, 0, errstr) |
---|
324 | self.failUnlessEqual(out, "", errstr) |
---|
325 | # self.failUnlessEqual(err, "", errstr) # See test_client_no_noise -- for now we ignore noise. |
---|
326 | |
---|
327 | # the parent (twistd) has exited. However, twistd writes the pid |
---|
328 | # from the child, not the parent, so we can't expect twistd.pid |
---|
329 | # to exist quite yet. |
---|
330 | |
---|
331 | # the node is running, but it might not have made it past the |
---|
332 | # first reactor turn yet, and if we kill it too early, it won't |
---|
333 | # remove the twistd.pid file. So wait until it does something |
---|
334 | # that we know it won't do until after the first turn. |
---|
335 | d.addCallback(_cb2) |
---|
336 | |
---|
337 | def _node_has_started(): |
---|
338 | return os.path.exists(INTRODUCER_FURL_FILE) |
---|
339 | d.addCallback(lambda res: self.poll(_node_has_started)) |
---|
340 | |
---|
341 | def _started(res): |
---|
342 | open(HOTLINE_FILE, "w").write("") |
---|
343 | self.failUnless(os.path.exists(TWISTD_PID_FILE)) |
---|
344 | # rm this so we can detect when the second incarnation is ready |
---|
345 | os.unlink(INTRODUCER_FURL_FILE) |
---|
346 | return utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "restart", c1], env=os.environ) |
---|
347 | d.addCallback(_started) |
---|
348 | |
---|
349 | def _then(res): |
---|
350 | out, err, rc_or_sig = res |
---|
351 | open(HOTLINE_FILE, "w").write("") |
---|
352 | errstr = "rc=%d, OUT: '%s', ERR: '%s'" % (rc_or_sig, out, err) |
---|
353 | self.failUnlessEqual(rc_or_sig, 0, errstr) |
---|
354 | self.failUnlessEqual(out, "", errstr) |
---|
355 | # self.failUnlessEqual(err, "", errstr) # See test_client_no_noise -- for now we ignore noise. |
---|
356 | d.addCallback(_then) |
---|
357 | |
---|
358 | # again, the second incarnation of the node might not be ready yet, |
---|
359 | # so poll until it is |
---|
360 | d.addCallback(lambda res: self.poll(_node_has_started)) |
---|
361 | |
---|
362 | # now we can kill it. TODO: On a slow machine, the node might kill |
---|
363 | # itself before we get a chance too, especially if spawning the |
---|
364 | # 'tahoe stop' command takes a while. |
---|
365 | def _stop(res): |
---|
366 | open(HOTLINE_FILE, "w").write("") |
---|
367 | self.failUnless(os.path.exists(TWISTD_PID_FILE)) |
---|
368 | |
---|
369 | return utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "stop", c1], env=os.environ) |
---|
370 | d.addCallback(_stop) |
---|
371 | |
---|
372 | def _after_stopping(res): |
---|
373 | out, err, rc_or_sig = res |
---|
374 | open(HOTLINE_FILE, "w").write("") |
---|
375 | # the parent has exited by now |
---|
376 | errstr = "rc=%d, OUT: '%s', ERR: '%s'" % (rc_or_sig, out, err) |
---|
377 | self.failUnlessEqual(rc_or_sig, 0, errstr) |
---|
378 | self.failUnlessEqual(out, "", errstr) |
---|
379 | # self.failUnlessEqual(err, "", errstr) # See test_client_no_noise -- for now we ignore noise. |
---|
380 | # the parent was supposed to poll and wait until it sees |
---|
381 | # twistd.pid go away before it exits, so twistd.pid should be |
---|
382 | # gone by now. |
---|
383 | self.failIf(os.path.exists(TWISTD_PID_FILE)) |
---|
384 | d.addCallback(_after_stopping) |
---|
385 | |
---|
386 | def _remove_hotline(res): |
---|
387 | os.unlink(HOTLINE_FILE) |
---|
388 | return res |
---|
389 | d.addBoth(_remove_hotline) |
---|
390 | return d |
---|
391 | test_introducer.timeout = 480 # This hit the 120-second timeout on "François Lenny-armv5tel", then it hit a 240-second timeout on our feisty2.5 buildslave: http://allmydata.org/buildbot/builders/feisty2.5/builds/2381/steps/test/logs/test.log |
---|
392 | |
---|
393 | def test_client_no_noise(self): |
---|
394 | self.skip_if_cannot_daemonize() |
---|
395 | import pkg_resources |
---|
396 | try: |
---|
397 | pkg_resources.require("Twisted>=9.0.0") |
---|
398 | except pkg_resources.VersionConflict: |
---|
399 | raise unittest.SkipTest("We pass this test only with Twisted >= v9.0.0") |
---|
400 | basedir = self.workdir("test_client_no_noise") |
---|
401 | c1 = os.path.join(basedir, "c1") |
---|
402 | HOTLINE_FILE = os.path.join(c1, "suicide_prevention_hotline") |
---|
403 | TWISTD_PID_FILE = os.path.join(c1, "twistd.pid") |
---|
404 | PORTNUMFILE = os.path.join(c1, "client.port") |
---|
405 | |
---|
406 | d = utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "create-client", "--basedir", c1, "--webport", "0"], env=os.environ) |
---|
407 | def _cb(res): |
---|
408 | out, err, rc_or_sig = res |
---|
409 | errstr = "cc=%d, OUT: '%s', ERR: '%s'" % (rc_or_sig, out, err) |
---|
410 | assert rc_or_sig == 0, errstr |
---|
411 | self.failUnlessEqual(rc_or_sig, 0) |
---|
412 | # By writing this file, we get forty seconds before the client will exit. This insures |
---|
413 | # that even if the 'stop' command doesn't work (and the test fails), the client should |
---|
414 | # still terminate. |
---|
415 | open(HOTLINE_FILE, "w").write("") |
---|
416 | open(os.path.join(c1, "introducer.furl"), "w").write("pb://xrndsskn2zuuian5ltnxrte7lnuqdrkz@127.0.0.1:55617/introducer\n") |
---|
417 | # now it's safe to start the node |
---|
418 | d.addCallback(_cb) |
---|
419 | |
---|
420 | def _start(res): |
---|
421 | return utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "start", c1], env=os.environ) |
---|
422 | d.addCallback(_start) |
---|
423 | |
---|
424 | def _cb2(res): |
---|
425 | out, err, rc_or_sig = res |
---|
426 | errstr = "cc=%d, OUT: '%s', ERR: '%s'" % (rc_or_sig, out, err) |
---|
427 | open(HOTLINE_FILE, "w").write("") |
---|
428 | self.failUnlessEqual(rc_or_sig, 0, errstr) |
---|
429 | self.failUnlessEqual(out, "", errstr) # If you emit noise, you fail this test. |
---|
430 | self.failUnlessEqual(err, "", errstr) |
---|
431 | |
---|
432 | # the parent (twistd) has exited. However, twistd writes the pid |
---|
433 | # from the child, not the parent, so we can't expect twistd.pid |
---|
434 | # to exist quite yet. |
---|
435 | |
---|
436 | # the node is running, but it might not have made it past the |
---|
437 | # first reactor turn yet, and if we kill it too early, it won't |
---|
438 | # remove the twistd.pid file. So wait until it does something |
---|
439 | # that we know it won't do until after the first turn. |
---|
440 | d.addCallback(_cb2) |
---|
441 | |
---|
442 | def _node_has_started(): |
---|
443 | return os.path.exists(PORTNUMFILE) |
---|
444 | d.addCallback(lambda res: self.poll(_node_has_started)) |
---|
445 | |
---|
446 | # now we can kill it. TODO: On a slow machine, the node might kill |
---|
447 | # itself before we get a chance too, especially if spawning the |
---|
448 | # 'tahoe stop' command takes a while. |
---|
449 | def _stop(res): |
---|
450 | self.failUnless(os.path.exists(TWISTD_PID_FILE), (TWISTD_PID_FILE, os.listdir(os.path.dirname(TWISTD_PID_FILE)))) |
---|
451 | return utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "stop", c1], env=os.environ) |
---|
452 | d.addCallback(_stop) |
---|
453 | return d |
---|
454 | |
---|
455 | def test_client(self): |
---|
456 | self.skip_if_cannot_daemonize() |
---|
457 | basedir = self.workdir("test_client") |
---|
458 | c1 = os.path.join(basedir, "c1") |
---|
459 | HOTLINE_FILE = os.path.join(c1, "suicide_prevention_hotline") |
---|
460 | TWISTD_PID_FILE = os.path.join(c1, "twistd.pid") |
---|
461 | PORTNUMFILE = os.path.join(c1, "client.port") |
---|
462 | |
---|
463 | d = utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "create-node", "--basedir", c1, "--webport", "0"], env=os.environ) |
---|
464 | def _cb(res): |
---|
465 | out, err, rc_or_sig = res |
---|
466 | self.failUnlessEqual(rc_or_sig, 0) |
---|
467 | # By writing this file, we get sixty seconds before the client will exit. This insures |
---|
468 | # that even if the 'stop' command doesn't work (and the test fails), the client should |
---|
469 | # still terminate. |
---|
470 | open(HOTLINE_FILE, "w").write("") |
---|
471 | open(os.path.join(c1, "introducer.furl"), "w").write("pb://xrndsskn2zuuian5ltnxrte7lnuqdrkz@127.0.0.1:55617/introducer\n") |
---|
472 | # now it's safe to start the node |
---|
473 | d.addCallback(_cb) |
---|
474 | |
---|
475 | def _start(res): |
---|
476 | return utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "start", c1], env=os.environ) |
---|
477 | d.addCallback(_start) |
---|
478 | |
---|
479 | def _cb2(res): |
---|
480 | out, err, rc_or_sig = res |
---|
481 | open(HOTLINE_FILE, "w").write("") |
---|
482 | errstr = "rc=%d, OUT: '%s', ERR: '%s'" % (rc_or_sig, out, err) |
---|
483 | self.failUnlessEqual(rc_or_sig, 0, errstr) |
---|
484 | self.failUnlessEqual(out, "", errstr) |
---|
485 | # self.failUnlessEqual(err, "", errstr) # See test_client_no_noise -- for now we ignore noise. |
---|
486 | |
---|
487 | # the parent (twistd) has exited. However, twistd writes the pid |
---|
488 | # from the child, not the parent, so we can't expect twistd.pid |
---|
489 | # to exist quite yet. |
---|
490 | |
---|
491 | # the node is running, but it might not have made it past the |
---|
492 | # first reactor turn yet, and if we kill it too early, it won't |
---|
493 | # remove the twistd.pid file. So wait until it does something |
---|
494 | # that we know it won't do until after the first turn. |
---|
495 | d.addCallback(_cb2) |
---|
496 | |
---|
497 | def _node_has_started(): |
---|
498 | return os.path.exists(PORTNUMFILE) |
---|
499 | d.addCallback(lambda res: self.poll(_node_has_started)) |
---|
500 | |
---|
501 | def _started(res): |
---|
502 | open(HOTLINE_FILE, "w").write("") |
---|
503 | self.failUnless(os.path.exists(TWISTD_PID_FILE)) |
---|
504 | # rm this so we can detect when the second incarnation is ready |
---|
505 | os.unlink(PORTNUMFILE) |
---|
506 | |
---|
507 | return utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "restart", c1], env=os.environ) |
---|
508 | d.addCallback(_started) |
---|
509 | |
---|
510 | def _cb3(res): |
---|
511 | out, err, rc_or_sig = res |
---|
512 | |
---|
513 | open(HOTLINE_FILE, "w").write("") |
---|
514 | errstr = "rc=%d, OUT: '%s', ERR: '%s'" % (rc_or_sig, out, err) |
---|
515 | self.failUnlessEqual(rc_or_sig, 0, errstr) |
---|
516 | self.failUnlessEqual(out, "", errstr) |
---|
517 | # self.failUnlessEqual(err, "", errstr) # See test_client_no_noise -- for now we ignore noise. |
---|
518 | d.addCallback(_cb3) |
---|
519 | |
---|
520 | # again, the second incarnation of the node might not be ready yet, |
---|
521 | # so poll until it is |
---|
522 | d.addCallback(lambda res: self.poll(_node_has_started)) |
---|
523 | |
---|
524 | # now we can kill it. TODO: On a slow machine, the node might kill |
---|
525 | # itself before we get a chance too, especially if spawning the |
---|
526 | # 'tahoe stop' command takes a while. |
---|
527 | def _stop(res): |
---|
528 | open(HOTLINE_FILE, "w").write("") |
---|
529 | self.failUnless(os.path.exists(TWISTD_PID_FILE), (TWISTD_PID_FILE, os.listdir(os.path.dirname(TWISTD_PID_FILE)))) |
---|
530 | return utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "stop", c1], env=os.environ) |
---|
531 | d.addCallback(_stop) |
---|
532 | |
---|
533 | def _cb4(res): |
---|
534 | out, err, rc_or_sig = res |
---|
535 | |
---|
536 | open(HOTLINE_FILE, "w").write("") |
---|
537 | # the parent has exited by now |
---|
538 | errstr = "rc=%d, OUT: '%s', ERR: '%s'" % (rc_or_sig, out, err) |
---|
539 | self.failUnlessEqual(rc_or_sig, 0, errstr) |
---|
540 | self.failUnlessEqual(out, "", errstr) |
---|
541 | # self.failUnlessEqual(err, "", errstr) # See test_client_no_noise -- for now we ignore noise. |
---|
542 | # the parent was supposed to poll and wait until it sees |
---|
543 | # twistd.pid go away before it exits, so twistd.pid should be |
---|
544 | # gone by now. |
---|
545 | self.failIf(os.path.exists(TWISTD_PID_FILE)) |
---|
546 | d.addCallback(_cb4) |
---|
547 | def _remove_hotline(res): |
---|
548 | os.unlink(HOTLINE_FILE) |
---|
549 | return res |
---|
550 | d.addBoth(_remove_hotline) |
---|
551 | return d |
---|
552 | |
---|
553 | def test_baddir(self): |
---|
554 | self.skip_if_cannot_daemonize() |
---|
555 | basedir = self.workdir("test_baddir") |
---|
556 | fileutil.make_dirs(basedir) |
---|
557 | |
---|
558 | d = utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "start", "--basedir", basedir], env=os.environ) |
---|
559 | def _cb(res): |
---|
560 | out, err, rc_or_sig = res |
---|
561 | self.failUnlessEqual(rc_or_sig, 1) |
---|
562 | self.failUnless("does not look like a node directory" in err, err) |
---|
563 | d.addCallback(_cb) |
---|
564 | |
---|
565 | def _then_stop_it(res): |
---|
566 | return utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "stop", "--basedir", basedir], env=os.environ) |
---|
567 | d.addCallback(_then_stop_it) |
---|
568 | |
---|
569 | def _cb2(res): |
---|
570 | out, err, rc_or_sig = res |
---|
571 | self.failUnlessEqual(rc_or_sig, 2) |
---|
572 | self.failUnless("does not look like a running node directory" in err) |
---|
573 | d.addCallback(_cb2) |
---|
574 | |
---|
575 | def _then_start_in_bogus_basedir(res): |
---|
576 | not_a_dir = os.path.join(basedir, "bogus") |
---|
577 | return utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "start", "--basedir", not_a_dir], env=os.environ) |
---|
578 | d.addCallback(_then_start_in_bogus_basedir) |
---|
579 | |
---|
580 | def _cb3(res): |
---|
581 | out, err, rc_or_sig = res |
---|
582 | self.failUnlessEqual(rc_or_sig, 1) |
---|
583 | self.failUnless("does not look like a directory at all" in err, err) |
---|
584 | d.addCallback(_cb3) |
---|
585 | return d |
---|
586 | |
---|
587 | def test_keygen(self): |
---|
588 | self.skip_if_cannot_daemonize() |
---|
589 | basedir = self.workdir("test_keygen") |
---|
590 | c1 = os.path.join(basedir, "c1") |
---|
591 | TWISTD_PID_FILE = os.path.join(c1, "twistd.pid") |
---|
592 | KEYGEN_FURL_FILE = os.path.join(c1, "key_generator.furl") |
---|
593 | |
---|
594 | d = utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "create-key-generator", "--basedir", c1], env=os.environ) |
---|
595 | def _cb(res): |
---|
596 | out, err, rc_or_sig = res |
---|
597 | self.failUnlessEqual(rc_or_sig, 0) |
---|
598 | d.addCallback(_cb) |
---|
599 | |
---|
600 | def _start(res): |
---|
601 | return utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "start", c1], env=os.environ) |
---|
602 | d.addCallback(_start) |
---|
603 | |
---|
604 | def _cb2(res): |
---|
605 | out, err, rc_or_sig = res |
---|
606 | errstr = "rc=%d, OUT: '%s', ERR: '%s'" % (rc_or_sig, out, err) |
---|
607 | self.failUnlessEqual(rc_or_sig, 0, errstr) |
---|
608 | self.failUnlessEqual(out, "", errstr) |
---|
609 | # self.failUnlessEqual(err, "", errstr) # See test_client_no_noise -- for now we ignore noise. |
---|
610 | |
---|
611 | # the parent (twistd) has exited. However, twistd writes the pid |
---|
612 | # from the child, not the parent, so we can't expect twistd.pid |
---|
613 | # to exist quite yet. |
---|
614 | |
---|
615 | # the node is running, but it might not have made it past the |
---|
616 | # first reactor turn yet, and if we kill it too early, it won't |
---|
617 | # remove the twistd.pid file. So wait until it does something |
---|
618 | # that we know it won't do until after the first turn. |
---|
619 | d.addCallback(_cb2) |
---|
620 | |
---|
621 | def _node_has_started(): |
---|
622 | return os.path.exists(KEYGEN_FURL_FILE) |
---|
623 | d.addCallback(lambda res: self.poll(_node_has_started)) |
---|
624 | |
---|
625 | def _started(res): |
---|
626 | self.failUnless(os.path.exists(TWISTD_PID_FILE)) |
---|
627 | # rm this so we can detect when the second incarnation is ready |
---|
628 | os.unlink(KEYGEN_FURL_FILE) |
---|
629 | return utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "restart", c1], env=os.environ) |
---|
630 | d.addCallback(_started) |
---|
631 | |
---|
632 | def _cb3(res): |
---|
633 | out, err, rc_or_sig = res |
---|
634 | errstr = "rc=%d, OUT: '%s', ERR: '%s'" % (rc_or_sig, out, err) |
---|
635 | self.failUnlessEqual(rc_or_sig, 0, errstr) |
---|
636 | self.failUnlessEqual(out, "", errstr) |
---|
637 | # self.failUnlessEqual(err, "", errstr) # See test_client_no_noise -- for now we ignore noise. |
---|
638 | d.addCallback(_cb3) |
---|
639 | |
---|
640 | # again, the second incarnation of the node might not be ready yet, |
---|
641 | # so poll until it is |
---|
642 | d.addCallback(lambda res: self.poll(_node_has_started)) |
---|
643 | |
---|
644 | # now we can kill it. TODO: On a slow machine, the node might kill |
---|
645 | # itself before we get a chance too, especially if spawning the |
---|
646 | # 'tahoe stop' command takes a while. |
---|
647 | def _stop(res): |
---|
648 | self.failUnless(os.path.exists(TWISTD_PID_FILE)) |
---|
649 | return utils.getProcessOutputAndValue(bintahoe, args=["--quiet", "stop", c1], env=os.environ) |
---|
650 | d.addCallback(_stop) |
---|
651 | |
---|
652 | def _cb4(res): |
---|
653 | out, err, rc_or_sig = res |
---|
654 | # the parent has exited by now |
---|
655 | errstr = "rc=%d, OUT: '%s', ERR: '%s'" % (rc_or_sig, out, err) |
---|
656 | self.failUnlessEqual(rc_or_sig, 0, errstr) |
---|
657 | self.failUnlessEqual(out, "", errstr) |
---|
658 | # self.failUnlessEqual(err, "", errstr) # See test_client_no_noise -- for now we ignore noise. |
---|
659 | # the parent was supposed to poll and wait until it sees |
---|
660 | # twistd.pid go away before it exits, so twistd.pid should be |
---|
661 | # gone by now. |
---|
662 | self.failIf(os.path.exists(TWISTD_PID_FILE)) |
---|
663 | d.addCallback(_cb4) |
---|
664 | return d |
---|