blob: 8436900d9bce00f1eca36a43651572849fed5a09 [file] [log] [blame] [edit]
#!/usr/bin/env python3
import os
import re
import shlex
import subprocess
import sys
import tempfile
from difflib import unified_diff
print("TAP version 14")
args = sys.argv[1:]
if (
not args
or not os.path.exists(args[0])
or "hb-vector" not in args[0]
):
sys.exit("First argument does not seem to point to usable hb-vector.")
hb_vector, args = args[0], args[1:]
if not args:
args = ["-"]
env = os.environ.copy()
env["LC_ALL"] = "C"
EXE_WRAPPER = os.environ.get("MESON_EXE_WRAPPER")
def resolve_path(base, path):
if os.path.isabs(path):
return path
return os.path.normpath(os.path.join(base, path))
def open_vector_batch_process():
cmd = [hb_vector, "--batch"]
if EXE_WRAPPER:
cmd = [EXE_WRAPPER] + cmd
process = subprocess.Popen(
cmd,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE,
stderr=subprocess.STDOUT,
env=env,
text=True,
)
return [process]
def close_vector_batch_process(vector_process):
process = vector_process[0]
if process is None:
return
if process.stdin:
process.stdin.close()
if process.stdout:
process.stdout.close()
process.wait()
vector_process[0] = None
vector_process = open_vector_batch_process()
def build_vector_command(font_path, options, unicodes, output_path):
command = []
if options:
command.extend(shlex.split(options))
command.extend([f"--output-file={output_path}", f"--unicodes={unicodes}", font_path])
return command
def run_vector_single(command):
cmd = [hb_vector]
cmd.extend(command)
if EXE_WRAPPER:
cmd = [EXE_WRAPPER] + cmd
return subprocess.run(cmd, env=env, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
def vector_cmd(command, vector_process):
process = vector_process[0]
# (Re)start vector tool if it is dead.
if process.poll() is not None:
if process.stdin:
process.stdin.close()
if process.stdout:
process.stdout.close()
vector_process[0] = open_vector_batch_process()[0]
process = vector_process[0]
process.stdin.write(";".join(command) + "\n")
process.stdin.flush()
diagnostics = []
while True:
line = process.stdout.readline()
if line == "":
return None, diagnostics
line = line.rstrip("\n")
if line in ("success", "failure"):
return line, diagnostics
diagnostics.append(line)
def run_vector(font_path, options, unicodes, output_path):
command = build_vector_command(font_path, options, unicodes, output_path)
status, diagnostics = vector_cmd(command, vector_process)
if status == "success":
return subprocess.CompletedProcess(
args=command,
returncode=0,
stdout="",
stderr="\n".join(diagnostics),
)
# Fall back to one-shot execution for proper diagnostics.
result = run_vector_single(command)
if diagnostics:
stderr = "\n".join(diagnostics)
if result.stderr:
stderr += "\n" + result.stderr
result = subprocess.CompletedProcess(
args=result.args,
returncode=result.returncode,
stdout=result.stdout,
stderr=stderr,
)
return result
def parse_unicodes(s):
# Match test/shape/hb_test_tools.py Unicode.parse semantics.
s = re.sub(r"0[xX]", " ", s)
s = re.sub(r"[<+\->{},;&#\\xXuUnNiI\n\t]", " ", s)
try:
return "".join(chr(int(x, 16)) for x in s.split())
except ValueError as e:
raise ValueError(f"invalid unicode codepoint list: {s!r}") from e
number = 0
fails = 0
try:
for filename in args:
if filename == "-":
f = sys.stdin
base = os.getcwd()
print("# Running tests from standard input")
else:
f = open(filename, encoding="utf-8")
base = os.path.dirname(os.path.abspath(filename))
print("# Running tests in " + filename)
for lineno, line in enumerate(f, start=1):
line = line.strip()
if not line or line.startswith("#"):
continue
number += 1
fields = line.split(";", 3)
if len(fields) != 4:
fails += 1
print(f"not ok {number} - parse:{filename}:{lineno}")
print("# malformed test line, expected 4 ';'-separated fields")
continue
font_file, options, unicodes, expected_file = fields
name = expected_file
font_path = resolve_path(base, font_file)
expected_path = resolve_path(base, expected_file)
if not os.path.exists(font_path):
fails += 1
print(f"not ok {number} - {name}")
print(f"# font file not found: {font_path}")
continue
if not os.path.exists(expected_path):
fails += 1
print(f"not ok {number} - {name}")
print(f"# expected output file not found: {expected_path}")
continue
try:
parse_unicodes(unicodes)
except ValueError as e:
fails += 1
print(f"not ok {number} - {name}")
print("# " + str(e))
continue
with tempfile.NamedTemporaryFile(prefix="hb-vector-test-", suffix=".svg", delete=False) as tmp:
output_path = tmp.name
try:
result = run_vector(font_path, options, unicodes, output_path)
if result.returncode != 0:
fails += 1
print(f"not ok {number} - {name}")
print(f"# hb-vector exited with code {result.returncode}")
stderr = result.stderr.strip()
if stderr:
for l in stderr.splitlines():
print("# " + l)
continue
if not os.path.exists(output_path):
fails += 1
print(f"not ok {number} - {name}")
print("# hb-vector did not create output file")
continue
with open(expected_path, encoding="utf-8") as fp:
expected = fp.read()
with open(output_path, encoding="utf-8") as fp:
actual = fp.read()
if actual == expected:
print(f"ok {number} - {name}")
continue
fails += 1
print(f"not ok {number} - {name}")
diff = unified_diff(
expected.splitlines(keepends=True),
actual.splitlines(keepends=True),
fromfile=expected_path,
tofile="actual",
)
for diff_line in diff:
print("# " + diff_line.rstrip("\n"))
finally:
if os.path.exists(output_path):
os.unlink(output_path)
if f is not sys.stdin:
f.close()
finally:
close_vector_batch_process(vector_process)
print(f"1..{number}")
if fails:
sys.exit(1)