1#!/usr/bin/env python 2 3from __future__ import print_function, division, absolute_import 4 5import sys, os, subprocess, tempfile, threading 6 7 8def which (program): 9 # https://stackoverflow.com/a/377028 10 def is_exe (fpath): 11 return os.path.isfile (fpath) and os.access (fpath, os.X_OK) 12 13 fpath, _ = os.path.split (program) 14 if fpath: 15 if is_exe (program): 16 return program 17 else: 18 for path in os.environ["PATH"].split (os.pathsep): 19 exe_file = os.path.join (path, program) 20 if is_exe (exe_file): 21 return exe_file 22 23 return None 24 25 26def cmd (command): 27 # https://stackoverflow.com/a/4408409 28 # https://stackoverflow.com/a/10012262 29 with tempfile.TemporaryFile () as tempf: 30 p = subprocess.Popen (command, stderr=tempf) 31 is_killed = {'value': False} 32 33 def timeout (p, is_killed): 34 is_killed['value'] = True 35 p.kill () 36 timeout_seconds = int (os.environ.get ("HB_TEST_SHAPE_FUZZER_TIMEOUT", "2")) 37 timer = threading.Timer (timeout_seconds, timeout, [p, is_killed]) 38 39 try: 40 timer.start() 41 p.wait () 42 tempf.seek (0) 43 text = tempf.read () 44 45 #TODO: Detect debug mode with a better way 46 is_debug_mode = b"SANITIZE" in text 47 48 text = "" if is_debug_mode else text.decode ("utf-8").strip () 49 returncode = p.returncode 50 finally: 51 timer.cancel() 52 53 if is_killed['value']: 54 text = 'error: timeout, ' + text 55 returncode = 1 56 57 return text, returncode 58 59 60srcdir = os.environ.get ("srcdir", ".") 61EXEEXT = os.environ.get ("EXEEXT", "") 62top_builddir = os.environ.get ("top_builddir", ".") 63hb_shape_fuzzer = os.path.join (top_builddir, "hb-shape-fuzzer" + EXEEXT) 64 65if not os.path.exists (hb_shape_fuzzer): 66 if len (sys.argv) == 1 or not os.path.exists (sys.argv[1]): 67 print ("""Failed to find hb-shape-fuzzer binary automatically, 68please provide it as the first argument to the tool""") 69 sys.exit (1) 70 71 hb_shape_fuzzer = sys.argv[1] 72 73print ('hb_shape_fuzzer:', hb_shape_fuzzer) 74fails = 0 75 76libtool = os.environ.get ('LIBTOOL') 77valgrind = None 78if os.environ.get ('RUN_VALGRIND', ''): 79 valgrind = which ('valgrind') 80 if valgrind is None: 81 print ("""Valgrind requested but not found.""") 82 sys.exit (1) 83 if libtool is None: 84 print ("""Valgrind support is currently autotools only and needs libtool but not found.""") 85 86 87parent_path = os.path.join (srcdir, "fonts") 88for file in os.listdir (parent_path): 89 path = os.path.join (parent_path, file) 90 91 if valgrind: 92 text, returncode = cmd (libtool.split(' ') + ['--mode=execute', valgrind + ' --leak-check=full --error-exitcode=1', '--', hb_shape_fuzzer, path]) 93 else: 94 text, returncode = cmd ([hb_shape_fuzzer, path]) 95 if 'error' in text: 96 returncode = 1 97 98 if (not valgrind or returncode) and text.strip (): 99 print (text) 100 101 if returncode != 0: 102 print ('failure on %s' % file) 103 fails = fails + 1 104 105 106if fails: 107 print ("%i shape fuzzer related tests failed." % fails) 108 sys.exit (1) 109