tahoe-lafs/contrib/fuse/runtests.py
robk-tahoe 06b05a868d * fuse/runtests: added --catch-up-pause option
On linux, write tests are failing because data written to fuse isn't showing
up in tahoe by the time it's checked.  it's not clear where this is originating,
since the fuse implementation [should be] waiting for completion of tahoe 
operations before returning from its calls.  This adds an option to control the
duration of a pause between the fuse write and the check of tahoe, which is by
default set to 2s on linux, which - somewhat inexplicably - seems to 'fix' the 
problem, in as far as it allows tests to complete.
2008-10-20 17:29:02 -07:00

905 lines
32 KiB
Python

#! /usr/bin/env python
'''
Unit and system tests for tahoe-fuse.
'''
# Note: It's always a SetupFailure, not a TestFailure if a webapi
# operation fails, because this does not indicate a fuse interface
# failure.
# TODO: Unmount after tests regardless of failure or success!
# TODO: Test mismatches between tahoe and fuse/posix. What about nodes
# with crazy names ('\0', unicode, '/', '..')? Huuuuge files?
# Huuuuge directories... As tahoe approaches production quality, it'd
# be nice if the fuse interface did so also by hardening against such cases.
# FIXME: Only create / launch necessary nodes. Do we still need an introducer and three nodes?
# FIXME: This framework might be replaceable with twisted.trial,
# especially the "layer" design, which is a bit cumbersome when
# using recursion to manage multiple clients.
# FIXME: Identify all race conditions (hint: starting clients, versus
# using the grid fs).
import sys, os, shutil, unittest, subprocess
import tempfile, re, time, random, httplib, urllib
#import traceback
from twisted.python import usage
if sys.platform.startswith('darwin'):
UNMOUNT_CMD = ['umount']
else:
# linux, and until we hear otherwise, all other platforms with fuse, by assumption
UNMOUNT_CMD = ['fusermount', '-u']
# Import fuse implementations:
#FuseDir = os.path.join('.', 'contrib', 'fuse')
#if not os.path.isdir(FuseDir):
# raise SystemExit('''
#Could not find directory "%s". Please run this script from the tahoe
#source base directory.
#''' % (FuseDir,))
FuseDir = '.'
### Load each implementation
sys.path.append(os.path.join(FuseDir, 'impl_a'))
import tahoe_fuse as impl_a
sys.path.append(os.path.join(FuseDir, 'impl_b'))
import pyfuse.tahoe as impl_b
sys.path.append(os.path.join(FuseDir, 'impl_c'))
import blackmatch as impl_c
### config info about each impl, including which make sense to run
implementations = {
'impl_a': dict(module=impl_a,
mount_args=['--basedir', '%(nodedir)s', '%(mountpath)s', ],
mount_wait=True,
suites=['read', ]),
'impl_b': dict(module=impl_b,
todo=True,
mount_args=['--basedir', '%(nodedir)s', '%(mountpath)s', ],
mount_wait=False,
suites=['read', ]),
'impl_c': dict(module=impl_c,
mount_args=['--cache-timeout', '0', '--root-uri', '%(root-uri)s',
'--node-directory', '%(nodedir)s', '%(mountpath)s', ],
mount_wait=True,
suites=['read', 'write', ]),
'impl_c_no_split': dict(module=impl_c,
mount_args=['--cache-timeout', '0', '--root-uri', '%(root-uri)s',
'--no-split',
'--node-directory', '%(nodedir)s', '%(mountpath)s', ],
mount_wait=True,
suites=['read', 'write', ]),
}
if sys.platform == 'darwin':
del implementations['impl_a']
del implementations['impl_b']
default_catch_up_pause = 0
if sys.platform == 'linux2':
default_catch_up_pause = 2
class FuseTestsOptions(usage.Options):
optParameters = [
["test-type", None, "both",
"Type of test to run; unit, system or both"
],
["implementations", None, "all",
"Comma separated list of implementations to test, or 'all'"
],
["suites", None, "all",
"Comma separated list of test suites to run, or 'all'"
],
["tests", None, None,
"Comma separated list of specific tests to run"
],
["path-to-tahoe", None, "../../bin/tahoe",
"Which 'tahoe' script to use to create test nodes"],
["tmp-dir", None, "/tmp",
"Where the test should create temporary files"],
# Note; this is '/tmp' because on leopard, tempfile.mkdtemp creates
# directories in a location which leads paths to exceed what macfuse
# can handle without leaking un-umount-able fuse processes.
["catch-up-pause", None, str(default_catch_up_pause),
"Pause between tahoe operations and fuse tests thereon"],
]
optFlags = [
["debug-wait", None,
"Causes the test system to pause at various points, to facilitate debugging"],
["web-open", None,
"Opens a web browser to the web ui at the start of each impl's tests"],
["no-cleanup", False,
"Prevents the cleanup of the working directories, to allow analysis thereof"],
]
def postOptions(self):
if self['suites'] == 'all':
self.suites = ['read', 'write']
# [ ] todo: deduce this from looking for test_ in dir(self)
else:
self.suites = map(str.strip, self['suites'].split(','))
if self['implementations'] == 'all':
self.implementations = implementations.keys()
else:
self.implementations = map(str.strip, self['implementations'].split(','))
if self['tests']:
self.tests = map(str.strip, self['tests'].split(','))
else:
self.tests = None
self.catch_up_pause = float(self['catch-up-pause'])
### Main flow control:
def main(args):
config = FuseTestsOptions()
config.parseOptions(args[1:])
target = 'all'
if len(args) > 1:
target = args.pop(1)
test_type = config['test-type']
if test_type not in ('both', 'unit', 'system'):
raise usage.error('test-type %r not supported' % (test_type,))
if test_type in ('both', 'unit'):
run_unit_tests([args[0]])
if test_type in ('both', 'system'):
return run_system_test(config)
def run_unit_tests(argv):
print 'Running Unit Tests.'
try:
unittest.main(argv=argv)
except SystemExit, se:
pass
print 'Unit Tests complete.\n'
def run_system_test(config):
return SystemTest(config).run()
def drepr(obj):
r = repr(obj)
if len(r) > 200:
return '%s ... %s [%d]' % (r[:100], r[-100:], len(r))
else:
return r
### System Testing:
class SystemTest (object):
def __init__(self, config):
self.config = config
# These members represent test state:
self.cliexec = None
self.testroot = None
# This test state is specific to the first client:
self.port = 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 '\n*** Setting up system tests.'
try:
results = self.init_cli_layer()
print '\n*** System Tests complete:'
total_failures = todo_failures = 0
for result in results:
impl_name, failures, total = result
if implementations[impl_name].get('todo'):
todo_failures += failures
else:
total_failures += failures
print 'Implementation %s: %d failed out of %d.' % result
if total_failures:
print '%s total failures, %s todo' % (total_failures, todo_failures)
return 1
else:
return 0
except SetupFailure, sfail:
print
print sfail
print '\n*** System Tests were not successfully completed.'
return 1
def maybe_wait(self, msg='waiting', or_if_webopen=False):
if self.config['debug-wait'] or or_if_webopen and self.config['web-open']:
print msg
raw_input()
def maybe_webopen(self, where=None):
if self.config['web-open']:
import webbrowser
url = self.weburl
if where is not None:
url += urllib.quote(where)
webbrowser.open(url)
def maybe_pause(self):
time.sleep(self.config.catch_up_pause)
def init_cli_layer(self):
'''This layer finds the appropriate tahoe executable.'''
#self.cliexec = os.path.join('.', 'bin', 'tahoe')
self.cliexec = self.config['path-to-tahoe']
version = self.run_tahoe('--version')
print 'Using %r with version:\n%s' % (self.cliexec, version.rstrip())
return self.create_testroot_layer()
def create_testroot_layer(self):
print 'Creating test base directory.'
#self.testroot = tempfile.mkdtemp(prefix='tahoe_fuse_test_')
#self.testroot = tempfile.mkdtemp(prefix='tahoe_fuse_test_', dir='/tmp/')
tmpdir = self.config['tmp-dir']
if tmpdir:
self.testroot = tempfile.mkdtemp(prefix='tahoe_fuse_test_', dir=tmpdir)
else:
self.testroot = tempfile.mkdtemp(prefix='tahoe_fuse_test_')
try:
return self.launch_introducer_layer()
finally:
if not self.config['no-cleanup']:
print 'Cleaning up test root directory.'
try:
shutil.rmtree(self.testroot)
except Exception, e:
print 'Exception removing test root directory: %r' % (self.testroot, )
print 'Ignoring cleanup exception: %r' % (e,)
else:
print 'Leaving test root directory: %r' % (self.testroot, )
def launch_introducer_layer(self):
print 'Launching introducer.'
introbase = os.path.join(self.testroot, 'introducer')
# NOTE: We assume if tahoe exits with non-zero status, no separate
# tahoe child process is still running.
createoutput = self.run_tahoe('create-introducer', '--basedir', introbase)
self.check_tahoe_output(createoutput, ExpectedCreationOutput, introbase)
startoutput = self.run_tahoe('start', '--basedir', introbase)
try:
self.check_tahoe_output(startoutput, ExpectedStartOutput, introbase)
return self.launch_clients_layer(introbase)
finally:
print 'Stopping introducer node.'
self.stop_node(introbase)
TotalClientsNeeded = 3
def launch_clients_layer(self, introbase, clientnum = 0):
if clientnum >= self.TotalClientsNeeded:
self.maybe_wait('waiting (launched clients)')
ret = self.create_test_dirnode_layer()
self.maybe_wait('waiting (ran tests)', or_if_webopen=True)
return ret
tmpl = 'Launching client %d of %d.'
print tmpl % (clientnum,
self.TotalClientsNeeded)
base = os.path.join(self.testroot, 'client_%d' % (clientnum,))
output = self.run_tahoe('create-client', '--basedir', base)
self.check_tahoe_output(output, ExpectedCreationOutput, base)
webportpath = os.path.join(base, 'webport')
if clientnum == 0:
# The first client is special:
self.clientbase = base
self.port = random.randrange(1024, 2**15)
f = open(webportpath, 'w')
f.write('tcp:%d:interface=127.0.0.1\n' % self.port)
f.close()
self.weburl = "http://127.0.0.1:%d/" % (self.port,)
print self.weburl
else:
if os.path.exists(webportpath):
os.remove(webportpath)
introfurl = os.path.join(introbase, 'introducer.furl')
self.polling_operation(lambda : os.path.isfile(introfurl),
'introducer.furl creation')
shutil.copy(introfurl, base)
# NOTE: We assume if tahoe exist with non-zero status, no separate
# tahoe child process is still running.
startoutput = self.run_tahoe('start', '--basedir', base)
try:
self.check_tahoe_output(startoutput, ExpectedStartOutput, base)
return self.launch_clients_layer(introbase, clientnum+1)
finally:
print 'Stopping client node %d.' % (clientnum,)
self.stop_node(base)
def create_test_dirnode_layer(self):
print 'Creating test dirnode.'
cap = self.create_dirnode()
f = open(os.path.join(self.clientbase, 'private', 'root_dir.cap'), 'w')
f.write(cap)
f.close()
return self.mount_fuse_layer(cap)
def mount_fuse_layer(self, root_uri):
mpbase = os.path.join(self.testroot, 'mountpoint')
os.mkdir(mpbase)
results = []
if self.config['debug-wait']:
ImplProcessManager.debug_wait = True
#for name, kwargs in implementations.items():
for name in self.config.implementations:
kwargs = implementations[name]
#print 'instantiating %s: %r' % (name, kwargs)
implprocmgr = ImplProcessManager(name, **kwargs)
print '\n*** Testing impl: %r' % (implprocmgr.name)
implprocmgr.configure(self.clientbase, mpbase)
implprocmgr.mount()
try:
failures, total = self.run_test_layer(root_uri, implprocmgr)
result = (implprocmgr.name, failures, total)
tmpl = '\n*** Test Results implementation %s: %d failed out of %d.'
print tmpl % result
results.append(result)
finally:
implprocmgr.umount()
return results
def run_test_layer(self, root_uri, iman):
self.maybe_webopen('uri/'+root_uri)
failures = 0
testnum = 0
numtests = 0
if self.config.tests:
tests = self.config.tests
else:
tests = list(set(self.config.suites).intersection(set(iman.suites)))
self.maybe_wait('waiting (about to run tests)')
for test in tests:
testnames = [n for n in sorted(dir(self)) if n.startswith('test_'+test)]
numtests += len(testnames)
print 'running %s %r tests' % (len(testnames), test,)
for testname in testnames:
testnum += 1
print '\n*** Running test #%d: %s' % (testnum, testname)
try:
testcap = self.create_dirnode()
dirname = '%s_%s' % (iman.name, testname)
self.attach_node(root_uri, testcap, dirname)
method = getattr(self, testname)
method(testcap, testdir = os.path.join(iman.mountpath, dirname))
print 'Test succeeded.'
except TestFailure, f:
print f
#print traceback.format_exc()
failures += 1
except:
print 'Error in test code... Cleaning up.'
raise
return (failures, numtests)
# Tests:
def test_read_directory_existence(self, testcap, testdir):
if not wrap_os_error(os.path.isdir, testdir):
raise TestFailure('Attached test directory not found: %r', testdir)
def test_read_empty_directory_listing(self, testcap, testdir):
listing = wrap_os_error(os.listdir, testdir)
if listing:
raise TestFailure('Expected empty directory, found: %r', listing)
def test_read_directory_listing(self, testcap, testdir):
names = []
filesizes = {}
for i in range(3):
fname = 'file_%d' % (i,)
names.append(fname)
body = 'Hello World #%d!' % (i,)
filesizes[fname] = len(body)
cap = self.webapi_call('PUT', '/uri', body)
self.attach_node(testcap, cap, fname)
dname = 'dir_%d' % (i,)
names.append(dname)
cap = self.create_dirnode()
self.attach_node(testcap, cap, dname)
names.sort()
listing = wrap_os_error(os.listdir, testdir)
listing.sort()
if listing != names:
tmpl = 'Expected directory list containing %r but fuse gave %r'
raise TestFailure(tmpl, names, listing)
for file, size in filesizes.items():
st = wrap_os_error(os.stat, os.path.join(testdir, file))
if st.st_size != size:
tmpl = 'Expected %r size of %r but fuse returned %r'
raise TestFailure(tmpl, file, size, st.st_size)
def test_read_file_contents(self, testcap, testdir):
name = 'hw.txt'
body = 'Hello World!'
cap = self.webapi_call('PUT', '/uri', body)
self.attach_node(testcap, cap, name)
path = os.path.join(testdir, name)
try:
found = open(path, 'r').read()
except Exception, err:
tmpl = 'Could not read file contents of %r: %r'
raise TestFailure(tmpl, path, err)
if found != body:
tmpl = 'Expected file contents %r but found %r'
raise TestFailure(tmpl, body, found)
def test_read_in_random_order(self, testcap, testdir):
sz = 2**20
bs = 2**10
assert(sz % bs == 0)
name = 'random_read_order'
body = os.urandom(sz)
cap = self.webapi_call('PUT', '/uri', body)
self.attach_node(testcap, cap, name)
# XXX this should also do a test where sz%bs != 0, so that it correctly tests
# the edge case where the last read is a 'short' block
path = os.path.join(testdir, name)
try:
fsize = os.path.getsize(path)
if fsize != len(body):
tmpl = 'Expected file size %s but found %s'
raise TestFailure(tmpl, len(body), fsize)
except Exception, err:
tmpl = 'Could not read file size for %r: %r'
raise TestFailure(tmpl, path, err)
try:
f = open(path, 'r')
posns = range(0,sz,bs)
random.shuffle(posns)
data = [None] * (sz/bs)
for p in posns:
f.seek(p)
data[p/bs] = f.read(bs)
found = ''.join(data)
except Exception, err:
tmpl = 'Could not read file %r: %r'
raise TestFailure(tmpl, path, err)
if found != body:
tmpl = 'Expected file contents %s but found %s'
raise TestFailure(tmpl, drepr(body), drepr(found))
def get_file(self, dircap, path):
body = self.webapi_call('GET', '/uri/%s/%s' % (dircap, path))
return body
def test_write_tiny_file(self, testcap, testdir):
self._write_test_linear(testcap, testdir, name='tiny.junk', bs=2**9, sz=2**9)
def test_write_linear_small_writes(self, testcap, testdir):
self._write_test_linear(testcap, testdir, name='large_linear.junk', bs=2**9, sz=2**20)
def test_write_linear_large_writes(self, testcap, testdir):
# at least on the mac, large io block sizes are reduced to 64k writes through fuse
self._write_test_linear(testcap, testdir, name='small_linear.junk', bs=2**18, sz=2**20)
def _write_test_linear(self, testcap, testdir, name, bs, sz):
body = os.urandom(sz)
try:
path = os.path.join(testdir, name)
f = file(path, 'w')
except Exception, err:
tmpl = 'Could not open file for write at %r: %r'
raise TestFailure(tmpl, path, err)
try:
for posn in range(0,sz,bs):
f.write(body[posn:posn+bs])
f.close()
except Exception, err:
tmpl = 'Could not write to file %r: %r'
raise TestFailure(tmpl, path, err)
self.maybe_pause()
self._check_write(testcap, name, body)
def _check_write(self, testcap, name, expected_body):
uploaded_body = self.get_file(testcap, name)
if uploaded_body != expected_body:
tmpl = 'Expected file contents %s but found %s'
raise TestFailure(tmpl, drepr(expected_body), drepr(uploaded_body))
def test_write_overlapping_small_writes(self, testcap, testdir):
self._write_test_overlap(testcap, testdir, name='large_overlap', bs=2**9, sz=2**20)
def test_write_overlapping_large_writes(self, testcap, testdir):
self._write_test_overlap(testcap, testdir, name='small_overlap', bs=2**18, sz=2**20)
def _write_test_overlap(self, testcap, testdir, name, bs, sz):
body = os.urandom(sz)
try:
path = os.path.join(testdir, name)
f = file(path, 'w')
except Exception, err:
tmpl = 'Could not open file for write at %r: %r'
raise TestFailure(tmpl, path, err)
try:
for posn in range(0,sz,bs):
start = max(0, posn-bs)
end = min(sz, posn+bs)
f.seek(start)
f.write(body[start:end])
f.close()
except Exception, err:
tmpl = 'Could not write to file %r: %r'
raise TestFailure(tmpl, path, err)
self.maybe_pause()
self._check_write(testcap, name, body)
def test_write_random_scatter(self, testcap, testdir):
sz = 2**20
name = 'random_scatter'
body = os.urandom(sz)
def rsize(sz=sz):
return min(int(random.paretovariate(.25)), sz/12)
# first chop up whole file into random sized chunks
slices = []
posn = 0
while posn < sz:
size = rsize()
slices.append( (posn, body[posn:posn+size]) )
posn += size
random.shuffle(slices) # and randomise their order
try:
path = os.path.join(testdir, name)
f = file(path, 'w')
except Exception, err:
tmpl = 'Could not open file for write at %r: %r'
raise TestFailure(tmpl, path, err)
try:
# write all slices: we hence know entire file is ultimately written
# write random excerpts: this provides for mixed and varied overlaps
for posn,slice in slices:
f.seek(posn)
f.write(slice)
rposn = random.randint(0,sz)
f.seek(rposn)
f.write(body[rposn:rposn+rsize()])
f.close()
except Exception, err:
tmpl = 'Could not write to file %r: %r'
raise TestFailure(tmpl, path, err)
self.maybe_pause()
self._check_write(testcap, name, body)
def test_write_partial_overwrite(self, testcap, testdir):
name = 'partial_overwrite'
body = '_'*132
overwrite = '^'*8
position = 26
def write_file(path, mode, contents, position=None):
try:
f = file(path, mode)
if position is not None:
f.seek(position)
f.write(contents)
f.close()
except Exception, err:
tmpl = 'Could not write to file %r: %r'
raise TestFailure(tmpl, path, err)
def read_file(path):
try:
f = file(path, 'rb')
contents = f.read()
f.close()
except Exception, err:
tmpl = 'Could not read file %r: %r'
raise TestFailure(tmpl, path, err)
return contents
path = os.path.join(testdir, name)
#write_file(path, 'w', body)
cap = self.webapi_call('PUT', '/uri', body)
self.attach_node(testcap, cap, name)
self.maybe_pause()
contents = read_file(path)
if contents != body:
raise TestFailure('File contents mismatch (%r) %r v.s. %r', path, contents, body)
write_file(path, 'r+', overwrite, position)
contents = read_file(path)
expected = body[:position] + overwrite + body[position+len(overwrite):]
if contents != expected:
raise TestFailure('File contents mismatch (%r) %r v.s. %r', path, contents, expected)
# 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 SetupFailure(tmpl,
self.cliexec,
realargs,
status,
output)
return output
def check_tahoe_output(self, output, expected, expdir):
ignorable_lines = map(re.compile, [
'.*site-packages/zope\.interface.*\.egg/zope/__init__.py:3: UserWarning: Module twisted was already imported from .*egg is being added to sys.path',
' import pkg_resources',
])
def ignore_line(line):
for ignorable_line in ignorable_lines:
if ignorable_line.match(line):
return True
else:
return False
output = '\n'.join( [ line
for line in output.split('\n')+['']
#if line not in ignorable_lines ] )
if not ignore_line(line) ] )
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'
self.warn(tmpl, expected, output)
elif expdir != m.group('path'):
tmpl = 'The output of tahoe refers to an unexpected directory:\n'
tmpl += 'Expected directory: %r\n'
tmpl += 'Actual directory: %r\n'
self.warn(tmpl, expdir, m.group(1))
def stop_node(self, basedir):
try:
self.run_tahoe('stop', '--basedir', basedir)
except Exception, e:
print 'Failed to stop tahoe node.'
print 'Ignoring cleanup exception:'
# Indent the exception description:
desc = str(e).rstrip()
print ' ' + desc.replace('\n', '\n ')
def webapi_call(self, method, path, body=None, **options):
if options:
path = path + '?' + ('&'.join(['%s=%s' % kv for kv in options.items()]))
conn = httplib.HTTPConnection('127.0.0.1', self.port)
conn.request(method, path, body = body)
resp = conn.getresponse()
if resp.status != 200:
tmpl = 'A webapi operation failed.\n'
tmpl += 'Request: %r %r\n'
tmpl += 'Body:\n%s\n'
tmpl += 'Response:\nStatus %r\nBody:\n%s'
raise SetupFailure(tmpl,
method, path,
body or '',
resp.status, body)
return resp.read()
def create_dirnode(self):
return self.webapi_call('PUT', '/uri', t='mkdir').strip()
def attach_node(self, dircap, childcap, childname):
body = self.webapi_call('PUT',
'/uri/%s/%s' % (dircap, childname),
body = childcap,
t = 'uri',
replace = 'false')
assert body.strip() == childcap, `body, dircap, childcap, childname`
def polling_operation(self, operation, polldesc, timeout = 10.0, pollinterval = 0.2):
totaltime = timeout # Fudging for edge-case SetupFailure description...
totalattempts = int(timeout / pollinterval)
starttime = time.time()
for attempt in range(totalattempts):
opstart = time.time()
try:
result = operation()
except KeyboardInterrupt, e:
raise
except Exception, e:
result = False
totaltime = time.time() - starttime
if result is not False:
#tmpl = '(Polling took over %.2f seconds.)'
#print tmpl % (totaltime,)
return result
elif totaltime > timeout:
break
else:
opdelay = time.time() - opstart
realinterval = max(0., pollinterval - opdelay)
#tmpl = '(Poll attempt %d failed after %.2f seconds, sleeping %.2f seconds.)'
#print tmpl % (attempt+1, opdelay, realinterval)
time.sleep(realinterval)
tmpl = 'Timeout while polling for: %s\n'
tmpl += 'Waited %.2f seconds (%d polls).'
raise SetupFailure(tmpl, polldesc, totaltime, attempt+1)
def warn(self, tmpl, *args):
print ('Test Warning: ' + tmpl) % args
# SystemTest Exceptions:
class Failure (Exception):
def __init__(self, tmpl, *args):
msg = self.Prefix + (tmpl % args)
Exception.__init__(self, msg)
class SetupFailure (Failure):
Prefix = 'Setup Failure - The test framework encountered an error:\n'
class TestFailure (Failure):
Prefix = 'TestFailure: '
### Unit Tests:
class Impl_A_UnitTests (unittest.TestCase):
'''Tests small stand-alone functions.'''
def test_canonicalize_cap(self):
iopairs = [('http://127.0.0.1:8123/uri/URI:DIR2:yar9nnzsho6czczieeesc65sry:upp1pmypwxits3w9izkszgo1zbdnsyk3nm6h7e19s7os7s6yhh9y',
'URI:DIR2:yar9nnzsho6czczieeesc65sry:upp1pmypwxits3w9izkszgo1zbdnsyk3nm6h7e19s7os7s6yhh9y'),
('http://127.0.0.1:8123/uri/URI%3ACHK%3Ak7ktp1qr7szmt98s1y3ha61d9w%3A8tiy8drttp65u79pjn7hs31po83e514zifdejidyeo1ee8nsqfyy%3A3%3A12%3A242?filename=welcome.html',
'URI:CHK:k7ktp1qr7szmt98s1y3ha61d9w:8tiy8drttp65u79pjn7hs31po83e514zifdejidyeo1ee8nsqfyy:3:12:242?filename=welcome.html')]
for input, output in iopairs:
result = impl_a.canonicalize_cap(input)
self.failUnlessEqual(output, result, 'input == %r' % (input,))
### Misc:
class ImplProcessManager(object):
debug_wait = False
def __init__(self, name, module, mount_args, mount_wait, suites, todo=False):
self.name = name
self.module = module
self.script = module.__file__
self.mount_args = mount_args
self.mount_wait = mount_wait
self.suites = suites
self.todo = todo
def maybe_wait(self, msg='waiting'):
if self.debug_wait:
print msg
raw_input()
def configure(self, client_nodedir, mountpoint):
self.client_nodedir = client_nodedir
self.mountpath = os.path.join(mountpoint, self.name)
os.mkdir(self.mountpath)
def mount(self):
print 'Mounting implementation: %s (%s)' % (self.name, self.script)
rootdirfile = os.path.join(self.client_nodedir, 'private', 'root_dir.cap')
root_uri = file(rootdirfile, 'r').read().strip()
fields = {'mountpath': self.mountpath,
'nodedir': self.client_nodedir,
'root-uri': root_uri,
}
args = ['python', self.script] + [ arg%fields for arg in self.mount_args ]
print ' '.join(args)
self.maybe_wait('waiting (about to launch fuse)')
if self.mount_wait:
exitcode, output = gather_output(args)
if exitcode != 0 or output:
tmpl = '%r failed to launch:\n'
tmpl += 'Exit Status: %r\n'
tmpl += 'Output:\n%s\n'
raise SetupFailure(tmpl, self.script, exitcode, output)
else:
self.proc = subprocess.Popen(args)
def umount(self):
print 'Unmounting implementation: %s' % (self.name,)
args = UNMOUNT_CMD + [self.mountpath]
print args
self.maybe_wait('waiting (unmount)')
#print os.system('ls -l '+self.mountpath)
ec, out = gather_output(args)
if ec != 0 or out:
tmpl = '%r failed to unmount:\n' % (' '.join(UNMOUNT_CMD),)
tmpl += 'Arguments: %r\n'
tmpl += 'Exit Status: %r\n'
tmpl += 'Output:\n%s\n'
raise SetupFailure(tmpl, args, ec, out)
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)
def wrap_os_error(meth, *args):
try:
return meth(*args)
except os.error, e:
raise TestFailure('%s', e)
ExpectedCreationOutput = r'(introducer|client) created in (?P<path>.*?)\n'
ExpectedStartOutput = r'STARTING (?P<path>.*?)\n(introducer|client) node probably started'
if __name__ == '__main__':
sys.exit(main(sys.argv))