Merge branch '2826-async-dispatch'

This commit is contained in:
Brian Warner 2016-09-09 16:27:10 -07:00
commit ecc2080372
22 changed files with 256 additions and 338 deletions

View File

@ -1,6 +1,4 @@
import os
import os, sys
from allmydata.scripts.common import BasedirOptions, NoDefaultBasedirOptions from allmydata.scripts.common import BasedirOptions, NoDefaultBasedirOptions
from allmydata.scripts.default_nodedir import _default_nodedir from allmydata.scripts.default_nodedir import _default_nodedir
from allmydata.util.assertutil import precondition from allmydata.util.assertutil import precondition
@ -129,7 +127,9 @@ def write_client_config(c, config):
c.write("enabled = false\n") c.write("enabled = false\n")
c.write("\n") c.write("\n")
def create_node(config, out=sys.stdout, err=sys.stderr): def create_node(config):
out = config.stdout
err = config.stderr
basedir = config['basedir'] basedir = config['basedir']
# This should always be called with an absolute Unicode basedir. # This should always be called with an absolute Unicode basedir.
precondition(isinstance(basedir, unicode), basedir) precondition(isinstance(basedir, unicode), basedir)
@ -162,12 +162,14 @@ def create_node(config, out=sys.stdout, err=sys.stderr):
print >>out, " Please set [node]nickname= in tahoe.cfg" print >>out, " Please set [node]nickname= in tahoe.cfg"
return 0 return 0
def create_client(config, out=sys.stdout, err=sys.stderr): def create_client(config):
config['no-storage'] = True config['no-storage'] = True
return create_node(config, out=out, err=err) return create_node(config)
def create_introducer(config, out=sys.stdout, err=sys.stderr): def create_introducer(config):
out = config.stdout
err = config.stderr
basedir = config['basedir'] basedir = config['basedir']
# This should always be called with an absolute Unicode basedir. # This should always be called with an absolute Unicode basedir.
precondition(isinstance(basedir, unicode), basedir) precondition(isinstance(basedir, unicode), basedir)

View File

