]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/commitdiff
A start at adding a system test for tahoe_fuse. Incomplete...
authornejucomo <nejucomo@gmail.com>
Sun, 20 Jan 2008 23:54:56 +0000 (16:54 -0700)
committernejucomo <nejucomo@gmail.com>
Sun, 20 Jan 2008 23:54:56 +0000 (16:54 -0700)
contrib/fuse/runtests.py

index e0292438e5f4a022b1714ab111e30ebef76d1a98..adb2f9112e79e55c219729af37ba7ceb2380a621 100644 (file)
@@ -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()