From 1c73a3d60e0f11eed42c786e573281787de36234 Mon Sep 17 00:00:00 2001 From: Yann Rouillard Date: Tue, 30 Apr 2013 01:40:33 +0200 Subject: [PATCH] added elfdump comparison test --- test/run_elfdump_tests.py | 166 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 166 insertions(+) create mode 100755 test/run_elfdump_tests.py diff --git a/test/run_elfdump_tests.py b/test/run_elfdump_tests.py new file mode 100755 index 0000000..3d5f2e7 --- /dev/null +++ b/test/run_elfdump_tests.py @@ -0,0 +1,166 @@ +#!/usr/bin/env python +#------------------------------------------------------------------------------- +# test/run_elfdump_tests.py +# +# Automatic test runner for elftools & elfdump +# +# Eli Bendersky (eliben@gmail.com) +# Yann Rouillard (yann@pleiades.fr.eu.org) +# This code is in the public domain +#------------------------------------------------------------------------------- +import os, sys +import re +from difflib import SequenceMatcher +from optparse import OptionParser +import logging +import platform +from utils import setup_syspath; setup_syspath() +from utils import run_exe, is_in_rootdir, dump_output_to_temp_files + + +# Create a global logger object +# +testlog = logging.getLogger('run_tests') +testlog.setLevel(logging.DEBUG) +testlog.addHandler(logging.StreamHandler(sys.stdout)) + +ELFDUMP_PATH = '/usr/ccs/bin/elfdump' + +def discover_testfiles(rootdir): + """ Discover test files in the given directory. Yield them one by one. + """ + for filename in os.listdir(rootdir): + name, ext = os.path.splitext(filename) + if ext == '.elf' and 'solaris' in name: + yield os.path.join(rootdir, filename) + + +def run_test_on_file(filename, verbose=False): + """ Runs a test on the given input filename. Return True if all test + runs succeeded. + """ + success = True + testlog.info("Test file '%s'" % filename) + for option in [ + '-y']: + if verbose: testlog.info("..option='%s'" % option) + # stdouts will be a 2-element list: output of elfdump and output + # of scripts/elfdump.py + stdouts = [] + for exe_path in [ELFDUMP_PATH, 'scripts/elfdump.py']: + args = [option, filename] + if verbose: testlog.info("....executing: '%s %s'" % ( + exe_path, ' '.join(args))) + rc, stdout = run_exe(exe_path, args) + if rc != 0: + testlog.error("@@ aborting - '%s' returned '%s'" % (exe_path, rc)) + return False + stdouts.append(stdout) + if verbose: testlog.info('....comparing output...') + rc, errmsg = compare_output(*stdouts) + if rc: + if verbose: testlog.info('.......................SUCCESS') + else: + success = False + testlog.info('.......................FAIL') + testlog.info('....for option "%s"' % option) + testlog.info('....Output #1 is elfdump, Output #2 is pyelftools') + testlog.info('@@ ' + errmsg) + dump_output_to_temp_files(testlog, *stdouts) + return success + + +def compare_output(s1, s2): + """ Compare stdout strings s1 and s2. + s1 is from elfdump, s2 from elftools elfdump.py + Return pair success, errmsg. If comparison succeeds, success is True + and errmsg is empty. Otherwise success is False and errmsg holds a + description of the mismatch. + + Note: this function contains some rather horrible hacks to ignore + differences which are not important for the verification of pyelftools. + This is due to some intricacies of binutils's elfdump which pyelftools + doesn't currently implement, or silly inconsistencies in the output of + elfdump, which I was reluctant to replicate. + Read the documentation for more details. + """ + def prepare_lines(s): + return [line for line in s.lower().splitlines() if line.strip() != ''] + def filter_elfdump_lines(lines): + filter_out = False + for line in lines: + if not filter_out: + yield line + + lines1 = prepare_lines(s1) + lines2 = prepare_lines(s2) + + lines1 = list(filter_elfdump_lines(lines1)) + + flag_after_symtable = False + + if len(lines1) != len(lines2): + return False, 'Number of lines different: %s vs %s' % ( + len(lines1), len(lines2)) + + for i in range(len(lines1)): + if 'symbol table' in lines1[i]: + flag_after_symtable = True + + # Compare ignoring whitespace + lines1_parts = lines1[i].split() + lines2_parts = lines2[i].split() + if ''.join(lines1_parts) != ''.join(lines2_parts): + ok = False + if not ok: + errmsg = 'Mismatch on line #%s:\n>>%s<<\n>>%s<<\n' % ( + i, lines1[i], lines2[i]) + return False, errmsg + return True, '' + + +def main(): + if not is_in_rootdir(): + testlog.error('Error: Please run me from the root dir of pyelftools!') + return 1 + + optparser = OptionParser( + usage='usage: %prog [options] [file] [file] ...', + prog='run_elfdump_tests.py') + optparser.add_option('-V', '--verbose', + action='store_true', dest='verbose', + help='Verbose output') + options, args = optparser.parse_args() + + if options.verbose: + testlog.info('Running in verbose mode') + testlog.info('Python executable = %s' % sys.executable) + testlog.info('elfdump path = %s' % READELF_PATH) + testlog.info('Given list of files: %s' % args) + + # If file names are given as command-line arguments, only these files + # are taken as inputs. Otherwise, autodiscovery is performed. + # + if len(args) > 0: + filenames = args + else: + filenames = list(discover_testfiles('test/testfiles')) + + success = True + for filename in filenames: + if success: + success = success and run_test_on_file( + filename, + verbose=options.verbose) + + if success: + testlog.info('\nConclusion: SUCCESS') + return 0 + else: + testlog.info('\nConclusion: FAIL') + return 1 + + +if __name__ == '__main__': + sys.exit(main()) + -- 2.30.2