blob: fb4684cf1533e0e5544739d51eaea3a82eac7a72 [file] [log] [blame]
Garret Rieger5c63c372018-01-26 16:57:42 -08001#!/usr/bin/env python
2
3# Runs a subsetting test suite. Compares the results of subsetting via harfbuz
4# to subsetting via fonttools.
5
Ebrahim Byagowicab2c2c2018-03-29 12:48:47 +04306from __future__ import print_function, division, absolute_import
Garret Rieger5c63c372018-01-26 16:57:42 -08007
8import io
Garret Rieger537698b2018-02-22 14:07:52 -08009from difflib import unified_diff
Garret Rieger5c63c372018-01-26 16:57:42 -080010import os
Garret Rieger04c1ec22018-02-14 17:00:18 -080011import re
Garret Rieger5c63c372018-01-26 16:57:42 -080012import subprocess
13import sys
Rod Sheetere9d154a2018-01-30 19:27:11 -080014import tempfile
Garret Rieger5c63c372018-01-26 16:57:42 -080015
16from subset_test_suite import SubsetTestSuite
17
Ebrahim Byagowif57804a2018-06-25 18:45:49 +043018# 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
35ttx = which ("ttx")
36ots_sanitize = which ("ots-sanitize")
37
38if not ttx:
39 print("TTX is not present, skipping test.")
40 sys.exit (77)
Garret Rieger5c63c372018-01-26 16:57:42 -080041
42def cmd(command):
43 p = subprocess.Popen (
44 command, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
Ebrahim Byagowif57804a2018-06-25 18:45:49 +043045 (stdoutdata, stderrdata) = p.communicate ()
Garret Rieger9b00b9a2018-03-06 17:47:40 -080046 print (stderrdata, end="") # file=sys.stderr
47 return stdoutdata, p.returncode
Garret Rieger5c63c372018-01-26 16:57:42 -080048
Ebrahim Byagowif57804a2018-06-25 18:45:49 +043049def read_binary (file_path):
50 with open (file_path, 'rb') as f:
51 return f.read ()
Rod Sheetere9d154a2018-01-30 19:27:11 -080052
53def fail_test(test, cli_args, message):
54 print ('ERROR: %s' % message)
55 print ('Test State:')
Ebrahim Byagowif57804a2018-06-25 18:45:49 +043056 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))
Rod Sheetere9d154a2018-01-30 19:27:11 -080062 return 1
Garret Rieger5c63c372018-01-26 16:57:42 -080063
Garret Riegerb4ba71e2018-02-28 15:44:00 -080064def run_test(test, should_check_ots):
Michiharu Arizabf4eb2e2018-09-18 15:53:37 -070065 out_file = os.path.join(tempfile.mkdtemp (), test.get_font_name () + '-subset' + test.get_font_extension ())
Garret Rieger058b1262018-02-01 18:22:14 -080066 cli_args = [hb_subset,
67 "--font-file=" + test.font_path,
68 "--output-file=" + out_file,
Ebrahim Byagowif57804a2018-06-25 18:45:49 +043069 "--unicodes=%s" % test.unicodes ()]
70 cli_args.extend (test.get_profile_flags ())
71 print (' '.join (cli_args))
72 _, return_code = cmd (cli_args)
Garret Rieger5c63c372018-01-26 16:57:42 -080073
74 if return_code:
Ebrahim Byagowif57804a2018-06-25 18:45:49 +043075 return fail_test (test, cli_args, "%s returned %d" % (' '.join (cli_args), return_code))
Garret Rieger5c63c372018-01-26 16:57:42 -080076
Ebrahim Byagowif57804a2018-06-25 18:45:49 +043077 expected_ttx, return_code = run_ttx (os.path.join (test_suite.get_output_directory (),
78 test.get_font_name ()))
Garret Rieger29d91522018-02-08 11:31:27 -080079 if return_code:
Ebrahim Byagowif57804a2018-06-25 18:45:49 +043080 return fail_test (test, cli_args, "ttx (expected) returned %d" % (return_code))
Rod Sheetere9d154a2018-01-30 19:27:11 -080081
Garret Rieger29d91522018-02-08 11:31:27 -080082 actual_ttx, return_code = run_ttx(out_file)
83 if return_code:
Ebrahim Byagowif57804a2018-06-25 18:45:49 +043084 return fail_test (test, cli_args, "ttx (actual) returned %d" % (return_code))
Rod Sheetere9d154a2018-01-30 19:27:11 -080085
Garret Riegerbfec28a2018-03-06 15:43:08 -080086 print ("stripping checksums.")
Garret Rieger04c1ec22018-02-14 17:00:18 -080087 expected_ttx = strip_check_sum (expected_ttx)
Garret Rieger537698b2018-02-22 14:07:52 -080088 actual_ttx = strip_check_sum (actual_ttx)
Garret Rieger04c1ec22018-02-14 17:00:18 -080089
Garret Riegerf9420d92018-02-08 11:30:36 -080090 if not actual_ttx == expected_ttx:
Ebrahim Byagowif57804a2018-06-25 18:45:49 +043091 for line in unified_diff (expected_ttx.splitlines (1), actual_ttx.splitlines (1)):
92 sys.stdout.write (line)
93 sys.stdout.flush ()
Garret Riegerf9420d92018-02-08 11:30:36 -080094 return fail_test(test, cli_args, 'ttx for expected and actual does not match.')
Garret Rieger5c63c372018-01-26 16:57:42 -080095
Garret Riegerb4ba71e2018-02-28 15:44:00 -080096 if should_check_ots:
97 print ("Checking output with ots-sanitize.")
Ebrahim Byagowif57804a2018-06-25 18:45:49 +043098 if not check_ots (out_file):
99 return fail_test (test, cli_args, 'ots for subsetted file fails.')
Garret Riegerb4ba71e2018-02-28 15:44:00 -0800100
Garret Rieger5c63c372018-01-26 16:57:42 -0800101 return 0
102
Ebrahim Byagowif57804a2018-06-25 18:45:49 +0430103def run_ttx (file):
Garret Riegerbfec28a2018-03-06 15:43:08 -0800104 print ("ttx %s" % file)
Ebrahim Byagowif57804a2018-06-25 18:45:49 +0430105 return cmd([ttx, "-q", "-o-", file])
Garret Riegerf9420d92018-02-08 11:30:36 -0800106
Garret Rieger04c1ec22018-02-14 17:00:18 -0800107def strip_check_sum (ttx_string):
Garret Riegera88504c2018-02-22 14:28:18 -0800108 return re.sub ('checkSumAdjustment value=["]0x([0-9a-fA-F])+["]',
Garret Rieger5241d7f2018-02-27 13:15:40 -0800109 'checkSumAdjustment value="0x00000000"',
Michiharu Arizabf4eb2e2018-09-18 15:53:37 -0700110 ttx_string.decode ("utf-8"), count=1)
Garret Rieger5c63c372018-01-26 16:57:42 -0800111
Ebrahim Byagowi92067622018-03-09 15:43:03 +0330112def has_ots ():
Ebrahim Byagowif57804a2018-06-25 18:45:49 +0430113 if not ots_sanitize:
Garret Riegerb4ba71e2018-02-28 15:44:00 -0800114 print("OTS is not present, skipping all ots checks.")
115 return False
116 return True
117
118def check_ots (path):
Ebrahim Byagowif57804a2018-06-25 18:45:49 +0430119 ots_report, returncode = cmd ([ots_sanitize, path])
Garret Riegerb4ba71e2018-02-28 15:44:00 -0800120 if returncode:
121 print("OTS Failure: %s" % ots_report);
122 return False
123 return True
124
Garret Rieger5c63c372018-01-26 16:57:42 -0800125args = sys.argv[1:]
126if not args or sys.argv[1].find('hb-subset') == -1 or not os.path.exists (sys.argv[1]):
127 print ("First argument does not seem to point to usable hb-subset.")
128 sys.exit (1)
129hb_subset, args = args[0], args[1:]
130
Ebrahim Byagowif57804a2018-06-25 18:45:49 +0430131if not len (args):
Garret Rieger5c63c372018-01-26 16:57:42 -0800132 print ("No tests supplied.")
133 sys.exit (1)
134
Garret Riegerb4ba71e2018-02-28 15:44:00 -0800135has_ots = has_ots()
136
Garret Rieger5c63c372018-01-26 16:57:42 -0800137fails = 0
138for path in args:
Ebrahim Byagowif57804a2018-06-25 18:45:49 +0430139 with io.open (path, mode="r", encoding="utf-8") as f:
Garret Rieger5c63c372018-01-26 16:57:42 -0800140 print ("Running tests in " + path)
Ebrahim Byagowif57804a2018-06-25 18:45:49 +0430141 test_suite = SubsetTestSuite (path, f.read())
142 for test in test_suite.tests ():
143 fails += run_test (test, has_ots)
Garret Rieger5c63c372018-01-26 16:57:42 -0800144
145if fails != 0:
146 print (str (fails) + " test(s) failed.")
147 sys.exit(1)
148else:
149 print ("All tests passed.")