@ -3,6 +3,7 @@ import os, sys
from cStringIO import StringIO from cStringIO import StringIO
from twisted.python import usage from twisted.python import usage
from twisted.internet import defer, task, threads
from allmydata.scripts.common import get_default_nodedir from allmydata.scripts.common import get_default_nodedir
from allmydata.scripts import debug, create_node, startstop_node, cli, \ from allmydata.scripts import debug, create_node, startstop_node, cli, \
@ -89,32 +90,17 @@ create_dispatch = {}
for module in (create_node, stats_gatherer): for module in (create_node, stats_gatherer):
create_dispatch.update(module.dispatch) create_dispatch.update(module.dispatch)
def runner(argv, def parse_options(argv, config=None):
run_by_human=True, if not config:
stdin=None, stdout=None, stderr=None, config = Options()
install_node_control=True, additional_commands=None): config.parseOptions(argv) # may raise usage.error
return config
assert sys.version_info < (3,), ur"Tahoe-LAFS does not run under Python 3. Please use Python 2.7.x."
stdin = stdin or sys.stdin
stdout = stdout or sys.stdout
stderr = stderr or sys.stderr
def parse_or_exit_with_explanation(argv, stdout=sys.stdout):
config = Options() config = Options()
if install_node_control:
config.subCommands.extend(startstop_node.subCommands)
ac_dispatch = {}
if additional_commands:
for ac in additional_commands:
config.subCommands.extend(ac.subCommands)
ac_dispatch.update(ac.dispatch)
try: try:
config.parseOptions(argv) parse_options(argv, config=config)
except usage.error, e: except usage.error, e:
if not run_by_human:
raise
c = config c = config
while hasattr(c, 'subOptions'): while hasattr(c, 'subOptions'):
c = c.subOptions c = c.subOptions
@ -124,51 +110,68 @@ def runner(argv,
except Exception: except Exception:
msg = repr(e) msg = repr(e)
print >>stdout, "%s: %s\n" % (sys.argv[0], quote_output(msg, quotemarks=False)) print >>stdout, "%s: %s\n" % (sys.argv[0], quote_output(msg, quotemarks=False))
return 1 sys.exit(1)
return config
def dispatch(config,
stdin=sys.stdin, stdout=sys.stdout, stderr=sys.stderr):
command = config.subCommand command = config.subCommand
so = config.subOptions so = config.subOptions
if config['quiet']: if config['quiet']:
stdout = StringIO() stdout = StringIO()
so.stdout = stdout so.stdout = stdout
so.stderr = stderr so.stderr = stderr
so.stdin = stdin so.stdin = stdin
if command in create_dispatch: if command in create_dispatch:
rc = create_dispatch[command](so, stdout, stderr) f = create_dispatch[command]
elif command in startstop_node.dispatch: elif command in startstop_node.dispatch:
rc = startstop_node.dispatch[command](so, stdout, stderr) f = startstop_node.dispatch[command]
elif command in debug.dispatch: elif command in debug.dispatch:
rc = debug.dispatch[command](so) f = debug.dispatch[command]
elif command in admin.dispatch: elif command in admin.dispatch:
rc = admin.dispatch[command](so) f = admin.dispatch[command]
elif command in cli.dispatch: elif command in cli.dispatch:
rc = cli.dispatch[command](so) # these are blocking, and must be run in a thread
f0 = cli.dispatch[command]
f = lambda so: threads.deferToThread(f0, so)
elif command in magic_folder_cli.dispatch: elif command in magic_folder_cli.dispatch:
rc = magic_folder_cli.dispatch[command](so) # same
elif command in ac_dispatch: f0 = magic_folder_cli.dispatch[command]
rc = ac_dispatch[command](so, stdout, stderr) f = lambda so: threads.deferToThread(f0, so)
else: else:
raise usage.UsageError() raise usage.UsageError()
return rc d = defer.maybeDeferred(f, so)
# the calling convention for CLI dispatch functions is that they either:
# 1: succeed and return rc=0
# 2: print explanation to stderr and return rc!=0
# 3: raise an exception that should just be printed normally
# 4: return a Deferred that does 1 or 2 or 3
def _raise_sys_exit(rc):
sys.exit(rc)
d.addCallback(_raise_sys_exit)
return d
def run():
assert sys.version_info < (3,), ur"Tahoe-LAFS does not run under Python 3. Please use Python 2.7.x."
def run(install_node_control=True): if sys.platform == "win32":
try: from allmydata.windows.fixups import initialize
if sys.platform == "win32": initialize()
from allmydata.windows.fixups import initialize d = defer.maybeDeferred(parse_or_exit_with_explanation, sys.argv[1:])
initialize() d.addCallback(dispatch)
def _show_exception(f):
rc = runner(sys.argv[1:], install_node_control=install_node_control) # when task.react() notices a non-SystemExit exception, it does
except Exception: # log.err() with the failure and then exits with rc=1. We want this
import traceback # to actually print the exception to stderr, like it would do if we
traceback.print_exc() # weren't using react().
rc = 1 if f.check(SystemExit):
return f # dispatch function handled it
sys.exit(rc) f.printTraceback(file=sys.stderr)
sys.exit(1)
d.addErrback(_show_exception)
task.react(lambda _reactor: d) # doesn't return: calls sys.exit(rc)
if __name__ == "__main__": if __name__ == "__main__":
run() run()

View File

@ -99,7 +99,9 @@ def identify_node_type(basedir):
return t return t
return None return None
def start(config, out=sys.stdout, err=sys.stderr): def start(config):
out = config.stdout
err = config.stderr
basedir = config['basedir'] basedir = config['basedir']
quoted_basedir = quote_local_unicode_path(basedir) quoted_basedir = quote_local_unicode_path(basedir)
print >>out, "STARTING", quoted_basedir print >>out, "STARTING", quoted_basedir
@ -169,7 +171,9 @@ def start(config, out=sys.stdout, err=sys.stderr):
# we should only reach here if --nodaemon or equivalent was used # we should only reach here if --nodaemon or equivalent was used
return 0 return 0
def stop(config, out=sys.stdout, err=sys.stderr): def stop(config):
out = config.stdout
err = config.stderr
basedir = config['basedir'] basedir = config['basedir']
quoted_basedir = quote_local_unicode_path(basedir) quoted_basedir = quote_local_unicode_path(basedir)
print >>out, "STOPPING", quoted_basedir print >>out, "STOPPING", quoted_basedir
@ -227,23 +231,24 @@ def stop(config, out=sys.stdout, err=sys.stderr):
# we define rc=1 to mean "I think something is still running, sorry" # we define rc=1 to mean "I think something is still running, sorry"
return 1 return 1
def restart(config, stdout, stderr): def restart(config):
rc = stop(config, stdout, stderr) stderr = config.stderr
rc = stop(config)
if rc == 2: if rc == 2:
print >>stderr, "ignoring couldn't-stop" print >>stderr, "ignoring couldn't-stop"
rc = 0 rc = 0
if rc: if rc:
print >>stderr, "not restarting" print >>stderr, "not restarting"
return rc return rc
return start(config, stdout, stderr) return start(config)
def run(config, stdout, stderr): def run(config):
config.twistd_args = config.twistd_args + ("--nodaemon",) config.twistd_args = config.twistd_args + ("--nodaemon",)
# Previously we would do the equivalent of adding ("--logfile", # Previously we would do the equivalent of adding ("--logfile",
# "tahoesvc.log"), but that redirects stdout/stderr which is often # "tahoesvc.log"), but that redirects stdout/stderr which is often
# unhelpful, and the user can add that option explicitly if they want. # unhelpful, and the user can add that option explicitly if they want.
return start(config, stdout, stderr) return start(config)
subCommands = [ subCommands = [

View File

@ -1,4 +1,4 @@
import os, sys import os
from twisted.python import usage from twisted.python import usage
from allmydata.scripts.common import NoDefaultBasedirOptions from allmydata.scripts.common import NoDefaultBasedirOptions
from allmydata.scripts.create_node import write_tac from allmydata.scripts.create_node import write_tac
@ -56,7 +56,8 @@ class CreateStatsGathererOptions(NoDefaultBasedirOptions):
""" """
def create_stats_gatherer(config, out=sys.stdout, err=sys.stderr): def create_stats_gatherer(config):
err = config.stderr
basedir = config['basedir'] basedir = config['basedir']
# This should always be called with an absolute Unicode basedir. # This should always be called with an absolute Unicode basedir.
precondition(isinstance(basedir, unicode), basedir) precondition(isinstance(basedir, unicode), basedir)

View File

@ -0,0 +1,19 @@
from ...util.encodingutil import unicode_to_argv
from ...scripts import runner
from ..common_util import ReallyEqualMixin, run_cli
def parse_options(basedir, command, args):
o = runner.Options()
o.parseOptions(["--node-directory", basedir, command] + args)
while hasattr(o, "subOptions"):
o = o.subOptions
return o
class CLITestMixin(ReallyEqualMixin):
def do_cli(self, verb, *args, **kwargs):
# client_num is used to execute client CLI commands on a specific
# client.
client_num = kwargs.get("client_num", 0)
client_dir = unicode_to_argv(self.get_clientdir(i=client_num))
nodeargs = [ "--node-directory", client_dir ]
return run_cli(verb, nodeargs=nodeargs, *args, **kwargs)

View File

@ -14,7 +14,7 @@ from allmydata.util.namespace import Namespace
from allmydata.scripts import cli, backupdb from allmydata.scripts import cli, backupdb
from ..common_util import StallMixin from ..common_util import StallMixin
from ..no_network import GridTestMixin from ..no_network import GridTestMixin
from .test_cli import CLITestMixin, parse_options from .common import CLITestMixin, parse_options
timeout = 480 # deep_check takes 360s on Zandr's linksys box, others take > 240s timeout = 480 # deep_check takes 360s on Zandr's linksys box, others take > 240s

View File

@ -10,7 +10,7 @@ from allmydata.mutable.publish import MutableData
from allmydata.immutable import upload from allmydata.immutable import upload
from allmydata.scripts import debug from allmydata.scripts import debug
from ..no_network import GridTestMixin from ..no_network import GridTestMixin
from .test_cli import CLITestMixin from .common import CLITestMixin
timeout = 480 # deep_check takes 360s on Zandr's linksys box, others take > 240s timeout = 480 # deep_check takes 360s on Zandr's linksys box, others take > 240s

View File

@ -5,6 +5,7 @@ import urllib, sys
from twisted.trial import unittest from twisted.trial import unittest
from twisted.python.monkey import MonkeyPatcher from twisted.python.monkey import MonkeyPatcher
from twisted.internet import task
import allmydata import allmydata
from allmydata.util import fileutil, hashutil, base32, keyutil from allmydata.util import fileutil, hashutil, base32, keyutil
@ -29,57 +30,16 @@ from allmydata.scripts.common import DEFAULT_ALIAS, get_aliases, get_alias, \
DefaultAliasMarker DefaultAliasMarker
from allmydata.scripts import cli, debug, runner from allmydata.scripts import cli, debug, runner
from ..common_util import ReallyEqualMixin from ..common_util import (ReallyEqualMixin, skip_if_cannot_represent_filename,
run_cli)
from ..no_network import GridTestMixin from ..no_network import GridTestMixin
from twisted.internet import threads # CLI tests use deferToThread from .common import CLITestMixin, parse_options
from twisted.python import usage from twisted.python import usage
from allmydata.util.assertutil import precondition from allmydata.util.encodingutil import listdir_unicode, get_io_encoding
from allmydata.util.encodingutil import listdir_unicode, unicode_platform, \
get_io_encoding, get_filesystem_encoding, unicode_to_argv
timeout = 480 # deep_check takes 360s on Zandr's linksys box, others take > 240s timeout = 480 # deep_check takes 360s on Zandr's linksys box, others take > 240s
def parse_options(basedir, command, args):
o = runner.Options()
o.parseOptions(["--node-directory", basedir, command] + args)
while hasattr(o, "subOptions"):
o = o.subOptions
return o
class CLITestMixin(ReallyEqualMixin):
def do_cli(self, verb, *args, **kwargs):
precondition(not [True for arg in args if not isinstance(arg, str)],
"arguments to do_cli must be strs -- convert using unicode_to_argv", args=args)
# client_num is used to execute client CLI commands on a specific client.
client_num = kwargs.get("client_num", 0)
nodeargs = [
"--node-directory", unicode_to_argv(self.get_clientdir(i=client_num)),
]
argv = nodeargs + [verb] + list(args)
stdin = kwargs.get("stdin", "")
stdout, stderr = StringIO(), StringIO()
d = threads.deferToThread(runner.runner, argv, run_by_human=False,
stdin=StringIO(stdin),
stdout=stdout, stderr=stderr)
def _done(rc):
return rc, stdout.getvalue(), stderr.getvalue()
d.addCallback(_done)
return d
def skip_if_cannot_represent_filename(self, u):
precondition(isinstance(u, unicode))
enc = get_filesystem_encoding()
if not unicode_platform():
try:
u.encode(enc)
except UnicodeEncodeError:
raise unittest.SkipTest("A non-ASCII filename could not be encoded on this platform.")
class CLI(CLITestMixin, unittest.TestCase): class CLI(CLITestMixin, unittest.TestCase):
def _dump_cap(self, *args): def _dump_cap(self, *args):
config = debug.DumpCapOptions() config = debug.DumpCapOptions()
@ -534,7 +494,7 @@ class CLI(CLITestMixin, unittest.TestCase):
filenames = [u'L\u00F4zane', u'Bern', u'Gen\u00E8ve'] # must be NFC filenames = [u'L\u00F4zane', u'Bern', u'Gen\u00E8ve'] # must be NFC
for name in filenames: for name in filenames:
self.skip_if_cannot_represent_filename(name) skip_if_cannot_represent_filename(name)
basedir = "cli/common/listdir_unicode_good" basedir = "cli/common/listdir_unicode_good"
fileutil.make_dirs(basedir) fileutil.make_dirs(basedir)
@ -552,10 +512,9 @@ class CLI(CLITestMixin, unittest.TestCase):
exc = Exception("canary") exc = Exception("canary")
ns = Namespace() ns = Namespace()
ns.runner_called = False ns.parse_called = False
def call_runner(args, install_node_control=True): def call_parse_or_exit(args):
ns.runner_called = True ns.parse_called = True
self.failUnlessEqual(install_node_control, True)
raise exc raise exc
ns.sys_exit_called = False ns.sys_exit_called = False
@ -563,13 +522,23 @@ class CLI(CLITestMixin, unittest.TestCase):
ns.sys_exit_called = True ns.sys_exit_called = True
self.failUnlessEqual(exitcode, 1) self.failUnlessEqual(exitcode, 1)
patcher = MonkeyPatcher((runner, 'runner', call_runner), def fake_react(f):
d = f("reactor")
# normally this Deferred would be errbacked with SystemExit, but
# since we mocked out sys.exit, it will be fired with None. So
# it's safe to drop it on the floor.
del d
patcher = MonkeyPatcher((runner, 'parse_or_exit_with_explanation',
call_parse_or_exit),
(sys, 'argv', ["tahoe"]), (sys, 'argv', ["tahoe"]),
(sys, 'exit', call_sys_exit), (sys, 'exit', call_sys_exit),
(sys, 'stderr', stderr)) (sys, 'stderr', stderr),
(task, 'react', fake_react),
)
patcher.runWithPatches(runner.run) patcher.runWithPatches(runner.run)
self.failUnless(ns.runner_called) self.failUnless(ns.parse_called)
self.failUnless(ns.sys_exit_called) self.failUnless(ns.sys_exit_called)
self.failUnlessIn(str(exc), stderr.getvalue()) self.failUnlessIn(str(exc), stderr.getvalue())
@ -747,21 +716,9 @@ class Ln(GridTestMixin, CLITestMixin, unittest.TestCase):
class Admin(unittest.TestCase): class Admin(unittest.TestCase):
def do_cli(self, *args, **kwargs):
argv = list(args)
stdin = kwargs.get("stdin", "")
stdout, stderr = StringIO(), StringIO()
d = threads.deferToThread(runner.runner, argv, run_by_human=False,
stdin=StringIO(stdin),
stdout=stdout, stderr=stderr)
def _done(res):
return stdout.getvalue(), stderr.getvalue()
d.addCallback(_done)
return d
def test_generate_keypair(self): def test_generate_keypair(self):
d = self.do_cli("admin", "generate-keypair") d = run_cli("admin", "generate-keypair")
def _done( (stdout, stderr) ): def _done( (rc, stdout, stderr) ):
lines = [line.strip() for line in stdout.splitlines()] lines = [line.strip() for line in stdout.splitlines()]
privkey_bits = lines[0].split() privkey_bits = lines[0].split()
pubkey_bits = lines[1].split() pubkey_bits = lines[1].split()
@ -780,8 +737,8 @@ class Admin(unittest.TestCase):
def test_derive_pubkey(self): def test_derive_pubkey(self):
priv1,pub1 = keyutil.make_keypair() priv1,pub1 = keyutil.make_keypair()
d = self.do_cli("admin", "derive-pubkey", priv1) d = run_cli("admin", "derive-pubkey", priv1)
def _done( (stdout, stderr) ): def _done( (rc, stdout, stderr) ):
lines = stdout.split("\n") lines = stdout.split("\n")
privkey_line = lines[0].strip() privkey_line = lines[0].strip()
pubkey_line = lines[1].strip() pubkey_line = lines[1].strip()

View File

@ -9,7 +9,8 @@ from allmydata.util.encodingutil import (quote_output, get_io_encoding,
unicode_to_output, to_str) unicode_to_output, to_str)
from allmydata.util.assertutil import _assert from allmydata.util.assertutil import _assert
from ..no_network import GridTestMixin from ..no_network import GridTestMixin
from .test_cli import CLITestMixin from .common import CLITestMixin
from ..common_util import skip_if_cannot_represent_filename
timeout = 480 # deep_check takes 360s on Zandr's linksys box, others take > 240s timeout = 480 # deep_check takes 360s on Zandr's linksys box, others take > 240s
@ -30,7 +31,7 @@ class Cp(GridTestMixin, CLITestMixin, unittest.TestCase):
except UnicodeEncodeError: except UnicodeEncodeError:
raise unittest.SkipTest("A non-ASCII command argument could not be encoded on this platform.") raise unittest.SkipTest("A non-ASCII command argument could not be encoded on this platform.")
self.skip_if_cannot_represent_filename(fn1) skip_if_cannot_represent_filename(fn1)
self.set_up_grid(oneshare=True) self.set_up_grid(oneshare=True)
@ -198,7 +199,7 @@ class Cp(GridTestMixin, CLITestMixin, unittest.TestCase):
except UnicodeEncodeError: except UnicodeEncodeError:
raise unittest.SkipTest("A non-ASCII command argument could not be encoded on this platform.") raise unittest.SkipTest("A non-ASCII command argument could not be encoded on this platform.")
self.skip_if_cannot_represent_filename(fn1) skip_if_cannot_represent_filename(fn1)
self.set_up_grid(oneshare=True) self.set_up_grid(oneshare=True)

View File

@ -1,54 +1,53 @@
import os import os
from StringIO import StringIO
from twisted.trial import unittest from twisted.trial import unittest
from allmydata.scripts import runner from twisted.internet import defer
from allmydata.util import configutil from allmydata.util import configutil
from ..common_util import run_cli
class Config(unittest.TestCase): class Config(unittest.TestCase):
def do_cli(self, *args):
argv = list(args)
stdout, stderr = StringIO(), StringIO()
rc = runner.runner(argv, run_by_human=False,
stdout=stdout, stderr=stderr)
return rc, stdout.getvalue(), stderr.getvalue()
def read_config(self, basedir): def read_config(self, basedir):
tahoe_cfg = os.path.join(basedir, "tahoe.cfg") tahoe_cfg = os.path.join(basedir, "tahoe.cfg")
config = configutil.get_config(tahoe_cfg) config = configutil.get_config(tahoe_cfg)
return config return config
@defer.inlineCallbacks
def test_client(self): def test_client(self):
basedir = self.mktemp() basedir = self.mktemp()
rc, out, err = self.do_cli("create-client", basedir) rc, out, err = yield run_cli("create-client", basedir)
cfg = self.read_config(basedir) cfg = self.read_config(basedir)
self.assertEqual(cfg.getboolean("node", "reveal-IP-address"), True) self.assertEqual(cfg.getboolean("node", "reveal-IP-address"), True)
@defer.inlineCallbacks
def test_client_hide_ip(self): def test_client_hide_ip(self):
basedir = self.mktemp() basedir = self.mktemp()
rc, out, err = self.do_cli("create-client", "--hide-ip", basedir) rc, out, err = yield run_cli("create-client", "--hide-ip", basedir)
cfg = self.read_config(basedir) cfg = self.read_config(basedir)
self.assertEqual(cfg.getboolean("node", "reveal-IP-address"), False) self.assertEqual(cfg.getboolean("node", "reveal-IP-address"), False)
@defer.inlineCallbacks
def test_node(self): def test_node(self):
basedir = self.mktemp() basedir = self.mktemp()
rc, out, err = self.do_cli("create-node", basedir) rc, out, err = yield run_cli("create-node", basedir)
cfg = self.read_config(basedir) cfg = self.read_config(basedir)
self.assertEqual(cfg.getboolean("node", "reveal-IP-address"), True) self.assertEqual(cfg.getboolean("node", "reveal-IP-address"), True)
@defer.inlineCallbacks
def test_node_hide_ip(self): def test_node_hide_ip(self):
basedir = self.mktemp() basedir = self.mktemp()
rc, out, err = self.do_cli("create-node", "--hide-ip", basedir) rc, out, err = yield run_cli("create-node", "--hide-ip", basedir)
cfg = self.read_config(basedir) cfg = self.read_config(basedir)
self.assertEqual(cfg.getboolean("node", "reveal-IP-address"), False) self.assertEqual(cfg.getboolean("node", "reveal-IP-address"), False)
@defer.inlineCallbacks
def test_introducer(self): def test_introducer(self):
basedir = self.mktemp() basedir = self.mktemp()
rc, out, err = self.do_cli("create-introducer", basedir) rc, out, err = yield run_cli("create-introducer", basedir)
cfg = self.read_config(basedir) cfg = self.read_config(basedir)
self.assertEqual(cfg.getboolean("node", "reveal-IP-address"), True) self.assertEqual(cfg.getboolean("node", "reveal-IP-address"), True)
@defer.inlineCallbacks
def test_introducer_hide_ip(self): def test_introducer_hide_ip(self):
basedir = self.mktemp() basedir = self.mktemp()
rc, out, err = self.do_cli("create-introducer", "--hide-ip", basedir) rc, out, err = yield run_cli("create-introducer", "--hide-ip", basedir)
cfg = self.read_config(basedir) cfg = self.read_config(basedir)
self.assertEqual(cfg.getboolean("node", "reveal-IP-address"), False) self.assertEqual(cfg.getboolean("node", "reveal-IP-address"), False)

View File

@ -6,7 +6,7 @@ from allmydata.scripts.common import get_aliases
from allmydata.scripts import cli, runner from allmydata.scripts import cli, runner
from ..no_network import GridTestMixin from ..no_network import GridTestMixin
from allmydata.util.encodingutil import quote_output, get_io_encoding from allmydata.util.encodingutil import quote_output, get_io_encoding
from .test_cli import CLITestMixin from .common import CLITestMixin
timeout = 480 # deep_check takes 360s on Zandr's linksys box, others take > 240s timeout = 480 # deep_check takes 360s on Zandr's linksys box, others take > 240s

View File

@ -6,7 +6,7 @@ from allmydata.interfaces import MDMF_VERSION, SDMF_VERSION
from allmydata.mutable.publish import MutableData from allmydata.mutable.publish import MutableData
from ..no_network import GridTestMixin from ..no_network import GridTestMixin
from allmydata.util.encodingutil import quote_output, get_io_encoding from allmydata.util.encodingutil import quote_output, get_io_encoding
from .test_cli import CLITestMixin from .common import CLITestMixin
timeout = 480 # deep_check takes 360s on Zandr's linksys box, others take > 240s timeout = 480 # deep_check takes 360s on Zandr's linksys box, others take > 240s

View File

@ -10,7 +10,7 @@ from allmydata.util.assertutil import precondition
from allmydata.util import fileutil from allmydata.util import fileutil
from allmydata.scripts.common import get_aliases from allmydata.scripts.common import get_aliases
from ..no_network import GridTestMixin from ..no_network import GridTestMixin
from .test_cli import CLITestMixin from .common import CLITestMixin
from allmydata.scripts import magic_folder_cli from allmydata.scripts import magic_folder_cli
from allmydata.util.fileutil import abspath_expanduser_unicode from allmydata.util.fileutil import abspath_expanduser_unicode
from allmydata.util.encodingutil import unicode_to_argv from allmydata.util.encodingutil import unicode_to_argv

View File

@ -3,7 +3,7 @@ from twisted.trial import unittest
from allmydata.util import fileutil from allmydata.util import fileutil
from ..no_network import GridTestMixin from ..no_network import GridTestMixin
from allmydata.scripts import tahoe_mv from allmydata.scripts import tahoe_mv
from .test_cli import CLITestMixin from .common import CLITestMixin
timeout = 480 # deep_check takes 360s on Zandr's linksys box, others take > 240s timeout = 480 # deep_check takes 360s on Zandr's linksys box, others take > 240s

View File

@ -6,9 +6,10 @@ from allmydata.util import fileutil
from allmydata.scripts.common import get_aliases from allmydata.scripts.common import get_aliases
from allmydata.scripts import cli from allmydata.scripts import cli
from ..no_network import GridTestMixin from ..no_network import GridTestMixin
from ..common_util import skip_if_cannot_represent_filename
from allmydata.util.encodingutil import get_io_encoding, unicode_to_argv from allmydata.util.encodingutil import get_io_encoding, unicode_to_argv
from allmydata.util.fileutil import abspath_expanduser_unicode from allmydata.util.fileutil import abspath_expanduser_unicode
from .test_cli import CLITestMixin from .common import CLITestMixin
timeout = 480 # deep_check takes 360s on Zandr's linksys box, others take > 240s timeout = 480 # deep_check takes 360s on Zandr's linksys box, others take > 240s
@ -427,7 +428,7 @@ class Put(GridTestMixin, CLITestMixin, unittest.TestCase):
except UnicodeEncodeError: except UnicodeEncodeError:
raise unittest.SkipTest("A non-ASCII command argument could not be encoded on this platform.") raise unittest.SkipTest("A non-ASCII command argument could not be encoded on this platform.")
self.skip_if_cannot_represent_filename(u"\u00E0 trier.txt") skip_if_cannot_represent_filename(u"\u00E0 trier.txt")
self.basedir = "cli/Put/immutable_from_file_unicode" self.basedir = "cli/Put/immutable_from_file_unicode"
self.set_up_grid(oneshare=True) self.set_up_grid(oneshare=True)

View File

@ -1,12 +1,50 @@
import os, signal, sys, time import os, signal, sys, time
from random import randrange from random import randrange
from cStringIO import StringIO
from twisted.internet import reactor, defer from twisted.internet import reactor, defer
from twisted.python import failure from twisted.python import failure
from twisted.trial import unittest
from allmydata.util import fileutil, log from allmydata.util import fileutil, log
from ..util.assertutil import precondition
from allmydata.util.encodingutil import unicode_platform, get_filesystem_encoding from allmydata.util.encodingutil import unicode_platform, get_filesystem_encoding
from ..scripts import runner
def skip_if_cannot_represent_filename(u):
precondition(isinstance(u, unicode))
enc = get_filesystem_encoding()
if not unicode_platform():
try:
u.encode(enc)
except UnicodeEncodeError:
raise unittest.SkipTest("A non-ASCII filename could not be encoded on this platform.")
def run_cli(verb, *args, **kwargs):
precondition(not [True for arg in args if not isinstance(arg, str)],
"arguments to do_cli must be strs -- convert using unicode_to_argv", args=args)
nodeargs = kwargs.get("nodeargs", [])
argv = nodeargs + [verb] + list(args)
stdin = kwargs.get("stdin", "")
stdout, stderr = StringIO(), StringIO()
d = defer.succeed(argv)
d.addCallback(runner.parse_or_exit_with_explanation, stdout=stdout)
d.addCallback(runner.dispatch,
stdin=StringIO(stdin),
stdout=stdout, stderr=stderr)
def _done(rc):
return 0, stdout.getvalue(), stderr.getvalue()
def _err(f):
f.trap(SystemExit)
return f.value.code, stdout.getvalue(), stderr.getvalue()
d.addCallbacks(_done, _err)
return d
def parse_cli(*argv):
# This parses the CLI options (synchronously), and returns the Options
# argument, or throws usage.UsageError if something went wrong.
return runner.parse_options(argv)
class DevNullDictionary(dict): class DevNullDictionary(dict):
def __setitem__(self, key, value): def __setitem__(self, key, value):

View File

@ -4,9 +4,9 @@ from StringIO import StringIO
from twisted.trial import unittest from twisted.trial import unittest
from allmydata.util import fileutil from allmydata.util import fileutil
from allmydata.util.encodingutil import listdir_unicode, get_filesystem_encoding, unicode_platform from allmydata.util.encodingutil import listdir_unicode
from allmydata.util.assertutil import precondition
from allmydata.scripts import backupdb from allmydata.scripts import backupdb
from .common_util import skip_if_cannot_represent_filename
class BackupDB(unittest.TestCase): class BackupDB(unittest.TestCase):
def create(self, dbfile): def create(self, dbfile):
@ -15,16 +15,6 @@ class BackupDB(unittest.TestCase):
self.failUnless(bdb, "unable to create backupdb from %r" % (dbfile,)) self.failUnless(bdb, "unable to create backupdb from %r" % (dbfile,))
return bdb return bdb
def skip_if_cannot_represent_filename(self, u):
precondition(isinstance(u, unicode))
enc = get_filesystem_encoding()
if not unicode_platform():
try:
u.encode(enc)
except UnicodeEncodeError:
raise unittest.SkipTest("A non-ASCII filename could not be encoded on this platform.")
def test_basic(self): def test_basic(self):
self.basedir = basedir = os.path.join("backupdb", "create") self.basedir = basedir = os.path.join("backupdb", "create")
fileutil.make_dirs(basedir) fileutil.make_dirs(basedir)
@ -222,8 +212,8 @@ class BackupDB(unittest.TestCase):
self.failIf(r.was_created()) self.failIf(r.was_created())
def test_unicode(self): def test_unicode(self):
self.skip_if_cannot_represent_filename(u"f\u00f6\u00f6.txt") skip_if_cannot_represent_filename(u"f\u00f6\u00f6.txt")
self.skip_if_cannot_represent_filename(u"b\u00e5r.txt") skip_if_cannot_represent_filename(u"b\u00e5r.txt")
self.basedir = basedir = os.path.join("backupdb", "unicode") self.basedir = basedir = os.path.join("backupdb", "unicode")
fileutil.make_dirs(basedir) fileutil.make_dirs(basedir)

View File

@ -4,12 +4,11 @@ from twisted.trial import unittest
from allmydata.util import configutil from allmydata.util import configutil
from allmydata.test.no_network import GridTestMixin from allmydata.test.no_network import GridTestMixin
from .cli.test_cli import CLITestMixin
from ..scripts import create_node from ..scripts import create_node
from .. import client from .. import client
class ConfigUtilTests(CLITestMixin, GridTestMixin, unittest.TestCase): class ConfigUtilTests(GridTestMixin, unittest.TestCase):
def test_config_utils(self): def test_config_utils(self):
self.basedir = "cli/ConfigUtilTests/test-config-utils" self.basedir = "cli/ConfigUtilTests/test-config-utils"

View File

@ -1,15 +1,12 @@
import os, simplejson, urllib import os, simplejson, urllib
from cStringIO import StringIO
from twisted.trial import unittest from twisted.trial import unittest
from twisted.internet import defer from twisted.internet import defer
from twisted.internet import threads # CLI tests use deferToThread
from allmydata.immutable import upload from allmydata.immutable import upload
from allmydata.mutable.common import UnrecoverableFileError from allmydata.mutable.common import UnrecoverableFileError
from allmydata.mutable.publish import MutableData from allmydata.mutable.publish import MutableData
from allmydata.util import idlib from allmydata.util import idlib
from allmydata.util import base32 from allmydata.util import base32
from allmydata.scripts import runner
from allmydata.interfaces import ICheckResults, ICheckAndRepairResults, \ from allmydata.interfaces import ICheckResults, ICheckAndRepairResults, \
IDeepCheckResults, IDeepCheckAndRepairResults IDeepCheckResults, IDeepCheckAndRepairResults
from allmydata.monitor import Monitor, OperationCancelledError from allmydata.monitor import Monitor, OperationCancelledError
@ -18,19 +15,13 @@ from twisted.web.client import getPage
from allmydata.test.common import ErrorMixin, _corrupt_mutable_share_data, \ from allmydata.test.common import ErrorMixin, _corrupt_mutable_share_data, \
ShouldFailMixin ShouldFailMixin
from allmydata.test.common_util import StallMixin from .common_util import StallMixin, run_cli
from allmydata.test.no_network import GridTestMixin from allmydata.test.no_network import GridTestMixin
from .cli.common import CLITestMixin
timeout = 2400 # One of these took 1046.091s on Zandr's ARM box. timeout = 2400 # One of these took 1046.091s on Zandr's ARM box.
class MutableChecker(GridTestMixin, unittest.TestCase, ErrorMixin): class MutableChecker(GridTestMixin, unittest.TestCase, ErrorMixin):
def _run_cli(self, argv):
stdout, stderr = StringIO(), StringIO()
# this can only do synchronous operations
assert argv[0] == "debug"
runner.runner(argv, run_by_human=False, stdout=stdout, stderr=stderr)
return stdout.getvalue()
def test_good(self): def test_good(self):
self.basedir = "deepcheck/MutableChecker/good" self.basedir = "deepcheck/MutableChecker/good"
self.set_up_grid() self.set_up_grid()
@ -130,7 +121,8 @@ class MutableChecker(GridTestMixin, unittest.TestCase, ErrorMixin):
return d return d
class DeepCheckBase(GridTestMixin, ErrorMixin, StallMixin, ShouldFailMixin): class DeepCheckBase(GridTestMixin, ErrorMixin, StallMixin, ShouldFailMixin,
CLITestMixin):
def web_json(self, n, **kwargs): def web_json(self, n, **kwargs):
kwargs["output"] = "json" kwargs["output"] = "json"
@ -727,17 +719,6 @@ class DeepCheckWebGood(DeepCheckBase, unittest.TestCase):
return d return d
def _run_cli(self, argv, stdin=""):
#print "CLI:", argv
stdout, stderr = StringIO(), StringIO()
d = threads.deferToThread(runner.runner, argv, run_by_human=False,
stdin=StringIO(stdin),
stdout=stdout, stderr=stderr)
def _done(res):
return stdout.getvalue(), stderr.getvalue()
d.addCallback(_done)
return d
def do_test_cli_good(self, ignored): def do_test_cli_good(self, ignored):
d = defer.succeed(None) d = defer.succeed(None)
d.addCallback(lambda ign: self.do_cli_manifest_stream1()) d.addCallback(lambda ign: self.do_cli_manifest_stream1())
@ -757,11 +738,8 @@ class DeepCheckWebGood(DeepCheckBase, unittest.TestCase):
self.failUnless(base32.b2a(self.large.get_storage_index()) in lines) self.failUnless(base32.b2a(self.large.get_storage_index()) in lines)
def do_cli_manifest_stream1(self): def do_cli_manifest_stream1(self):
basedir = self.get_clientdir(0) d = self.do_cli("manifest", self.root_uri)
d = self._run_cli(["--node-directory", basedir, def _check((rc,out,err)):
"manifest",
self.root_uri])
def _check((out,err)):
self.failUnlessEqual(err, "") self.failUnlessEqual(err, "")
lines = [l for l in out.split("\n") if l] lines = [l for l in out.split("\n") if l]
self.failUnlessEqual(len(lines), 8) self.failUnlessEqual(len(lines), 8)
@ -785,12 +763,8 @@ class DeepCheckWebGood(DeepCheckBase, unittest.TestCase):
return d return d
def do_cli_manifest_stream2(self): def do_cli_manifest_stream2(self):
basedir = self.get_clientdir(0) d = self.do_cli("manifest", "--raw", self.root_uri)
d = self._run_cli(["--node-directory", basedir, def _check((rc,out,err)):
"manifest",
"--raw",
self.root_uri])
def _check((out,err)):
self.failUnlessEqual(err, "") self.failUnlessEqual(err, "")
# this should be the same as the POST t=stream-manifest output # this should be the same as the POST t=stream-manifest output
self._check_streamed_manifest(out) self._check_streamed_manifest(out)
@ -798,24 +772,16 @@ class DeepCheckWebGood(DeepCheckBase, unittest.TestCase):
return d return d
def do_cli_manifest_stream3(self): def do_cli_manifest_stream3(self):
basedir = self.get_clientdir(0) d = self.do_cli("manifest", "--storage-index", self.root_uri)
d = self._run_cli(["--node-directory", basedir, def _check((rc,out,err)):
"manifest",
"--storage-index",
self.root_uri])
def _check((out,err)):
self.failUnlessEqual(err, "") self.failUnlessEqual(err, "")
self._check_manifest_storage_index(out) self._check_manifest_storage_index(out)
d.addCallback(_check) d.addCallback(_check)
return d return d
def do_cli_manifest_stream4(self): def do_cli_manifest_stream4(self):
basedir = self.get_clientdir(0) d = self.do_cli("manifest", "--verify-cap", self.root_uri)
d = self._run_cli(["--node-directory", basedir, def _check((rc,out,err)):
"manifest",
"--verify-cap",
self.root_uri])
def _check((out,err)):
self.failUnlessEqual(err, "") self.failUnlessEqual(err, "")
lines = [l for l in out.split("\n") if l] lines = [l for l in out.split("\n") if l]
self.failUnlessEqual(len(lines), 3) self.failUnlessEqual(len(lines), 3)
@ -826,12 +792,8 @@ class DeepCheckWebGood(DeepCheckBase, unittest.TestCase):
return d return d
def do_cli_manifest_stream5(self): def do_cli_manifest_stream5(self):
basedir = self.get_clientdir(0) d = self.do_cli("manifest", "--repair-cap", self.root_uri)
d = self._run_cli(["--node-directory", basedir, def _check((rc,out,err)):
"manifest",
"--repair-cap",
self.root_uri])
def _check((out,err)):
self.failUnlessEqual(err, "") self.failUnlessEqual(err, "")
lines = [l for l in out.split("\n") if l] lines = [l for l in out.split("\n") if l]
self.failUnlessEqual(len(lines), 3) self.failUnlessEqual(len(lines), 3)
@ -842,11 +804,8 @@ class DeepCheckWebGood(DeepCheckBase, unittest.TestCase):
return d return d
def do_cli_stats1(self): def do_cli_stats1(self):
basedir = self.get_clientdir(0) d = self.do_cli("stats", self.root_uri)
d = self._run_cli(["--node-directory", basedir, def _check3((rc,out,err)):
"stats",
self.root_uri])
def _check3((out,err)):
lines = [l.strip() for l in out.split("\n") if l] lines = [l.strip() for l in out.split("\n") if l]
self.failUnless("count-immutable-files: 1" in lines) self.failUnless("count-immutable-files: 1" in lines)
self.failUnless("count-mutable-files: 1" in lines) self.failUnless("count-mutable-files: 1" in lines)
@ -862,12 +821,8 @@ class DeepCheckWebGood(DeepCheckBase, unittest.TestCase):
return d return d
def do_cli_stats2(self): def do_cli_stats2(self):
basedir = self.get_clientdir(0) d = self.do_cli("stats", "--raw", self.root_uri)
d = self._run_cli(["--node-directory", basedir, def _check4((rc,out,err)):
"stats",
"--raw",
self.root_uri])
def _check4((out,err)):
data = simplejson.loads(out) data = simplejson.loads(out)
self.failUnlessEqual(data["count-immutable-files"], 1) self.failUnlessEqual(data["count-immutable-files"], 1)
self.failUnlessEqual(data["count-immutable-files"], 1) self.failUnlessEqual(data["count-immutable-files"], 1)
@ -983,20 +938,14 @@ class DeepCheckWebBad(DeepCheckBase, unittest.TestCase):
return d return d
def _run_cli(self, argv):
stdout, stderr = StringIO(), StringIO()
# this can only do synchronous operations
assert argv[0] == "debug"
runner.runner(argv, run_by_human=False, stdout=stdout, stderr=stderr)
return stdout.getvalue()
def _delete_some_shares(self, node): def _delete_some_shares(self, node):
self.delete_shares_numbered(node.get_uri(), [0,1]) self.delete_shares_numbered(node.get_uri(), [0,1])
@defer.inlineCallbacks
def _corrupt_some_shares(self, node): def _corrupt_some_shares(self, node):
for (shnum, serverid, sharefile) in self.find_uri_shares(node.get_uri()): for (shnum, serverid, sharefile) in self.find_uri_shares(node.get_uri()):
if shnum in (0,1): if shnum in (0,1):
self._run_cli(["debug", "corrupt-share", sharefile]) yield run_cli("debug", "corrupt-share", sharefile)
def _delete_most_shares(self, node): def _delete_most_shares(self, node):
self.delete_shares_numbered(node.get_uri(), range(1,10)) self.delete_shares_numbered(node.get_uri(), range(1,10))

View File

@ -71,7 +71,7 @@ from allmydata.util.encodingutil import argv_to_unicode, unicode_to_url, \
get_io_encoding, get_filesystem_encoding, to_str, from_utf8_or_none, _reload, \ get_io_encoding, get_filesystem_encoding, to_str, from_utf8_or_none, _reload, \
to_filepath, extend_filepath, unicode_from_filepath, unicode_segments_from to_filepath, extend_filepath, unicode_from_filepath, unicode_segments_from
from allmydata.dirnode import normalize from allmydata.dirnode import normalize
from .common_util import skip_if_cannot_represent_filename
from twisted.python import usage from twisted.python import usage
@ -265,16 +265,8 @@ class StdlibUnicode(unittest.TestCase):
"""This mainly tests that some of the stdlib functions support Unicode paths, but also that """This mainly tests that some of the stdlib functions support Unicode paths, but also that
listdir_unicode works for valid filenames.""" listdir_unicode works for valid filenames."""
def skip_if_cannot_represent_filename(self, u):
enc = get_filesystem_encoding()
if not unicode_platform():
try:
u.encode(enc)
except UnicodeEncodeError:
raise unittest.SkipTest("A non-ASCII filename could not be encoded on this platform.")
def test_mkdir_open_exists_abspath_listdir_expanduser(self): def test_mkdir_open_exists_abspath_listdir_expanduser(self):
self.skip_if_cannot_represent_filename(lumiere_nfc) skip_if_cannot_represent_filename(lumiere_nfc)
try: try:
os.mkdir(lumiere_nfc) os.mkdir(lumiere_nfc)

View File

@ -1,5 +1,4 @@
import os.path, re, sys, subprocess import os.path, re, sys, subprocess
from cStringIO import StringIO
from twisted.trial import unittest from twisted.trial import unittest
@ -10,11 +9,11 @@ from twisted.internet.defer import inlineCallbacks, returnValue
from allmydata.util import fileutil, pollmixin from allmydata.util import fileutil, pollmixin
from allmydata.util.encodingutil import unicode_to_argv, unicode_to_output, \ from allmydata.util.encodingutil import unicode_to_argv, unicode_to_output, \
get_filesystem_encoding get_filesystem_encoding
from allmydata.scripts import runner
from allmydata.client import Client from allmydata.client import Client
from allmydata.test import common_util from allmydata.test import common_util
import allmydata import allmydata
from allmydata import __appname__ from allmydata import __appname__
from .common_util import parse_cli, run_cli
timeout = 240 timeout = 240
@ -180,11 +179,7 @@ class CreateNode(unittest.TestCase):
fileutil.make_dirs(basedir) fileutil.make_dirs(basedir)
return basedir return basedir
def run_tahoe(self, argv): @inlineCallbacks
out,err = StringIO(), StringIO()
rc = runner.runner(argv, stdout=out, stderr=err)
return rc, out.getvalue(), err.getvalue()
def do_create(self, kind, *args): def do_create(self, kind, *args):
basedir = self.workdir("test_" + kind) basedir = self.workdir("test_" + kind)
command = "create-" + kind command = "create-" + kind
@ -193,7 +188,7 @@ class CreateNode(unittest.TestCase):
n1 = os.path.join(basedir, command + "-n1") n1 = os.path.join(basedir, command + "-n1")
argv = ["--quiet", command, "--basedir", n1] + list(args) argv = ["--quiet", command, "--basedir", n1] + list(args)
rc, out, err = self.run_tahoe(argv) rc, out, err = yield run_cli(*argv)
self.failUnlessEqual(err, "") self.failUnlessEqual(err, "")
self.failUnlessEqual(out, "") self.failUnlessEqual(out, "")
self.failUnlessEqual(rc, 0) self.failUnlessEqual(rc, 0)
@ -213,7 +208,7 @@ class CreateNode(unittest.TestCase):
self.failUnless("\nreserved_space = 1G\n" in content) self.failUnless("\nreserved_space = 1G\n" in content)
# creating the node a second time should be rejected # creating the node a second time should be rejected
rc, out, err = self.run_tahoe(argv) rc, out, err = yield run_cli(*argv)
self.failIfEqual(rc, 0, str((out, err, rc))) self.failIfEqual(rc, 0, str((out, err, rc)))
self.failUnlessEqual(out, "") self.failUnlessEqual(out, "")
self.failUnless("is not empty." in err) self.failUnless("is not empty." in err)
@ -226,7 +221,7 @@ class CreateNode(unittest.TestCase):
# test that the non --basedir form works too # test that the non --basedir form works too
n2 = os.path.join(basedir, command + "-n2") n2 = os.path.join(basedir, command + "-n2")
argv = ["--quiet", command] + list(args) + [n2] argv = ["--quiet", command] + list(args) + [n2]
rc, out, err = self.run_tahoe(argv) rc, out, err = yield run_cli(*argv)
self.failUnlessEqual(err, "") self.failUnlessEqual(err, "")
self.failUnlessEqual(out, "") self.failUnlessEqual(out, "")
self.failUnlessEqual(rc, 0) self.failUnlessEqual(rc, 0)
@ -236,7 +231,7 @@ class CreateNode(unittest.TestCase):
# test the --node-directory form # test the --node-directory form
n3 = os.path.join(basedir, command + "-n3") n3 = os.path.join(basedir, command + "-n3")
argv = ["--quiet", "--node-directory", n3, command] + list(args) argv = ["--quiet", "--node-directory", n3, command] + list(args)
rc, out, err = self.run_tahoe(argv) rc, out, err = yield run_cli(*argv)
self.failUnlessEqual(err, "") self.failUnlessEqual(err, "")
self.failUnlessEqual(out, "") self.failUnlessEqual(out, "")
self.failUnlessEqual(rc, 0) self.failUnlessEqual(rc, 0)
@ -247,7 +242,7 @@ class CreateNode(unittest.TestCase):
# test that the output (without --quiet) includes the base directory # test that the output (without --quiet) includes the base directory
n4 = os.path.join(basedir, command + "-n4") n4 = os.path.join(basedir, command + "-n4")
argv = [command] + list(args) + [n4] argv = [command] + list(args) + [n4]
rc, out, err = self.run_tahoe(argv) rc, out, err = yield run_cli(*argv)
self.failUnlessEqual(err, "") self.failUnlessEqual(err, "")
self.failUnlessIn(" created in ", out) self.failUnlessIn(" created in ", out)
self.failUnlessIn(n4, out) self.failUnlessIn(n4, out)
@ -257,18 +252,14 @@ class CreateNode(unittest.TestCase):
self.failUnless(os.path.exists(os.path.join(n4, tac))) self.failUnless(os.path.exists(os.path.join(n4, tac)))
# make sure it rejects too many arguments # make sure it rejects too many arguments
argv = [command, "basedir", "extraarg"] self.failUnlessRaises(usage.UsageError, parse_cli,
self.failUnlessRaises(usage.UsageError, command, "basedir", "extraarg")
runner.runner, argv,
run_by_human=False)
# when creating a non-client, there is no default for the basedir # when creating a non-client, there is no default for the basedir
if not is_client: if not is_client:
argv = [command] argv = [command]
self.failUnlessRaises(usage.UsageError, self.failUnlessRaises(usage.UsageError, parse_cli,
runner.runner, argv, command)
run_by_human=False)
def test_node(self): def test_node(self):
self.do_create("node") self.do_create("node")
@ -285,49 +276,42 @@ class CreateNode(unittest.TestCase):
def test_subcommands(self): def test_subcommands(self):
# no arguments should trigger a command listing, via UsageError # no arguments should trigger a command listing, via UsageError
self.failUnlessRaises(usage.UsageError, self.failUnlessRaises(usage.UsageError, parse_cli,
runner.runner, )
[],
run_by_human=False)
@inlineCallbacks
def test_stats_gatherer_good_args(self): def test_stats_gatherer_good_args(self):
rc = runner.runner(["create-stats-gatherer", "--hostname=foo", rc,out,err = yield run_cli("create-stats-gatherer", "--hostname=foo",
self.mktemp()]) self.mktemp())
self.assertEqual(rc, 0) self.assertEqual(rc, 0)
rc = runner.runner(["create-stats-gatherer", "--location=tcp:foo:1234", rc,out,err = yield run_cli("create-stats-gatherer",
"--port=tcp:1234", self.mktemp()]) "--location=tcp:foo:1234",
"--port=tcp:1234", self.mktemp())
self.assertEqual(rc, 0) self.assertEqual(rc, 0)
def test_stats_gatherer_bad_args(self): def test_stats_gatherer_bad_args(self):
def _test(args):
argv = args.split()
self.assertRaises(usage.UsageError, parse_cli, *argv)
# missing hostname/location/port # missing hostname/location/port
argv = "create-stats-gatherer D" _test("create-stats-gatherer D")
self.assertRaises(usage.UsageError, runner.runner, argv.split(),
run_by_human=False)
# missing port # missing port
argv = "create-stats-gatherer --location=foo D" _test("create-stats-gatherer --location=foo D")
self.assertRaises(usage.UsageError, runner.runner, argv.split(),
run_by_human=False)
# missing location # missing location
argv = "create-stats-gatherer --port=foo D" _test("create-stats-gatherer --port=foo D")
self.assertRaises(usage.UsageError, runner.runner, argv.split(),
run_by_human=False)
# can't provide both # can't provide both
argv = "create-stats-gatherer --hostname=foo --port=foo D" _test("create-stats-gatherer --hostname=foo --port=foo D")
self.assertRaises(usage.UsageError, runner.runner, argv.split(),
run_by_human=False)
# can't provide both # can't provide both
argv = "create-stats-gatherer --hostname=foo --location=foo D" _test("create-stats-gatherer --hostname=foo --location=foo D")
self.assertRaises(usage.UsageError, runner.runner, argv.split(),
run_by_human=False)
# can't provide all three # can't provide all three
argv = "create-stats-gatherer --hostname=foo --location=foo --port=foo D" _test("create-stats-gatherer --hostname=foo --location=foo --port=foo D")
self.assertRaises(usage.UsageError, runner.runner, argv.split(),
run_by_human=False)
class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin, class RunNode(common_util.SignalMixin, unittest.TestCase, pollmixin.PollMixin,
RunBinTahoeMixin): RunBinTahoeMixin):

View File

@ -1,10 +1,8 @@
import os, re, sys, time, simplejson import os, re, sys, time, simplejson
from cStringIO import StringIO
from twisted.trial import unittest from twisted.trial import unittest
from twisted.internet import defer from twisted.internet import defer
from twisted.internet import threads # CLI tests use deferToThread
from twisted.application import service from twisted.application import service
import allmydata import allmydata
@ -20,7 +18,6 @@ from allmydata.util import log, base32
from allmydata.util.encodingutil import quote_output, unicode_to_argv from allmydata.util.encodingutil import quote_output, unicode_to_argv
from allmydata.util.fileutil import abspath_expanduser_unicode from allmydata.util.fileutil import abspath_expanduser_unicode
from allmydata.util.consumer import MemoryConsumer, download_to_data from allmydata.util.consumer import MemoryConsumer, download_to_data
from allmydata.scripts import runner
from allmydata.stats import StatsGathererService from allmydata.stats import StatsGathererService
from allmydata.interfaces import IDirectoryNode, IFileNode, \ from allmydata.interfaces import IDirectoryNode, IFileNode, \
NoSuchChildError, NoSharesError NoSuchChildError, NoSharesError
@ -39,6 +36,7 @@ from .common import TEST_RSA_KEY_SIZE
# TODO: move this to common or common_util # TODO: move this to common or common_util
from allmydata.test.test_runner import RunBinTahoeMixin from allmydata.test.test_runner import RunBinTahoeMixin
from . import common_util as testutil from . import common_util as testutil
from .common_util import run_cli
LARGE_DATA = """ LARGE_DATA = """
This is some data to publish to the remote grid.., which needs to be large This is some data to publish to the remote grid.., which needs to be large
@ -1064,6 +1062,7 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
return d1 return d1
d.addCallback(_create_mutable) d.addCallback(_create_mutable)
@defer.inlineCallbacks
def _test_debug(res): def _test_debug(res):
# find a share. It is important to run this while there is only # find a share. It is important to run this while there is only
# one slot in the grid. # one slot in the grid.
@ -1073,11 +1072,8 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
% filename) % filename)
log.msg(" for clients[%d]" % client_num) log.msg(" for clients[%d]" % client_num)
out,err = StringIO(), StringIO() rc,output,err = yield run_cli("debug", "dump-share", "--offsets",
rc = runner.runner(["debug", "dump-share", "--offsets", filename)
filename],
stdout=out, stderr=err)
output = out.getvalue()
self.failUnlessEqual(rc, 0) self.failUnlessEqual(rc, 0)
try: try:
self.failUnless("Mutable slot found:\n" in output) self.failUnless("Mutable slot found:\n" in output)
@ -1862,6 +1858,7 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
return d return d
@defer.inlineCallbacks
def _test_runner(self, res): def _test_runner(self, res):
# exercise some of the diagnostic tools in runner.py # exercise some of the diagnostic tools in runner.py
@ -1887,11 +1884,8 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
% self.basedir) % self.basedir)
log.msg("test_system.SystemTest._test_runner using %r" % filename) log.msg("test_system.SystemTest._test_runner using %r" % filename)
out,err = StringIO(), StringIO() rc,output,err = yield run_cli("debug", "dump-share", "--offsets",
rc = runner.runner(["debug", "dump-share", "--offsets", unicode_to_argv(filename))
unicode_to_argv(filename)],
stdout=out, stderr=err)
output = out.getvalue()
self.failUnlessEqual(rc, 0) self.failUnlessEqual(rc, 0)
# we only upload a single file, so we can assert some things about # we only upload a single file, so we can assert some things about
@ -1917,23 +1911,18 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
sharedir, shnum = os.path.split(filename) sharedir, shnum = os.path.split(filename)
storagedir, storage_index_s = os.path.split(sharedir) storagedir, storage_index_s = os.path.split(sharedir)
storage_index_s = str(storage_index_s) storage_index_s = str(storage_index_s)
out,err = StringIO(), StringIO()
nodedirs = [self.getdir("client%d" % i) for i in range(self.numclients)] nodedirs = [self.getdir("client%d" % i) for i in range(self.numclients)]
cmd = ["debug", "find-shares", storage_index_s] + nodedirs rc,out,err = yield run_cli("debug", "find-shares", storage_index_s,
rc = runner.runner(cmd, stdout=out, stderr=err) *nodedirs)
self.failUnlessEqual(rc, 0) self.failUnlessEqual(rc, 0)
out.seek(0) sharefiles = [sfn.strip() for sfn in out.splitlines()]
sharefiles = [sfn.strip() for sfn in out.readlines()]
self.failUnlessEqual(len(sharefiles), 10) self.failUnlessEqual(len(sharefiles), 10)
# also exercise the 'catalog-shares' tool # also exercise the 'catalog-shares' tool
out,err = StringIO(), StringIO()
nodedirs = [self.getdir("client%d" % i) for i in range(self.numclients)] nodedirs = [self.getdir("client%d" % i) for i in range(self.numclients)]
cmd = ["debug", "catalog-shares"] + nodedirs rc,out,err = yield run_cli("debug", "catalog-shares", *nodedirs)
rc = runner.runner(cmd, stdout=out, stderr=err)
self.failUnlessEqual(rc, 0) self.failUnlessEqual(rc, 0)
out.seek(0) descriptions = [sfn.strip() for sfn in out.splitlines()]
descriptions = [sfn.strip() for sfn in out.readlines()]
self.failUnlessEqual(len(descriptions), 30) self.failUnlessEqual(len(descriptions), 30)
matching = [line matching = [line
for line in descriptions for line in descriptions
@ -1981,10 +1970,10 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
f.write(private_uri) f.write(private_uri)
f.close() f.close()
@defer.inlineCallbacks
def run(ignored, verb, *args, **kwargs): def run(ignored, verb, *args, **kwargs):
stdin = kwargs.get("stdin", "") rc,out,err = yield run_cli(verb, *args, nodeargs=nodeargs, **kwargs)
newargs = nodeargs + [verb] + list(args) defer.returnValue((out,err))
return self._run_cli(newargs, stdin=stdin)
def _check_ls((out,err), expected_children, unexpected_children=[]): def _check_ls((out,err), expected_children, unexpected_children=[]):
self.failUnlessEqual(err, "") self.failUnlessEqual(err, "")
@ -2345,17 +2334,6 @@ class SystemTest(SystemTestMixin, RunBinTahoeMixin, unittest.TestCase):
d.addCallback(_check_ls) d.addCallback(_check_ls)
return d return d
def _run_cli(self, argv, stdin=""):
#print "CLI:", argv
stdout, stderr = StringIO(), StringIO()
d = threads.deferToThread(runner.runner, argv, run_by_human=False,
stdin=StringIO(stdin),
stdout=stdout, stderr=stderr)
def _done(res):
return stdout.getvalue(), stderr.getvalue()
d.addCallback(_done)
return d
def _test_checker(self, res): def _test_checker(self, res):
ut = upload.Data("too big to be literal" * 200, convergence=None) ut = upload.Data("too big to be literal" * 200, convergence=None)
d = self._personal_node.add_file(u"big file", ut) d = self._personal_node.add_file(u"big file", ut)