[test] Compile python modules Minor speedup. Need to distribute them from meson.build I suppose.
diff --git a/test/shape/run-tests.py b/test/shape/run-tests.py index 21b8327..2184a5f 100755 --- a/test/shape/run-tests.py +++ b/test/shape/run-tests.py
@@ -1,332 +1,3 @@ #!/usr/bin/env python3 -import sys, os, subprocess, hashlib - -args = sys.argv[1:] - -verbose = False -if args and args[0] == "-v": - verbose = True - args = args[1:] - -if not args or args[0].find("hb-shape") == -1 or not os.path.exists(args[0]): - sys.exit("""First argument does not seem to point to usable hb-shape.""") -hb_shape, args = args[0], args[1:] - -env = os.environ.copy() -env["LC_ALL"] = "C" - -EXE_WRAPPER = os.environ.get("MESON_EXE_WRAPPER") - - -def open_shape_batch_process(): - cmd = [hb_shape, "--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 - - -shape_process = open_shape_batch_process() -no_glyph_names_process = None - - -def shape_cmd(command, shape_process, verbose=False): - global hb_shape - - # (Re)start shaper if it is dead - if shape_process.poll() is not None: - shape_process = open_shape_batch_process() - - if verbose: - print(hb_shape + " " + " ".join(command)) - shape_process.stdin.write((";".join(command) + "\n").encode("utf-8")) - shape_process.stdin.flush() - return shape_process.stdout.readline().decode("utf-8").strip() - - -def plural(what): - if not what.endswith("s"): - what += "s" - return what - - -def whats_var_name(what): - return plural(what).replace("-", "_") - - -def supported_whats_var_name(what): - whats = whats_var_name(what) - return "supported_" + whats - - -def supported_whats(what): - return globals()[supported_whats_var_name(what)] - - -def all_whats_var_name(what): - whats = whats_var_name(what) - return "all_" + whats - - -def all_whats(what): - return globals()[all_whats_var_name(what)] - - -# Collect supported backends -for what in ["shaper", "face-loader", "font-funcs"]: - subcommand = "--list-" + plural(what) - - cmd = [hb_shape, subcommand] - if EXE_WRAPPER: - cmd = [EXE_WRAPPER] + cmd - - what_process = subprocess.Popen( - cmd, - stdin=subprocess.PIPE, - stdout=subprocess.PIPE, - stderr=sys.stdout, - env=env, - ) - # Capture the output - what_list = what_process.communicate()[0].decode("utf-8").strip().split() - if what_process.returncode: - sys.exit(f"Failed to run: {hb_shape} {subcommand}") - whats = plural(what) - var_name = supported_whats_var_name(what) - globals()[var_name] = what_list - print(f"Supported {whats}: {what_list}") - -# If running under Wine and not native dlls, make the respective shapers unavailable. -if os.environ.get("WINEPATH"): - overrides = os.environ.get("WINEDLLOVERRIDES", "").lower() - if "directwrite" in supported_shapers and overrides.find("dwrite") == -1: - supported_shapers.remove("directwrite") - print("Skipping DirectWrite shaper under Wine.") - if "uniscribe" in supported_shapers and overrides.find("usp10") == -1: - supported_shapers.remove("uniscribe") - print("Skipping Uniscribe shaper under Wine.") - - -passes = 0 -fails = 0 -skips = 0 - -if not len(args): - args = ["-"] - -for filename in args: - if filename == "-": - print("Running tests from standard input") - else: - print("Running tests in " + filename) - - if filename == "-": - f = sys.stdin - else: - f = open(filename, encoding="utf8") - - # By default test all backends - for what in ["shaper", "face-loader", "font-funcs"]: - all_var_name = all_whats_var_name(what) - globals()[all_var_name] = supported_whats(what) - all_shapers = ["ot"] # But only 'ot' shaper - - # Right now we only test the 'ot' shaper if nothing specified, - # but try all font-funcs unless overriden. - # Only 'ot' face-loader is tested. - - for line in f: - comment = False - if line.startswith("#"): - comment = True - line = line[1:] - - if line.startswith(" "): - if verbose: - print("#%s" % line) - continue - - line = line.strip() - if not line: - continue - - if line.startswith("@"): - # Directive - line = line.strip() - line = line.split("#")[0].strip()[1:] - consumed = False - for what in ["shaper", "face-loader", "font-funcs"]: - whats = plural(what) - if line.startswith(what) or line.startswith(whats): - command, values = line.split("=") - values = values.strip().split(",") - - supported = supported_whats(what) - if command[-1] == "-": - # Exclude - values = [v for v in supported if v not in values] - else: - # Specify - values = [v for v in values if v in supported] - - var_name = all_whats_var_name(what) - print(f"Setting {whats} to test to {values}") - globals()[var_name] = values - consumed = True - if consumed: - print(line) - continue - else: - print("Unrecognized directive: %s" % line, file=sys.stderr) - sys.exit(1) - - fontfile, options, unicodes, glyphs_expected = line.split(";") - options = options.split() - if fontfile.startswith("/") or fontfile.startswith('"/'): - if os.name == "nt": # Skip on Windows - continue - - fontfile, expected_hash = (fontfile.split("@") + [""])[:2] - - try: - with open(fontfile, "rb") as ff: - if expected_hash: - actual_hash = hashlib.sha1(ff.read()).hexdigest().strip() - if actual_hash != expected_hash: - print( - "different version of %s found; Expected hash %s, got %s; skipping." - % (fontfile, expected_hash, actual_hash) - ) - skips += 1 - continue - except IOError: - print("%s not found, skip." % fontfile) - skips += 1 - continue - else: - cwd = os.path.dirname(filename) - fontfile = os.path.normpath(os.path.join(cwd, fontfile)) - - if comment: - if verbose: - print('# %s "%s" --unicodes %s' % (hb_shape, fontfile, unicodes)) - continue - - skip_test = False - shaper = None - face_loader = None - font_funcs = None - new_options = [] - it = iter(options) - for option in it: - consumed = False - for what in ["shaper", "face-loader", "font-funcs"]: - if option.startswith("--" + what): - try: - backend = option.split("=")[1] - except IndexError: - backend = next(it) - if backend not in supported_whats(what): - skips += 1 - print(f"Skipping test with {what}={backend}.") - skip_test = True - break - what = what.replace("-", "_") - globals()[what] = backend - consumed = True - if not consumed: - new_options.append(option) - - if skip_test: - break - if skip_test: - continue - options = new_options - - for shaper in [shaper] if shaper else all_whats("shaper"): - for font_funcs in [font_funcs] if font_funcs else all_whats("font-funcs"): - extra_options = [] - - if shaper: - extra_options.append("--shaper=" + shaper) - if face_loader: - extra_options.append("--face-loader=" + face_loader) - if font_funcs: - extra_options.append("--font-funcs=" + font_funcs) - - if glyphs_expected != "*": - extra_options.append("--verify") - extra_options.append("--unsafe-to-concat") - - if verbose: - print( - "# shaper=%s face-loader=%s font-funcs=%s" - % (shaper, face_loader, font_funcs) - ) - cmd = [fontfile] + ["--unicodes", unicodes] + options + extra_options - glyphs = shape_cmd(cmd, shape_process, verbose).strip() - - if glyphs_expected == "*": - passes += 1 - continue - - final_glyphs = glyphs - final_glyphs_expected = glyphs_expected - - if glyphs != glyphs_expected and glyphs.find("gid") != -1: - if not no_glyph_names_process: - no_glyph_names_process = open_shape_batch_process() - - cmd2 = [fontfile] + ["--glyphs", "--no-glyph-names", glyphs] - final_glyphs = shape_cmd(cmd2, no_glyph_names_process).strip() - - cmd2 = [fontfile] + [ - "--glyphs", - "--no-glyph-names", - glyphs_expected, - ] - final_glyphs_expected = shape_cmd( - cmd2, no_glyph_names_process - ).strip() - - # If the removal of glyph_ids failed, fail the test. - # https://github.com/harfbuzz/harfbuzz/issues/5169 - if not final_glyphs_expected or final_glyphs != final_glyphs_expected: - print(hb_shape + " " + " ".join(cmd), file=sys.stderr) - print("Actual: " + glyphs, file=sys.stderr) - print("Expected: " + glyphs_expected, file=sys.stderr) - if final_glyphs != glyphs: - print( - "Actual (no glyph names): " + final_glyphs, - file=sys.stderr, - ) - print( - "Expected (no glyph names): " + final_glyphs_expected, - file=sys.stderr, - ) - fails += 1 - else: - passes += 1 - -print( - "%d tests passed; %d failed; %d skipped." % (passes, fails, skips), file=sys.stderr -) -if not (fails + passes): - print("No tests ran.") -elif not (fails + skips): - print("All tests passed.") - -if fails: - sys.exit(1) -elif passes: - sys.exit(0) -else: - sys.exit(77) +import run_tests_module
diff --git a/test/shape/run_tests_module.py b/test/shape/run_tests_module.py new file mode 100644 index 0000000..7871bd5 --- /dev/null +++ b/test/shape/run_tests_module.py
@@ -0,0 +1,330 @@ +import sys, os, subprocess, hashlib + +args = sys.argv[1:] + +verbose = False +if args and args[0] == "-v": + verbose = True + args = args[1:] + +if not args or args[0].find("hb-shape") == -1 or not os.path.exists(args[0]): + sys.exit("""First argument does not seem to point to usable hb-shape.""") +hb_shape, args = args[0], args[1:] + +env = os.environ.copy() +env["LC_ALL"] = "C" + +EXE_WRAPPER = os.environ.get("MESON_EXE_WRAPPER") + + +def open_shape_batch_process(): + cmd = [hb_shape, "--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 + + +shape_process = open_shape_batch_process() +no_glyph_names_process = None + + +def shape_cmd(command, shape_process, verbose=False): + global hb_shape + + # (Re)start shaper if it is dead + if shape_process.poll() is not None: + shape_process = open_shape_batch_process() + + if verbose: + print(hb_shape + " " + " ".join(command)) + shape_process.stdin.write((";".join(command) + "\n").encode("utf-8")) + shape_process.stdin.flush() + return shape_process.stdout.readline().decode("utf-8").strip() + + +def plural(what): + if not what.endswith("s"): + what += "s" + return what + + +def whats_var_name(what): + return plural(what).replace("-", "_") + + +def supported_whats_var_name(what): + whats = whats_var_name(what) + return "supported_" + whats + + +def supported_whats(what): + return globals()[supported_whats_var_name(what)] + + +def all_whats_var_name(what): + whats = whats_var_name(what) + return "all_" + whats + + +def all_whats(what): + return globals()[all_whats_var_name(what)] + + +# Collect supported backends +for what in ["shaper", "face-loader", "font-funcs"]: + subcommand = "--list-" + plural(what) + + cmd = [hb_shape, subcommand] + if EXE_WRAPPER: + cmd = [EXE_WRAPPER] + cmd + + what_process = subprocess.Popen( + cmd, + stdin=subprocess.PIPE, + stdout=subprocess.PIPE, + stderr=sys.stdout, + env=env, + ) + # Capture the output + what_list = what_process.communicate()[0].decode("utf-8").strip().split() + if what_process.returncode: + sys.exit(f"Failed to run: {hb_shape} {subcommand}") + whats = plural(what) + var_name = supported_whats_var_name(what) + globals()[var_name] = what_list + print(f"Supported {whats}: {what_list}") + +# If running under Wine and not native dlls, make the respective shapers unavailable. +if os.environ.get("WINEPATH"): + overrides = os.environ.get("WINEDLLOVERRIDES", "").lower() + if "directwrite" in supported_shapers and overrides.find("dwrite") == -1: + supported_shapers.remove("directwrite") + print("Skipping DirectWrite shaper under Wine.") + if "uniscribe" in supported_shapers and overrides.find("usp10") == -1: + supported_shapers.remove("uniscribe") + print("Skipping Uniscribe shaper under Wine.") + + +passes = 0 +fails = 0 +skips = 0 + +if not len(args): + args = ["-"] + +for filename in args: + if filename == "-": + print("Running tests from standard input") + else: + print("Running tests in " + filename) + + if filename == "-": + f = sys.stdin + else: + f = open(filename, encoding="utf8") + + # By default test all backends + for what in ["shaper", "face-loader", "font-funcs"]: + all_var_name = all_whats_var_name(what) + globals()[all_var_name] = supported_whats(what) + all_shapers = ["ot"] # But only 'ot' shaper + + # Right now we only test the 'ot' shaper if nothing specified, + # but try all font-funcs unless overriden. + # Only 'ot' face-loader is tested. + + for line in f: + comment = False + if line.startswith("#"): + comment = True + line = line[1:] + + if line.startswith(" "): + if verbose: + print("#%s" % line) + continue + + line = line.strip() + if not line: + continue + + if line.startswith("@"): + # Directive + line = line.strip() + line = line.split("#")[0].strip()[1:] + consumed = False + for what in ["shaper", "face-loader", "font-funcs"]: + whats = plural(what) + if line.startswith(what) or line.startswith(whats): + command, values = line.split("=") + values = values.strip().split(",") + + supported = supported_whats(what) + if command[-1] == "-": + # Exclude + values = [v for v in supported if v not in values] + else: + # Specify + values = [v for v in values if v in supported] + + var_name = all_whats_var_name(what) + print(f"Setting {whats} to test to {values}") + globals()[var_name] = values + consumed = True + if consumed: + print(line) + continue + else: + print("Unrecognized directive: %s" % line, file=sys.stderr) + sys.exit(1) + + fontfile, options, unicodes, glyphs_expected = line.split(";") + options = options.split() + if fontfile.startswith("/") or fontfile.startswith('"/'): + if os.name == "nt": # Skip on Windows + continue + + fontfile, expected_hash = (fontfile.split("@") + [""])[:2] + + try: + with open(fontfile, "rb") as ff: + if expected_hash: + actual_hash = hashlib.sha1(ff.read()).hexdigest().strip() + if actual_hash != expected_hash: + print( + "different version of %s found; Expected hash %s, got %s; skipping." + % (fontfile, expected_hash, actual_hash) + ) + skips += 1 + continue + except IOError: + print("%s not found, skip." % fontfile) + skips += 1 + continue + else: + cwd = os.path.dirname(filename) + fontfile = os.path.normpath(os.path.join(cwd, fontfile)) + + if comment: + if verbose: + print('# %s "%s" --unicodes %s' % (hb_shape, fontfile, unicodes)) + continue + + skip_test = False + shaper = None + face_loader = None + font_funcs = None + new_options = [] + it = iter(options) + for option in it: + consumed = False + for what in ["shaper", "face-loader", "font-funcs"]: + if option.startswith("--" + what): + try: + backend = option.split("=")[1] + except IndexError: + backend = next(it) + if backend not in supported_whats(what): + skips += 1 + print(f"Skipping test with {what}={backend}.") + skip_test = True + break + what = what.replace("-", "_") + globals()[what] = backend + consumed = True + if not consumed: + new_options.append(option) + + if skip_test: + break + if skip_test: + continue + options = new_options + + for shaper in [shaper] if shaper else all_whats("shaper"): + for font_funcs in [font_funcs] if font_funcs else all_whats("font-funcs"): + extra_options = [] + + if shaper: + extra_options.append("--shaper=" + shaper) + if face_loader: + extra_options.append("--face-loader=" + face_loader) + if font_funcs: + extra_options.append("--font-funcs=" + font_funcs) + + if glyphs_expected != "*": + extra_options.append("--verify") + extra_options.append("--unsafe-to-concat") + + if verbose: + print( + "# shaper=%s face-loader=%s font-funcs=%s" + % (shaper, face_loader, font_funcs) + ) + cmd = [fontfile] + ["--unicodes", unicodes] + options + extra_options + glyphs = shape_cmd(cmd, shape_process, verbose).strip() + + if glyphs_expected == "*": + passes += 1 + continue + + final_glyphs = glyphs + final_glyphs_expected = glyphs_expected + + if glyphs != glyphs_expected and glyphs.find("gid") != -1: + if not no_glyph_names_process: + no_glyph_names_process = open_shape_batch_process() + + cmd2 = [fontfile] + ["--glyphs", "--no-glyph-names", glyphs] + final_glyphs = shape_cmd(cmd2, no_glyph_names_process).strip() + + cmd2 = [fontfile] + [ + "--glyphs", + "--no-glyph-names", + glyphs_expected, + ] + final_glyphs_expected = shape_cmd( + cmd2, no_glyph_names_process + ).strip() + + # If the removal of glyph_ids failed, fail the test. + # https://github.com/harfbuzz/harfbuzz/issues/5169 + if not final_glyphs_expected or final_glyphs != final_glyphs_expected: + print(hb_shape + " " + " ".join(cmd), file=sys.stderr) + print("Actual: " + glyphs, file=sys.stderr) + print("Expected: " + glyphs_expected, file=sys.stderr) + if final_glyphs != glyphs: + print( + "Actual (no glyph names): " + final_glyphs, + file=sys.stderr, + ) + print( + "Expected (no glyph names): " + final_glyphs_expected, + file=sys.stderr, + ) + fails += 1 + else: + passes += 1 + +print( + "%d tests passed; %d failed; %d skipped." % (passes, fails, skips), file=sys.stderr +) +if not (fails + passes): + print("No tests ran.") +elif not (fails + skips): + print("All tests passed.") + +if fails: + sys.exit(1) +elif passes: + sys.exit(0) +else: + sys.exit(77)
diff --git a/test/subset/run-repack-tests.py b/test/subset/run-repack-tests.py index a0389e5..406374b 100755 --- a/test/subset/run-repack-tests.py +++ b/test/subset/run-repack-tests.py
@@ -1,131 +1,3 @@ #!/usr/bin/env python3 -# Runs a subsetting test suite. Compares the results of subsetting via harfbuzz -# to subsetting via fonttools. - -from difflib import unified_diff -import os -import re -import subprocess -import sys -import tempfile -import shutil -import io - -from repack_test import RepackTest - -try: - from fontTools.ttLib import TTFont -except ImportError: - print("fonttools is not present, skipping test.") - sys.exit(77) - -ots_sanitize = shutil.which("ots-sanitize") - -EXE_WRAPPER = os.environ.get("MESON_EXE_WRAPPER") - - -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_name %s" % test.font_name) - print(" test.test_path %s" % os.path.abspath(test.test_path)) - return 1 - - -def run_test(test, should_check_ots): - out_file = os.path.join(out_dir, 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 - - -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.") -hb_subset, args = args[0], args[1:] - -if len(args) != 1: - sys.exit("No tests supplied.") - -has_ots = has_ots() - -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.") - -out_dir = tempfile.mkdtemp() - -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; output left in %s" % (fails, out_dir)) -else: - print("All tests passed.") - shutil.rmtree(out_dir) +import run_repack_tests_module
diff --git a/test/subset/run-tests.py b/test/subset/run-tests.py index b403bfa..2184a5f 100755 --- a/test/subset/run-tests.py +++ b/test/subset/run-tests.py
@@ -1,207 +1,3 @@ #!/usr/bin/env python3 -# Runs a subsetting test suite. Compares the results of subsetting via harfbuzz -# to subsetting via fonttools. - -from difflib import unified_diff -import os -import re -import subprocess -import sys -import tempfile -import shutil -import io - -from subset_test_suite import SubsetTestSuite - -try: - from fontTools.ttLib import TTFont -except ImportError: - TTFont = None - -ots_sanitize = shutil.which("ots-sanitize") - - -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( - out_dir, 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", - ] - ) - - 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 - - -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.") -hb_subset, args = args[0], args[1:] - -if not len(args): - sys.exit("No tests supplied.") - -has_ots = has_ots() - -env = os.environ.copy() -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() -out_dir = tempfile.mkdtemp() - -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) - -if fails != 0: - sys.exit("%d test(s) failed; output left in %s" % (fails, out_dir)) -else: - print("All tests passed.") - shutil.rmtree(out_dir) +import run_tests_module
diff --git a/test/subset/run_repack_tests_module.py b/test/subset/run_repack_tests_module.py new file mode 100755 index 0000000..a0389e5 --- /dev/null +++ b/test/subset/run_repack_tests_module.py
@@ -0,0 +1,131 @@ +#!/usr/bin/env python3 + +# Runs a subsetting test suite. Compares the results of subsetting via harfbuzz +# to subsetting via fonttools. + +from difflib import unified_diff +import os +import re +import subprocess +import sys +import tempfile +import shutil +import io + +from repack_test import RepackTest + +try: + from fontTools.ttLib import TTFont +except ImportError: + print("fonttools is not present, skipping test.") + sys.exit(77) + +ots_sanitize = shutil.which("ots-sanitize") + +EXE_WRAPPER = os.environ.get("MESON_EXE_WRAPPER") + + +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_name %s" % test.font_name) + print(" test.test_path %s" % os.path.abspath(test.test_path)) + return 1 + + +def run_test(test, should_check_ots): + out_file = os.path.join(out_dir, 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 + + +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.") +hb_subset, args = args[0], args[1:] + +if len(args) != 1: + sys.exit("No tests supplied.") + +has_ots = has_ots() + +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.") + +out_dir = tempfile.mkdtemp() + +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; output left in %s" % (fails, out_dir)) +else: + print("All tests passed.") + shutil.rmtree(out_dir)
diff --git a/test/subset/run_tests_module.py b/test/subset/run_tests_module.py new file mode 100755 index 0000000..b403bfa --- /dev/null +++ b/test/subset/run_tests_module.py
@@ -0,0 +1,207 @@ +#!/usr/bin/env python3 + +# Runs a subsetting test suite. Compares the results of subsetting via harfbuzz +# to subsetting via fonttools. + +from difflib import unified_diff +import os +import re +import subprocess +import sys +import tempfile +import shutil +import io + +from subset_test_suite import SubsetTestSuite + +try: + from fontTools.ttLib import TTFont +except ImportError: + TTFont = None + +ots_sanitize = shutil.which("ots-sanitize") + + +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( + out_dir, 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", + ] + ) + + 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 + + +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.") +hb_subset, args = args[0], args[1:] + +if not len(args): + sys.exit("No tests supplied.") + +has_ots = has_ots() + +env = os.environ.copy() +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() +out_dir = tempfile.mkdtemp() + +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) + +if fails != 0: + sys.exit("%d test(s) failed; output left in %s" % (fails, out_dir)) +else: + print("All tests passed.") + shutil.rmtree(out_dir)