From: nejucomo Date: Sun, 20 Jan 2008 23:54:56 +0000 (-0700) Subject: A start at adding a system test for tahoe_fuse. Incomplete... X-Git-Tag: allmydata-tahoe-0.8.0~245 X-Git-Url: https://git.rkrishnan.org/provisioning?a=commitdiff_plain;h=026f2d0df54eb0dd7c152f5bcced3d9034832852;p=tahoe-lafs%2Ftahoe-lafs.git A start at adding a system test for tahoe_fuse. Incomplete... --- diff --git a/contrib/fuse/runtests.py b/contrib/fuse/runtests.py index e0292438..adb2f911 100644 --- a/contrib/fuse/runtests.py +++ b/contrib/fuse/runtests.py @@ -6,11 +6,211 @@ Note: The API design of the python-fuse library makes unit testing much of tahoe-fuse.py tricky business. ''' -import unittest +import sys, os, shutil, unittest, subprocess, tempfile, re import tahoe_fuse +### Main flow control: +def main(args = sys.argv[1:]): + target = 'all' + if args: + if len(args) != 1: + raise SystemExit(Usage) + target = args[0] + + if target not in ('all', 'unit', 'system'): + raise SystemExit(Usage) + + if target in ('all', 'unit'): + run_unit_tests() + + if target in ('all', 'system'): + run_system_test() + + +def run_unit_tests(): + print 'Running Unit Tests.' + try: + unittest.main() + except SystemExit, se: + pass + print 'Unit Tests complete.\n' + + +def run_system_test(): + SystemTest().run() + + +### System Testing: +class SystemTest (object): + def __init__(self): + self.cliexec = None + self.introbase = None + self.clientbase = None + + ## Top-level flow control: + # These "*_layer" methods call eachother in a linear fashion, using + # exception unwinding to do cleanup properly. Each "layer" invokes + # a deeper layer, and each layer does its own cleanup upon exit. + + def run(self): + print 'Running System Test.' + try: + self.init_cli_layer() + except self.SetupFailure, sfail: + print + print sfail + + print 'System Test complete.' + + def init_cli_layer(self): + '''This layer finds the appropriate tahoe executable.''' + runtestpath = os.path.abspath(sys.argv[0]) + path = runtestpath + for expectedname in ('runtests.py', 'fuse', 'contrib'): + path, name = os.path.split(path) + + if name != expectedname: + reason = 'Unexpected test script path: %r\n' + reason += 'The system test script must be run from the source directory.' + raise self.SetupFailure(reason, runtestpath) + + self.cliexec = os.path.join(path, 'bin', 'tahoe') + version = self.run_tahoe('--version') + print 'Using %r with version:\n%s' % (self.cliexec, version.rstrip()) + + self.create_introducer_layer() + + def create_introducer_layer(self): + print 'Creating introducer.' + self.introbase = tempfile.mkdtemp(prefix='tahoe_fuse_test_', + suffix='_introducer') + try: + output = self.run_tahoe('create-introducer', '--basedir', self.introbase) + + pat = r'^introducer created in (.*?)\n\s*$' + self.check_tahoe_output(output, pat, self.introbase) + + self.launch_introducer_layer() + + finally: + print 'Removing introducer directory.' + try: + shutil.rmtree(self.introbase) + except Exception, e: + print 'Exception removing test client directory: %r' % (self.introbase,) + print 'Ignoring cleanup exception: %r' % (e,) + + def launch_introducer_layer(self): + print 'Launching introducer.' + # NOTE: We assume if tahoe exist with non-zero status, no separate + # tahoe child process is still running. + output = self.run_tahoe('start', '--basedir', self.introbase) + try: + pat = r'^STARTING (.*?)\nintroducer node probably started\s*$' + self.check_tahoe_output(output, pat, self.introbase) + + self.create_client_layer() + + finally: + print 'Stopping introducer node.' + try: + output = self.run_tahoe('stop', '--basedir', self.introbase) + except Exception, e: + print 'Failed to stop introducer node. Output:' + print output + print 'Ignoring cleanup exception: %r' % (e,) + + def create_client_layer(self): + print 'Creating client.' + self.clientbase = tempfile.mkdtemp(prefix='tahoe_fuse_test_', + suffix='_client') + try: + output = self.run_tahoe('create-client', '--basedir', self.clientbase) + pat = r'^client created in (.*?)\n' + pat += r' please copy introducer.furl into the directory\s*$' + self.check_tahoe_output(output, pat, self.clientbase) + + self.launch_client_layer() + + finally: + print 'Removing client directory.' + try: + shutil.rmtree(self.clientbase) + except Exception, e: + print 'Exception removing test client directory: %r' % (self.clientbase,) + print 'Ignoring cleanup exception: %r' % (e,) + + def launch_client_layer(self): + print 'Launching client.' + # NOTE: We assume if tahoe exist with non-zero status, no separate + # tahoe child process is still running. + output = self.run_tahoe('start', '--basedir', self.clientbase) + try: + pat = r'^STARTING (.*?)\nclient node probably started\s*$' + self.check_tahoe_output(output, pat, self.clientbase) + + self.mount_fuse_layer() + + finally: + print 'Stopping client node.' + try: + output = self.run_tahoe('stop', '--basedir', self.clientbase) + except Exception, e: + print 'Failed to stop client node. Output:' + print output + print 'Ignoring cleanup exception: %r' % (e,) + + def mount_fuse_layer(self): + # XXX not implemented. + pass + + + # Utilities: + def run_tahoe(self, *args): + realargs = ('tahoe',) + args + status, output = gather_output(realargs, executable=self.cliexec) + if status != 0: + tmpl = 'The tahoe cli exited with nonzero status.\n' + tmpl += 'Executable: %r\n' + tmpl += 'Command arguments: %r\n' + tmpl += 'Exit status: %r\n' + tmpl += 'Output:\n%s\n[End of tahoe output.]\n' + raise self.SetupFailure(tmpl, + self.cliexec, + realargs, + status, + output) + return output + + def check_tahoe_output(self, output, expected, expdir): + m = re.match(expected, output, re.M) + if m is None: + tmpl = 'The output of tahoe did not match the expectation:\n' + tmpl += 'Expected regex: %s\n' + tmpl += 'Actual output: %r\n' + raise self.SetupFailure(tmpl, expected, output) + + if expdir != m.group(1): + tmpl = 'The output of tahoe refers to an unexpected directory:\n' + tmpl += 'Expected directory: %r\n' + tmpl += 'Actual directory: %r\n' + raise self.SetupFailure(tmpl, expdir, m.group(1)) + + + # SystemTest Exceptions: + class Failure (Exception): + pass + + class SetupFailure (Failure): + def __init__(self, tmpl, *args): + msg = 'SystemTest.SetupFailure - A test environment could not be created:\n' + msg += tmpl % args + SystemTest.Failure.__init__(self, msg) + + +### Unit Tests: class TestUtilFunctions (unittest.TestCase): '''Tests small stand-alone functions.''' def test_canonicalize_cap(self): @@ -25,7 +225,30 @@ class TestUtilFunctions (unittest.TestCase): +### Misc: +def gather_output(*args, **kwargs): + ''' + This expects the child does not require input and that it closes + stdout/err eventually. + ''' + p = subprocess.Popen(stdout = subprocess.PIPE, + stderr = subprocess.STDOUT, + *args, + **kwargs) + output = p.stdout.read() + exitcode = p.wait() + return (exitcode, output) + + +Usage = ''' +Usage: %s [target] + +Run tests for the given target. + +target is one of: unit, system, or all +''' % (sys.argv[0],) + if __name__ == '__main__': - unittest.main() + main()