From: Justin Seyster Date: Wed, 1 Sep 2010 19:34:56 +0000 (-0400) Subject: Test script no longer automatically generates hooks. X-Git-Tag: release-v1.0~49^2~10 X-Git-Url: https://git.fsl.cs.sunysb.edu/?a=commitdiff_plain;h=5afe770fd43f5c2222a3629c15ba106af70196bf;p=interaspect.git Test script no longer automatically generates hooks. Instead, it is necessary to supply a user-written hooks in a separate source file. This makes the test case system much simpler and more flexible. --- diff --git a/test/run-testcase.py b/test/run-testcase.py index bf65276..2a5576a 100755 --- a/test/run-testcase.py +++ b/test/run-testcase.py @@ -50,14 +50,17 @@ class MisplacedElement(XMLDataException): def getMessage(self): return "Element <" + self.element + "> outside of <" + self.parent + ">" -class BadName(XMLDataException): - def __init__(self, attr, val): - self.attr = attr - self.val = val +class MisplacedCData(XMLDataException): + def getMessage(self): + return "Character data outside of element" + +class DuplicateTag(XMLDataException): + def __init__(self, element, parent): + self.element = element + self.parent = parent def getMessage(self): - return ("Value of " + self.attr + " not a legal C function name: " - + self.val) + return "Only one <" + self.element + "> allowed in <" + self.parent + ">" class BadId(XMLDataException): def __init__(self, id_name, val): @@ -68,34 +71,11 @@ class BadId(XMLDataException): return ("Invalid reference: No " + self.id_name + " with id " + self.val) -class DuplicateHook(XMLDataException): - def __init__(self, run_name, hook_name): - self.run_name = run_name - self.hook_name = hook_name - - def getMessage(self): - return (self.run_name + ": Run contains two hooks with same name, " - + self.hook_name + ", but different arguments") - -# A plug-in includes a source file and the descriptions of each hook -# that the plug-in might add a call for. +# A plug-in includes a source file and id. class Plugin: def __init__(self, plugin_id, source): self.plugin_id = plugin_id self.source = source - self.hooks = {} - -# A hook description comprises the C function name for the hook and -# the types for all its arguments. -class Hook: - def __init__(self, name): - self.name = name - self.arg_list = [] - -class Arg: - def __init__(self, arg_id, arg_type): - self.arg_id = arg_id - self.arg_type = arg_type # A run has a target source file along with a list of plug-ins that # should be used when compiling the target and the hooks necessary for @@ -103,76 +83,22 @@ class Arg: # output that the target is supposed to produce (because of the # plug-in) when it runs. class Run: - def __init__(self, name, target_source): - self.name = name; - self.target_source = target_source; + def __init__(self, name, target_source, hooks_source): + self.name = name + self.target_source = target_source + self.hooks_source = hooks_source self.plugin_list = [] - self.hooks = {} - self.output = [] - -# A Value is one entry in the output list for a Run object. -class Value: - def __init__(self, val_type, val): - self.val_type = val_type - self.val = val - -# Called for two hooks with the same name, return True if they have -# _exactly_ the same arguments. -def hooksMatch(hook1, hook2): - assert hook1.name == hook2.name - - if len(hook1.arg_list) != len(hook2.arg_list): - return False - - for i in range(len(hook1.arg_list)): - arg1 = hook1.arg_list[i] - arg2 = hook2.arg_list[i] - if arg1.arg_id != arg2.arg_id or arg1.arg_type != arg2.arg_type: - return False - - return True + self.output = "" class TestcaseHandler(handler.ContentHandler): plugins = {} - current_plugin = None - current_hook = None run_list = [] current_run = None current_call_hook = None - current_value_arg_id = None - current_value_cdata = "" - - def isAllowedInC(self, token): - if not re.match('[a-zA-Z_][0-9a-zA-Z_]*', token): - return False - elif token == 'if': - return False - elif token == 'else': - return False - elif token == 'for': - return False - elif token == 'do': - return False - elif token == 'while': - return False - elif token == 'void': - return False - elif token == 'unsigned': - return False - elif token == 'short': - return False - elif token == 'long': - return False - elif token == 'int': - return False - elif token == 'float': - return False - elif token == 'double': - return False - else: - return True + current_cdata = "" + in_output = False def startTestcase(self, attrs): self.name = attrs.get('name') @@ -188,50 +114,9 @@ class TestcaseHandler(handler.ContentHandler): if source is None: raise MissingAttr("plugin", "source") - self.current_plugin = Plugin(plugin_id, source) - - def endPlugin(self): - self.plugins[self.current_plugin.plugin_id] = (self.current_plugin) - self.current_plugin = None - - def startHook(self, attrs): - if self.current_hook is not None: - raise MisplacedElement("hook", "plugin"); - - name = attrs.get('name') - if name is None: - raise MissingAttr("hook", "name") - elif not self.isAllowedInC(name): - raise BadName("name", name) - - self.current_hook = Hook(name) - - def endHook(self): - if self.current_plugin is None: - raise MisplacedElement("hook", "plugin") - - self.current_plugin.hooks[self.current_hook.name] = self.current_hook - self.current_hook = None - - def startArg(self, attrs): - if self.current_hook is None: - raise MisplacedElement("arg", "hook") - - arg_id = attrs.get('id') - if arg_id is None: - raise MissingAttr("arg", "id") - - arg_type = attrs.get('type') - if arg_type is None: - raise MissingAttr("arg", "type") - - arg = Arg(arg_id, arg_type) - self.current_hook.arg_list.append(arg) + self.plugins[plugin_id] = Plugin(plugin_id, source) def startRun(self, attrs): - if self.current_plugin is not None: - raise MisplacedElement("run", "testcase") - run_name = attrs.get('name') if run_name is None: raise MissingAttr("run", "name") @@ -240,7 +125,11 @@ class TestcaseHandler(handler.ContentHandler): if target_source is None: raise MissingAttr("run", "target") - self.current_run = Run(run_name, target_source) + hooks_source = attrs.get('hooks') + if hooks_source is None: + raise MissingAttr("run", "hooks") + + self.current_run = Run(run_name, target_source, hooks_source) def endRun(self): self.run_list.append(self.current_run) @@ -259,80 +148,25 @@ class TestcaseHandler(handler.ContentHandler): except KeyError as e: raise BadId("plugin", plugin_id) - # Add all of this plug-in's hooks to the run. - for name, hook in plugin.hooks.iteritems(): - dup_hook = None - try: - dup_hook = self.current_run.hooks[name] - except KeyError as e: - pass - - if dup_hook is None: - # We don't have any hooks with this name. - self.current_run.hooks[name] = hook - else: - # There is already a hook with this name. Make sure - # it matches. - if not hooksMatch(hook, dup_hook): - raise DuplicateHook(self.current_run.name, name) - # And the plug-in itself! self.current_run.plugin_list.append(plugin) - def startCall(self, attrs): - if self.current_run is None or self.current_call_hook is not None: - raise MisplacedElement("call", "run") + def startOutput(self, attrs): + if (self.current_run is None): + raise MisplacedElement("output", "run") - hook_name = attrs.get('name') - if hook_name is None: - raise MissingAttr("call", "name") + if (self.current_run.output != ""): + raise DuplicateTag("output", "run") - try: - hook = self.current_run.hooks[hook_name] - except KeyError as e: - raise BadId("hook", hook_name) - - self.current_call_hook = hook - - output_val = Value('Hook', hook_name) - self.current_run.output.append(output_val) - - def endCall(self): - self.current_call_hook = None - - def startValue(self, attrs): - if (self.current_call_hook is None - or self.current_value_arg_id is not None): - raise MisplacedElement("value", "call") - - arg_id = attrs.get('id') - if arg_id is None: - raise MissingAttr("value", "id") - - # We can't examine the cdata until the endValue event. Stash - # the argument name until then. - self.current_value_arg_id = arg_id - self.current_value_cdata = "" - - def endValue(self): - assert self.current_call_hook is not None - assert self.current_value_arg_id is not None + # We're only interested in the cdata, which we can't get until + # the endOutput event. + self.current_cdata = "" + self.in_output = True - # Find the arg with the given arg id. These arguments are not - # stored in an associative array because their order is - # important. These lists are way too small to justify the - # overhead of a separate index. - arg = None - for arg_it in self.current_call_hook.arg_list: - if self.current_value_arg_id == arg_it.arg_id: - arg = arg_it - if arg is None: - raise BadId("arg", self.current_value_arg_id) - - new_value = Value(arg.arg_type, self.current_value_cdata) - self.current_run.output.append(new_value) - - self.current_value_arg_id = None + def endOutput(self): + assert self.current_run is not None + self.current_run.output = self.current_cdata + self.in_output = False # Parsing with SAX is simple but tedious. There's a start # function for each tag and an end function for some tags. They @@ -343,62 +177,24 @@ class TestcaseHandler(handler.ContentHandler): self.startTestcase(attrs) elif name == 'plugin': self.startPlugin(attrs) - elif name == 'hook': - self.startHook(attrs) - elif name == 'arg': - self.startArg(attrs) elif name == 'run': self.startRun(attrs) elif name == 'using': self.startUsing(attrs) - elif name == 'call': - self.startCall(attrs) - elif name == 'value': - self.startValue(attrs) + elif name == 'output': + self.startOutput(attrs) def endElement(self, name): - if name == 'plugin': - self.endPlugin() - elif name == 'hook': - self.endHook() - elif name == 'run': + if name == 'run': self.endRun() - elif name == 'call': - self.endCall() - elif name == 'value': - self.endValue() + elif name == 'output': + self.endOutput() def characters(self, chars): - if self.current_value_arg_id is not None: - self.current_value_cdata += chars - -def getCheckFormat(c_type): - c_type = c_type.strip() - if re.match(r".*\bchar\s+\*", c_type): - return r"string: %s\n" - if re.match(r".*\*$", c_type): - return r"pointer: %p\n" - elif c_type == 'int': - return r"int: %d\n" - else: - return None + if not self.in_output and re.search(r"\S", chars): + raise MisplacedCData() -# Print the C prototype and function body for the given plug-in hook -# to the given stream. -def printHook(stream, hook): - params = ['{0:s} arg{1:d}'.format(hook.arg_list[i].arg_type, i) for i in - range(len(hook.arg_list))] - param_text = ', '.join(params) - - stream.write('void {0:s}({1:s})\n'.format(hook.name, param_text)) - stream.write('{\n'); - stream.write(' check_printf("hook: {0:s}\\n");\n'.format(hook.name)) - for i in range(len(hook.arg_list)): - arg = hook.arg_list[i] - check_format = getCheckFormat(arg.arg_type) - if check_format is not None: - stream.write(' check_printf("{0:s}", arg{1:d});\n'.format(check_format, i)) - stream.write('}\n'); + self.current_cdata += chars # Run GCC with the given arguments. On failure, print an appropriate # error and return False. @@ -462,11 +258,11 @@ def compilePlugin(working_dir, plugin_id, plugin_base_name, plugin_source): return plugin_lib_name -# Compile the testcase instrumentation target, along with -# auto-generated hook functions. Several compilation files get -# created in the given working directory. It is the caller's -# responsibility to delete these files. No files will be left over in -# other directories, though. +# Compile the testcase instrumentation target, along with supplied +# hook functions. Several compilation files get created in the given +# working directory. It is the caller's responsibility to delete +# these files. No files will be left over in other directories, +# though. # # working_dir: A temporary directory to store intermediate files and # the resulting executable. @@ -474,25 +270,20 @@ def compilePlugin(working_dir, plugin_id, plugin_base_name, plugin_source): # target_source: The C source file for the target program (the program # that we intend to instrument with the test plug-ins). # +# hooks_source: The C source file with the advice functions. This +# source is compiled without any plug-ins. +# # plugin_libs: A list of .so plug-in files that will be used to # compile the target program. # # On success, compileTestcase returns the path to the final test # executable (which will be in working_dir). On failure, the return # value is None. -def compileTestcase(working_dir, target_source, plugin_libs, hooks): - # Create a C file with the necessary plug-in hooks and compile it. - hook_file_name = working_dir + '/hooks.c' - hook_file = open(hook_file_name, 'w'); - hook_file.write('#include "test-driver.h"\n') - for name, hook in hooks.iteritems(): - printHook(hook_file, hook) - hook_file.close() - +def compileTestcase(working_dir, target_source, hooks_source, plugin_libs): test_include = '-I{0:s}/test'.format(gcc_interaspect_src) hook_o_file = working_dir + '/hooks.o' cmd_args = ['-Wall', '-Werror', test_include, '-c', '-o', hook_o_file, - hook_file_name] + hooks_source] result = runGCC(cmd_args, "Fatal -- Failed to compile plug-in hooks:") if not result: return None @@ -542,8 +333,8 @@ def doRun(run): return False plugin_libs.append(plugin_lib_name) - test_executable = compileTestcase(tmp_dir, run.target_source, plugin_libs, - run.hooks) + test_executable = compileTestcase(tmp_dir, run.target_source, + run.hooks_source, plugin_libs) if test_executable is not None: test_proc = subprocess.Popen([test_executable]) diff --git a/test/testcase.dtd b/test/testcase.dtd index 324d786..0a1741e 100644 --- a/test/testcase.dtd +++ b/test/testcase.dtd @@ -1,11 +1,8 @@ - - - + + - - - + - - - -