~ubuntu-branches/ubuntu/karmic/tahoe-lafs/karmic

« back to all changes in this revision

Viewing changes to src/allmydata/test/test_runner.py

  • Committer: Bazaar Package Importer
  • Author(s): Zooko O'Whielacronx (Hacker)
  • Date: 2009-09-24 00:00:05 UTC
  • Revision ID: james.westby@ubuntu.com-20090924000005-ixe2n4yngmk49ysz
Tags: upstream-1.5.0
ImportĀ upstreamĀ versionĀ 1.5.0

Show diffs side-by-side

added added

removed removed

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