return ("Test program printed unexpected additional output:\n"
+ self.extra)
+class NoAutoheader(TestProgramException):
+ def getMessage(self):
+ return "Test plug-in did not generate an automatic header file."
+
# A plug-in includes a source file and id.
class Plugin:
def __init__(self, plugin_id, source):
self.hooks_source = hooks_source
self.plugin_list = []
self.output = ""
+ self.prototypes = None
class TestcaseHandler(handler.ContentHandler):
plugins = {}
self.current_run.output = self.current_cdata
self.in_output = False
+ def startPrototypes(self, attrs):
+ if (self.current_run is None):
+ raise MisplacedElement("prototypes", "run")
+
+ if (self.current_run.prototypes is not None):
+ raise DuplicateTag("prototypes", "run")
+
+ # We're only interested in the cdata, which we can't get until
+ # the endPrototypes event.
+ self.current_cdata = ""
+ self.in_output = True
+
+ def endPrototypes(self):
+ assert self.current_run is not None
+ self.current_run.prototypes = 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
# just stash the data they find into a relevant data structure
self.startUsing(attrs)
elif name == 'output':
self.startOutput(attrs)
+ elif name == 'prototypes':
+ self.startPrototypes(attrs)
def endElement(self, name):
if name == 'run':
self.endRun()
elif name == 'output':
self.endOutput()
+ elif name == 'prototypes':
+ self.endPrototypes()
def characters(self, chars):
if not self.in_output and re.search(r"\S", chars):
# file (which will be in working_dir). On failure, the return value
# is None.
def compilePlugin(working_dir, plugin_id, plugin_base_name, plugin_source):
- plugin_lib_name = '{0:s}/{1:s}.so.1.0.0'.format(working_dir,
- plugin_base_name)
+ plugin_lib_name = '{0:s}/{1:s}.so'.format(working_dir,
+ plugin_base_name)
plugin_source = formatSourceFile(plugin_source)
include_flag = '-I{0:s}/src'.format(gcc_interaspect_src)
# 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.
+# autoheader_file: The file name where InterAspect should generate its
+# header. This name will be passed to the plug-in, which has the
+# option of using it to call aop_write_c_header().
+#
+# plugin_names: An ordered list of the names of plug-ins that will be
+# used when compiling the target plug-in.
+#
+# plugin_libs: A mapping from plug-in names to the actual .so files.
+# There needs to be an entry in this dictionary for each plug-in in
+# plugin_names.
#
# 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, hooks_source, plugin_libs):
+def compileTestcase(working_dir, target_source, hooks_source, autoheader_file,
+ plugin_names, plugin_libs):
hooks_source = formatSourceFile(hooks_source)
test_include = '-I{0:s}/test'.format(gcc_interaspect_src)
hook_o_file = working_dir + '/hooks.o'
# Compile the target itself.
target_source = formatSourceFile(target_source)
target_o_file = working_dir + '/target.o'
- cmd_args = ['-fplugin={0:s}'.format(lib) for lib in plugin_libs]
+ cmd_args = []
+ for name in plugin_names:
+ lib = plugin_libs[name];
+ assert lib is not None
+
+ # An argument specifying the plug-in to GCC.
+ cmd_args += ['-fplugin={0:s}'.format(lib)]
+
+ # An argument specifying the name of the autogenerated header
+ # file to the plug-in.
+ cmd_args += ['-fplugin-arg-{0:s}-header={1:s}'
+ .format(name, autoheader_file)]
+
cmd_args += ['-Wall', '-Werror', '-c', '-o', target_o_file, target_source]
result = runGCC(cmd_args, "Fatal -- Failed to compile target source:")
if not result:
return executable
-# Run the given process (which is the test program) and check that its
-# output matches expected output. If the run fails or if the output
-# does not match, checkRun raises a TestProgramException.
-def checkRun(test_proc, expected_output):
- (actual_output, _) = test_proc.communicate()
-
- if (test_proc.returncode != 0):
- raise ErrorReturnCode()
-
+# Check if the output of the test case matches the expected output.
+# If it does not, raise an exception with an error message explaining
+# the mismatch.
+def checkRun(actual_output, expected_output):
actual_array = actual_output.strip().splitlines()
expected_array = expected_output.strip().splitlines()
if (len(actual_array) > len(expected_array)):
raise ExtraOutput(actual_array[len(expected_array)].strip())
+def checkHeader(autoheader_file, expected_prototypes):
+ header_output = ""
+
+ try:
+ filehandle = None # So close does not crash when open fails.
+ filehandle = open(autoheader_file, 'r')
+
+ # We want to read in all the lines that come after BEGIN
+ # PROTOTYPES until we see the #endif the closes the header
+ # guard..
+ in_prototypes = False
+ for line in filehandle:
+ if (re.search(r"BEGIN PROTOTYPES", line)):
+ in_prototypes = True
+ continue
+ elif (re.search(r"endif", line)):
+ break
+
+ if (in_prototypes):
+ header_output += line
+
+ except IOError as e:
+ sys.stderr.write('{0:s}: {1:s}\n'.format(autoheader_file, e.strerror))
+ raise NoAutoheader()
+ finally:
+ if (filehandle is not None):
+ filehandle.close()
+
+ checkRun(header_output.strip(), expected_prototypes)
+
# Same as doRun but takes a temporary working directory (to place
# compiled objects) as an input.
def doRunInTempDir(run, tmp_dir):
print " Run:", run.name
# Compile all the plug-ins for this test.
- plugin_libs = []
+ plugin_names = []
+ plugin_libs = {}
for i in range(len(run.plugin_list)):
plugin = run.plugin_list[i]
plugin_base_name = 'test_plugin_{0:d}'.format(i + 1)
plugin_base_name, plugin.source)
if plugin_lib_name is None:
return False
- plugin_libs.append(plugin_lib_name)
+ plugin_names.append(plugin_base_name)
+ plugin_libs[plugin_base_name] = plugin_lib_name
+ autoheader_file = tmp_dir + '/autoheader.h'
test_executable = compileTestcase(tmp_dir, run.target_source,
- run.hooks_source, plugin_libs)
+ run.hooks_source, autoheader_file,
+ plugin_names, plugin_libs)
if test_executable is None:
return False
sys.exit(1)
# ... and run it.
+ (test_output, _) = test_proc.communicate()
+ if (test_proc.returncode != 0):
+ raise ErrorReturnCode()
+
+ # Check the output
try:
- checkRun(test_proc, run.output)
+ checkRun(test_output, run.output)
+ if (run.prototypes is not None):
+ checkHeader(autoheader_file, run.prototypes)
except TestProgramException as e:
print e.getMessage()
return False