mirror of
https://github.com/harfbuzz/harfbuzz.git
synced 2025-04-04 21:15:06 +00:00
332 lines
10 KiB
Python
Executable file
332 lines
10 KiB
Python
Executable file
#!/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)
|