mirror of
				https://github.com/ethereum/solidity
				synced 2023-10-03 13:03:40 +00:00 
			
		
		
		
	
		
			
				
	
	
		
			127 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			127 lines
		
	
	
		
			4.2 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable File
		
	
	
	
	
#!/usr/bin/env python3
 | 
						|
 | 
						|
from argparse import ArgumentParser
 | 
						|
import sys
 | 
						|
import os
 | 
						|
import subprocess
 | 
						|
import re
 | 
						|
import glob
 | 
						|
import threading
 | 
						|
import time
 | 
						|
 | 
						|
DESCRIPTION = """Regressor is a tool to run regression tests in a CI env."""
 | 
						|
 | 
						|
class PrintDotsThread(object):
 | 
						|
    """Prints a dot every "interval" (default is 300) seconds"""
 | 
						|
 | 
						|
    def __init__(self, interval=300):
 | 
						|
        self.interval = interval
 | 
						|
 | 
						|
        thread = threading.Thread(target=self.run, args=())
 | 
						|
        thread.daemon = True
 | 
						|
        thread.start()
 | 
						|
 | 
						|
    def run(self):
 | 
						|
        """ Runs until the main Python thread exits. """
 | 
						|
        ## Print a newline at the very beginning.
 | 
						|
        print("")
 | 
						|
        while True:
 | 
						|
            # Print dot
 | 
						|
            print(".")
 | 
						|
            time.sleep(self.interval)
 | 
						|
 | 
						|
class regressor():
 | 
						|
    _re_sanitizer_log = re.compile(r"""ERROR: (libFuzzer|UndefinedBehaviorSanitizer)""")
 | 
						|
 | 
						|
    def __init__(self, description, args):
 | 
						|
        self._description = description
 | 
						|
        self._args = self.parseCmdLine(description, args)
 | 
						|
        self._repo_root = os.path.dirname(sys.path[0])
 | 
						|
        self._fuzzer_path = os.path.join(self._repo_root,
 | 
						|
                                         "build/test/tools/ossfuzz")
 | 
						|
        self._logpath = os.path.join(self._repo_root, "test_results")
 | 
						|
 | 
						|
    def parseCmdLine(self, description, args):
 | 
						|
        argParser = ArgumentParser(description)
 | 
						|
        argParser.add_argument('-o', '--out-dir', required=True, type=str,
 | 
						|
                               help="""Directory where test results will be written""")
 | 
						|
        return argParser.parse_args(args)
 | 
						|
 | 
						|
    @staticmethod
 | 
						|
    def run_cmd(command, logfile=None, env=None):
 | 
						|
        """
 | 
						|
        Args:
 | 
						|
            command (str): command to run
 | 
						|
            logfile (str): log file name
 | 
						|
            env (dict): dictionary holding key-value pairs for bash environment
 | 
						|
                    variables
 | 
						|
 | 
						|
        Returns:
 | 
						|
            int: The exit status of the command. Exit status codes are:
 | 
						|
                0       -> Success
 | 
						|
                1-255   -> Failure
 | 
						|
        """
 | 
						|
        if not logfile:
 | 
						|
            logfile = os.devnull
 | 
						|
 | 
						|
        if not env:
 | 
						|
            env = os.environ.copy()
 | 
						|
 | 
						|
        with open(logfile, 'w') as logfh:
 | 
						|
            with subprocess.Popen(command, shell=True, executable='/bin/bash',
 | 
						|
                                    env=env, stdout=logfh,
 | 
						|
                                    stderr=subprocess.STDOUT) as proc:
 | 
						|
                ret = proc.wait()
 | 
						|
                logfh.close()
 | 
						|
                return ret
 | 
						|
 | 
						|
    def process_log(self, logfile):
 | 
						|
        """
 | 
						|
        Args:
 | 
						|
            logfile (str): log file name
 | 
						|
 | 
						|
        Returns:
 | 
						|
            bool: Test status.
 | 
						|
                True       -> Success
 | 
						|
                False      -> Failure
 | 
						|
        """
 | 
						|
 | 
						|
        ## Log may contain non ASCII characters, so we simply stringify them
 | 
						|
        ## since they don't matter for regular expression matching
 | 
						|
        rawtext = str(open(logfile, 'rb').read())
 | 
						|
        return not re.search(self._re_sanitizer_log, rawtext)
 | 
						|
 | 
						|
    def run(self):
 | 
						|
        """
 | 
						|
        Returns:
 | 
						|
            bool: Test status.
 | 
						|
                True       -> All tests succeeded
 | 
						|
                False      -> At least one test failed
 | 
						|
        """
 | 
						|
 | 
						|
        testStatus = []
 | 
						|
        for fuzzer in glob.iglob("{}/*_ossfuzz".format(self._fuzzer_path)):
 | 
						|
            basename = os.path.basename(fuzzer)
 | 
						|
            logfile = os.path.join(self._logpath, "{}.log".format(basename))
 | 
						|
            corpus_dir = "/tmp/solidity-fuzzing-corpus/{0}_seed_corpus" \
 | 
						|
                .format(basename)
 | 
						|
            cmd = "find {0} -type f | xargs -n1 sh -c '{1} $0 || exit 255'".format(corpus_dir, fuzzer)
 | 
						|
            self.run_cmd(cmd, logfile=logfile)
 | 
						|
            ret = self.process_log(logfile)
 | 
						|
            if not ret:
 | 
						|
                print(
 | 
						|
                    "\t[-] libFuzzer reported failure for {0}. "
 | 
						|
                    "Failure logged to test_results".format(
 | 
						|
                        basename))
 | 
						|
                testStatus.append(False)
 | 
						|
            else:
 | 
						|
                print("\t[+] {0} passed regression tests.".format(basename))
 | 
						|
                testStatus.append(True)
 | 
						|
        return all(testStatus)
 | 
						|
 | 
						|
 | 
						|
if __name__ == '__main__':
 | 
						|
    dotprinter = PrintDotsThread()
 | 
						|
    tool = regressor(DESCRIPTION, sys.argv[1:])
 | 
						|
    sys.exit(not tool.run())
 |