255 lines
7.2 KiB
Python
Executable File
255 lines
7.2 KiB
Python
Executable File
#!/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)
|