• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/env python
2
3# Runs a subsetting test suite. Compares the results of subsetting via harfbuzz
4# to subsetting via fonttools.
5
6from __future__ import print_function, division, absolute_import
7
8import io
9from difflib import unified_diff
10import os
11import re
12import subprocess
13import sys
14import tempfile
15
16from subset_test_suite import SubsetTestSuite
17
18# https://stackoverflow.com/a/377028
19def which (program):
20	def is_exe (fpath):
21		return os.path.isfile (fpath) and os.access (fpath, os.X_OK)
22
23	fpath, _ = os.path.split (program)
24	if fpath:
25		if is_exe (program):
26			return program
27	else:
28		for path in os.environ["PATH"].split (os.pathsep):
29			exe_file = os.path.join (path, program)
30			if is_exe (exe_file):
31				return exe_file
32
33	return None
34
35fonttools = which ("fonttools")
36ots_sanitize = which ("ots-sanitize")
37
38if not fonttools:
39	print ("fonttools is not present, skipping test.")
40	sys.exit (77)
41
42def cmd(command):
43	p = subprocess.Popen (
44		command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
45	(stdoutdata, stderrdata) = p.communicate ()
46	print (stderrdata, end="") # file=sys.stderr
47	return stdoutdata, p.returncode
48
49def read_binary (file_path):
50	with open (file_path, 'rb') as f:
51		return f.read ()
52
53def fail_test (test, cli_args, message):
54	print ('ERROR: %s' % message)
55	print ('Test State:')
56	print ('  test.font_path    %s' % os.path.abspath (test.font_path))
57	print ('  test.profile_path %s' % os.path.abspath (test.profile_path))
58	print ('  test.unicodes	    %s' % test.unicodes ())
59	expected_file = os.path.join(test_suite.get_output_directory (),
60				     test.get_font_name ())
61	print ('  expected_file	    %s' % os.path.abspath (expected_file))
62	return 1
63
64def run_test (test, should_check_ots):
65	out_file = os.path.join (tempfile.mkdtemp (), test.get_font_name () + '-subset' + test.get_font_extension ())
66	cli_args = [hb_subset,
67		    "--font-file=" + test.font_path,
68		    "--output-file=" + out_file,
69		    "--unicodes=%s" % test.unicodes (),
70		    "--drop-tables+=DSIG,GPOS,GSUB,GDEF,gvar,avar,MVAR,HVAR"]
71	cli_args.extend (test.get_profile_flags ())
72	print (' '.join (cli_args))
73	_, return_code = cmd (cli_args)
74
75	if return_code:
76		return fail_test (test, cli_args, "%s returned %d" % (' '.join (cli_args), return_code))
77
78	expected_ttx, return_code = run_ttx (os.path.join (test_suite.get_output_directory (),
79					     test.get_font_name ()))
80	if return_code:
81		return fail_test (test, cli_args, "ttx (expected) returned %d" % (return_code))
82
83	actual_ttx, return_code = run_ttx (out_file)
84	if return_code:
85		return fail_test (test, cli_args, "ttx (actual) returned %d" % (return_code))
86
87	print ("stripping checksums.")
88	expected_ttx = strip_check_sum (expected_ttx)
89	actual_ttx = strip_check_sum (actual_ttx)
90
91	if not actual_ttx == expected_ttx:
92		for line in unified_diff (expected_ttx.splitlines (1), actual_ttx.splitlines (1)):
93			sys.stdout.write (line)
94		sys.stdout.flush ()
95		return fail_test (test, cli_args, 'ttx for expected and actual does not match.')
96
97	if should_check_ots:
98		print ("Checking output with ots-sanitize.")
99		if not check_ots (out_file):
100			return fail_test (test, cli_args, 'ots for subsetted file fails.')
101
102	return 0
103
104def run_ttx (file):
105	print ("fonttools ttx %s" % file)
106	return cmd ([fonttools, "ttx", "-q", "-o-", file])
107
108def strip_check_sum (ttx_string):
109	return re.sub ('checkSumAdjustment value=["]0x([0-9a-fA-F])+["]',
110		       'checkSumAdjustment value="0x00000000"',
111		       ttx_string.decode ("utf-8"), count=1)
112
113def has_ots ():
114	if not ots_sanitize:
115		print("OTS is not present, skipping all ots checks.")
116		return False
117	return True
118
119def check_ots (path):
120	ots_report, returncode = cmd ([ots_sanitize, path])
121	if returncode:
122		print("OTS Failure: %s" % ots_report);
123		return False
124	return True
125
126args = sys.argv[1:]
127if not args or sys.argv[1].find ('hb-subset') == -1 or not os.path.exists (sys.argv[1]):
128	print ("First argument does not seem to point to usable hb-subset.")
129	sys.exit (1)
130hb_subset, args = args[0], args[1:]
131
132if not len (args):
133	print ("No tests supplied.")
134	sys.exit (1)
135
136has_ots = has_ots()
137
138fails = 0
139for path in args:
140	with io.open (path, mode="r", encoding="utf-8") as f:
141		print ("Running tests in " + path)
142		test_suite = SubsetTestSuite (path, f.read ())
143		for test in test_suite.tests ():
144			fails += run_test (test, has_ots)
145
146if fails != 0:
147	print (str (fails) + " test(s) failed.")
148	sys.exit(1)
149else:
150	print ("All tests passed.")
151