From e0aee5815a86348505d7b018dabc78c643d8ed86 Mon Sep 17 00:00:00 2001 From: Behdad Esfahbod Date: Sat, 22 Mar 2025 13:07:10 -0600 Subject: [PATCH] [test] Respect MESON_EXE_WRAPPER in more test runners It's not working for me though. Meson doesn't seem to set WINEPATH during testing. --- test/fuzzing/run-fuzzer-tests.py | 7 + test/shape/run-tests.py | 2 - test/subset/run-repack-tests.py | 151 ++++++++-------- test/subset/run-tests.py | 291 ++++++++++++++++++------------- 4 files changed, 257 insertions(+), 194 deletions(-) diff --git a/test/fuzzing/run-fuzzer-tests.py b/test/fuzzing/run-fuzzer-tests.py index 7f9671096..1e39ed0a2 100755 --- a/test/fuzzing/run-fuzzer-tests.py +++ b/test/fuzzing/run-fuzzer-tests.py @@ -2,15 +2,22 @@ import pathlib import subprocess +import os import sys import tempfile +EXE_WRAPPER = os.environ.get("MESON_EXE_WRAPPER") + def run_command(command): """ Run a command, capturing potentially large output in a temp file. Returns (output_string, exit_code). """ + global EXE_WRAPPER + if EXE_WRAPPER: + command = [EXE_WRAPPER] + command + with tempfile.TemporaryFile() as tempf: p = subprocess.Popen(command, stdout=tempf, stderr=tempf) p.wait() diff --git a/test/shape/run-tests.py b/test/shape/run-tests.py index 5046aeabc..c7ca38248 100755 --- a/test/shape/run-tests.py +++ b/test/shape/run-tests.py @@ -20,8 +20,6 @@ EXE_WRAPPER = os.environ.get("MESON_EXE_WRAPPER") def open_shape_batch_process(): - global hb_shape, env - cmd = [hb_shape, "--batch"] if EXE_WRAPPER: cmd = [EXE_WRAPPER] + cmd diff --git a/test/subset/run-repack-tests.py b/test/subset/run-repack-tests.py index 9c25b2bcf..69751f0c3 100755 --- a/test/subset/run-repack-tests.py +++ b/test/subset/run-repack-tests.py @@ -15,101 +15,114 @@ import io from repack_test import RepackTest try: - from fontTools.ttLib import TTFont + from fontTools.ttLib import TTFont except ImportError: - print ("fonttools is not present, skipping test.") - sys.exit (77) + print("fonttools is not present, skipping test.") + sys.exit(77) -ots_sanitize = shutil.which ("ots-sanitize") +ots_sanitize = shutil.which("ots-sanitize") -def subset_cmd (command): - global hb_subset, process - print (hb_subset + ' ' + " ".join(command)) - process.stdin.write ((';'.join (command) + '\n').encode ("utf-8")) - process.stdin.flush () - return process.stdout.readline().decode ("utf-8").strip () +EXE_WRAPPER = os.environ.get("MESON_EXE_WRAPPER") -def cmd (command): - p = subprocess.Popen ( - command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - universal_newlines=True) - (stdoutdata, stderrdata) = p.communicate () - print (stderrdata, end="", file=sys.stderr) - return stdoutdata, p.returncode -def fail_test (test, cli_args, message): - print ('ERROR: %s' % message) - print ('Test State:') - print (' test.font_name %s' % test.font_name) - print (' test.test_path %s' % os.path.abspath (test.test_path)) - return 1 +def subset_cmd(command): + global hb_subset, process + print(hb_subset + " " + " ".join(command)) + process.stdin.write((";".join(command) + "\n").encode("utf-8")) + process.stdin.flush() + return process.stdout.readline().decode("utf-8").strip() -def run_test (test, should_check_ots): - out_file = os.path.join (tempfile.mkdtemp (), test.font_name + '-subset.ttf') - cli_args = ["--font-file=" + test.font_path (), - "--output-file=" + out_file, - "--unicodes=%s" % test.codepoints_string (), - "--drop-tables-=GPOS,GSUB,GDEF",] - print (' '.join (cli_args)) - ret = subset_cmd (cli_args) - if ret != "success": - return fail_test (test, cli_args, "%s failed" % ' '.join (cli_args)) +def cmd(command): + p = subprocess.Popen( + command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True + ) + (stdoutdata, stderrdata) = p.communicate() + print(stderrdata, end="", file=sys.stderr) + return stdoutdata, p.returncode - try: - with TTFont (out_file) as font: - pass - except Exception as e: - print (e) - return fail_test (test, cli_args, "ttx failed to parse the result") - if should_check_ots: - print ("Checking output with ots-sanitize.") - if not check_ots (out_file): - return fail_test (test, cli_args, 'ots for subsetted file fails.') +def fail_test(test, cli_args, message): + print("ERROR: %s" % message) + print("Test State:") + print(" test.font_name %s" % test.font_name) + print(" test.test_path %s" % os.path.abspath(test.test_path)) + return 1 - return 0 -def has_ots (): - if not ots_sanitize: - print ("OTS is not present, skipping all ots checks.") - return False - return True +def run_test(test, should_check_ots): + out_file = os.path.join(tempfile.mkdtemp(), test.font_name + "-subset.ttf") + cli_args = [ + "--font-file=" + test.font_path(), + "--output-file=" + out_file, + "--unicodes=%s" % test.codepoints_string(), + "--drop-tables-=GPOS,GSUB,GDEF", + ] + print(" ".join(cli_args)) + ret = subset_cmd(cli_args) + + if ret != "success": + return fail_test(test, cli_args, "%s failed" % " ".join(cli_args)) + + try: + with TTFont(out_file) as font: + pass + except Exception as e: + print(e) + return fail_test(test, cli_args, "ttx failed to parse the result") + + if should_check_ots: + print("Checking output with ots-sanitize.") + if not check_ots(out_file): + return fail_test(test, cli_args, "ots for subsetted file fails.") + + return 0 + + +def has_ots(): + if not ots_sanitize: + print("OTS is not present, skipping all ots checks.") + return False + return True + + +def check_ots(path): + ots_report, returncode = cmd([ots_sanitize, path]) + if returncode: + print("OTS Failure: %s" % ots_report) + return False + return True -def check_ots (path): - ots_report, returncode = cmd ([ots_sanitize, path]) - if returncode: - print ("OTS Failure: %s" % ots_report) - return False - return True args = sys.argv[1:] -if not args or sys.argv[1].find ('hb-subset') == -1 or not os.path.exists (sys.argv[1]): - sys.exit ("First argument does not seem to point to usable hb-subset.") +if not args or sys.argv[1].find("hb-subset") == -1 or not os.path.exists(sys.argv[1]): + sys.exit("First argument does not seem to point to usable hb-subset.") hb_subset, args = args[0], args[1:] -if len (args) != 1: - sys.exit ("No tests supplied.") +if len(args) != 1: + sys.exit("No tests supplied.") has_ots = has_ots() -process = subprocess.Popen ([hb_subset, '--batch'], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=sys.stdout) +batch_cmd = [hb_subset, "--batch"] +if EXE_WRAPPER: + batch_cmd = [EXE_WRAPPER] + batch_cmd +process = subprocess.Popen( + batch_cmd, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=sys.stdout +) fails = 0 path = args[0] if not path.endswith(".tests"): - sys.exit ("Not a valid test case path.") + sys.exit("Not a valid test case path.") -with open (path, mode="r", encoding="utf-8") as f: - # TODO(garretrieger): re-enable OTS checking. - fails += run_test (RepackTest (path, f.read ()), False) +with open(path, mode="r", encoding="utf-8") as f: + # TODO(garretrieger): re-enable OTS checking. + fails += run_test(RepackTest(path, f.read()), False) if fails != 0: - sys.exit ("%d test(s) failed." % fails) + sys.exit("%d test(s) failed." % fails) else: - print ("All tests passed.") + print("All tests passed.") diff --git a/test/subset/run-tests.py b/test/subset/run-tests.py index c0c256d89..4aeed407e 100755 --- a/test/subset/run-tests.py +++ b/test/subset/run-tests.py @@ -15,146 +15,191 @@ import io from subset_test_suite import SubsetTestSuite try: - from fontTools.ttLib import TTFont + from fontTools.ttLib import TTFont except ImportError: TTFont = None -ots_sanitize = shutil.which ("ots-sanitize") - -def subset_cmd (command): - global hb_subset, process - print (hb_subset + ' ' + " ".join(command)) - process.stdin.write ((';'.join (command) + '\n').encode ("utf-8")) - process.stdin.flush () - return process.stdout.readline().decode ("utf-8").strip () - -def cmd (command): - p = subprocess.Popen ( - command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, - universal_newlines=True) - (stdoutdata, stderrdata) = p.communicate () - print (stderrdata, end="", file=sys.stderr) - return stdoutdata, p.returncode - -def fail_test (test, cli_args, message): - print ('ERROR: %s' % message) - print ('Test State:') - print (' test.font_path %s' % os.path.abspath (test.font_path)) - print (' test.profile_path %s' % os.path.abspath (test.profile_path)) - print (' test.unicodes %s' % test.unicodes ()) - expected_file = os.path.join (test_suite.get_output_directory (), - test.get_font_name ()) - print (' expected_file %s' % os.path.abspath (expected_file)) - return 1 - -def run_test (test, should_check_ots, preprocess): - out_file = os.path.join (tempfile.mkdtemp (), test.get_font_name () + '-subset' + test.get_font_extension ()) - cli_args = ["--font-file=" + test.font_path, - "--output-file=" + out_file, - "--unicodes=%s" % test.unicodes (), - "--drop-tables+=DSIG,BASE", - "--drop-tables-=sbix"] - if preprocess: - cli_args.extend(["--preprocess-face",]) - - cli_args.extend (test.get_profile_flags ()) - if test.get_instance_flags (): - cli_args.extend (["--instance=%s" % ','.join(test.get_instance_flags ())]) - if test.iup_optimize: - cli_args.extend (["--optimize",]) - ret = subset_cmd (cli_args) - - if ret != "success": - return fail_test (test, cli_args, "%s failed" % ' '.join (cli_args)) - - expected_file = os.path.join (test_suite.get_output_directory (), test.get_font_name ()) - with open (expected_file, "rb") as fp: - expected_contents = fp.read() - with open (out_file, "rb") as fp: - actual_contents = fp.read() - - if expected_contents == actual_contents: - if should_check_ots: - print ("Checking output with ots-sanitize.") - if not check_ots (out_file): - return fail_test (test, cli_args, 'ots for subsetted file fails.') - return 0 - - if TTFont is None: - print ("fonttools is not present, skipping TTX diff.") - return fail_test (test, cli_args, "hash for expected and actual does not match.") - - with io.StringIO () as fp: - try: - with TTFont (expected_file) as font: - font.saveXML (fp) - except Exception as e: - print (e) - return fail_test (test, cli_args, "ttx failed to parse the expected result") - expected_ttx = fp.getvalue () - - with io.StringIO () as fp: - try: - with TTFont (out_file) as font: - font.saveXML (fp) - except Exception as e: - print (e) - return fail_test (test, cli_args, "ttx failed to parse the actual result") - actual_ttx = fp.getvalue () - - if actual_ttx != expected_ttx: - for line in unified_diff (expected_ttx.splitlines (1), actual_ttx.splitlines (1)): - sys.stdout.write (line) - sys.stdout.flush () - return fail_test (test, cli_args, 'ttx for expected and actual does not match.') - - return fail_test (test, cli_args, 'hash for expected and actual does not match, ' - 'but the ttx matches. Expected file needs to be updated?') +ots_sanitize = shutil.which("ots-sanitize") -def has_ots (): - if not ots_sanitize: - print ("OTS is not present, skipping all ots checks.") - return False - return True +def subset_cmd(command): + global hb_subset, subset_process + + # (Re)start shaper if it is dead + if subset_process.poll() is not None: + subset_process = open_subset_batch_process() + + print(hb_subset + " " + " ".join(command)) + subset_process.stdin.write((";".join(command) + "\n").encode("utf-8")) + subset_process.stdin.flush() + return subset_process.stdout.readline().decode("utf-8").strip() + + +def cmd(command): + p = subprocess.Popen( + command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True + ) + (stdoutdata, stderrdata) = p.communicate() + print(stderrdata, end="", file=sys.stderr) + return stdoutdata, p.returncode + + +def fail_test(test, cli_args, message): + print("ERROR: %s" % message) + print("Test State:") + print(" test.font_path %s" % os.path.abspath(test.font_path)) + print(" test.profile_path %s" % os.path.abspath(test.profile_path)) + print(" test.unicodes %s" % test.unicodes()) + expected_file = os.path.join( + test_suite.get_output_directory(), test.get_font_name() + ) + print(" expected_file %s" % os.path.abspath(expected_file)) + return 1 + + +def run_test(test, should_check_ots, preprocess): + out_file = os.path.join( + tempfile.mkdtemp(), test.get_font_name() + "-subset" + test.get_font_extension() + ) + cli_args = [ + "--font-file=" + test.font_path, + "--output-file=" + out_file, + "--unicodes=%s" % test.unicodes(), + "--drop-tables+=DSIG,BASE", + "--drop-tables-=sbix", + ] + if preprocess: + cli_args.extend( + [ + "--preprocess-face", + ] + ) + + cli_args.extend(test.get_profile_flags()) + if test.get_instance_flags(): + cli_args.extend(["--instance=%s" % ",".join(test.get_instance_flags())]) + if test.iup_optimize: + cli_args.extend( + [ + "--optimize", + ] + ) + ret = subset_cmd(cli_args) + + if ret != "success": + return fail_test(test, cli_args, "%s failed" % " ".join(cli_args)) + + expected_file = os.path.join( + test_suite.get_output_directory(), test.get_font_name() + ) + with open(expected_file, "rb") as fp: + expected_contents = fp.read() + with open(out_file, "rb") as fp: + actual_contents = fp.read() + + if expected_contents == actual_contents: + if should_check_ots: + print("Checking output with ots-sanitize.") + if not check_ots(out_file): + return fail_test(test, cli_args, "ots for subsetted file fails.") + return 0 + + if TTFont is None: + print("fonttools is not present, skipping TTX diff.") + return fail_test(test, cli_args, "hash for expected and actual does not match.") + + with io.StringIO() as fp: + try: + with TTFont(expected_file) as font: + font.saveXML(fp) + except Exception as e: + print(e) + return fail_test(test, cli_args, "ttx failed to parse the expected result") + expected_ttx = fp.getvalue() + + with io.StringIO() as fp: + try: + with TTFont(out_file) as font: + font.saveXML(fp) + except Exception as e: + print(e) + return fail_test(test, cli_args, "ttx failed to parse the actual result") + actual_ttx = fp.getvalue() + + if actual_ttx != expected_ttx: + for line in unified_diff(expected_ttx.splitlines(1), actual_ttx.splitlines(1)): + sys.stdout.write(line) + sys.stdout.flush() + return fail_test(test, cli_args, "ttx for expected and actual does not match.") + + return fail_test( + test, + cli_args, + "hash for expected and actual does not match, " + "but the ttx matches. Expected file needs to be updated?", + ) + + +def has_ots(): + if not ots_sanitize: + print("OTS is not present, skipping all ots checks.") + return False + return True + + +def check_ots(path): + ots_report, returncode = cmd([ots_sanitize, path]) + if returncode: + print("OTS Failure: %s" % ots_report) + return False + return True -def check_ots (path): - ots_report, returncode = cmd ([ots_sanitize, path]) - if returncode: - print ("OTS Failure: %s" % ots_report) - return False - return True args = sys.argv[1:] -if not args or sys.argv[1].find ('hb-subset') == -1 or not os.path.exists (sys.argv[1]): - sys.exit ("First argument does not seem to point to usable hb-subset.") +if not args or sys.argv[1].find("hb-subset") == -1 or not os.path.exists(sys.argv[1]): + sys.exit("First argument does not seem to point to usable hb-subset.") hb_subset, args = args[0], args[1:] -if not len (args): - sys.exit ("No tests supplied.") +if not len(args): + sys.exit("No tests supplied.") has_ots = has_ots() env = os.environ.copy() -env['LC_ALL'] = 'C' -process = subprocess.Popen ([hb_subset, '--batch'], - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=sys.stdout, - env=env) +env["LC_ALL"] = "C" + +EXE_WRAPPER = os.environ.get("MESON_EXE_WRAPPER") + + +def open_subset_batch_process(): + cmd = [hb_subset, "--batch"] + if EXE_WRAPPER: + cmd = [EXE_WRAPPER] + cmd + + process = subprocess.Popen( + cmd, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=sys.stdout, + env=env, + ) + return process + + +subset_process = open_subset_batch_process() fails = 0 for path in args: - with open (path, mode="r", encoding="utf-8") as f: - print ("Running tests in " + path) - test_suite = SubsetTestSuite (path, f.read ()) - for test in test_suite.tests (): - # Tests are run with and without preprocessing, results should be the - # same between them. - fails += run_test (test, has_ots, False) - fails += run_test (test, has_ots, True) + with open(path, mode="r", encoding="utf-8") as f: + print("Running tests in " + path) + test_suite = SubsetTestSuite(path, f.read()) + for test in test_suite.tests(): + # Tests are run with and without preprocessing, results should be the + # same between them. + fails += run_test(test, has_ots, False) + fails += run_test(test, has_ots, True) if fails != 0: - sys.exit ("%d test(s) failed." % fails) + sys.exit("%d test(s) failed." % fails) else: - print ("All tests passed.") + print("All tests passed.")