Garret Rieger | 5c63c37 | 2018-01-26 16:57:42 -0800 | [diff] [blame] | 1 | #!/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 Byagowi | cab2c2c | 2018-03-29 12:48:47 +0430 | [diff] [blame] | 6 | from __future__ import print_function, division, absolute_import |
Garret Rieger | 5c63c37 | 2018-01-26 16:57:42 -0800 | [diff] [blame] | 7 | |
| 8 | import io |
Garret Rieger | 537698b | 2018-02-22 14:07:52 -0800 | [diff] [blame] | 9 | from difflib import unified_diff |
Garret Rieger | 5c63c37 | 2018-01-26 16:57:42 -0800 | [diff] [blame] | 10 | import os |
Garret Rieger | 04c1ec2 | 2018-02-14 17:00:18 -0800 | [diff] [blame] | 11 | import re |
Garret Rieger | 5c63c37 | 2018-01-26 16:57:42 -0800 | [diff] [blame] | 12 | import subprocess |
| 13 | import sys |
Rod Sheeter | e9d154a | 2018-01-30 19:27:11 -0800 | [diff] [blame] | 14 | import tempfile |
Garret Rieger | 5c63c37 | 2018-01-26 16:57:42 -0800 | [diff] [blame] | 15 | |
| 16 | from subset_test_suite import SubsetTestSuite |
| 17 | |
Ebrahim Byagowi | f57804a | 2018-06-25 18:45:49 +0430 | [diff] [blame] | 18 | # https://stackoverflow.com/a/377028 |
| 19 | def 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 | |
| 35 | ttx = which ("ttx") |
| 36 | ots_sanitize = which ("ots-sanitize") |
| 37 | |
| 38 | if not ttx: |
| 39 | print("TTX is not present, skipping test.") |
| 40 | sys.exit (77) |
Garret Rieger | 5c63c37 | 2018-01-26 16:57:42 -0800 | [diff] [blame] | 41 | |
| 42 | def cmd(command): |
| 43 | p = subprocess.Popen ( |
| 44 | command, stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
Ebrahim Byagowi | f57804a | 2018-06-25 18:45:49 +0430 | [diff] [blame] | 45 | (stdoutdata, stderrdata) = p.communicate () |
Garret Rieger | 9b00b9a | 2018-03-06 17:47:40 -0800 | [diff] [blame] | 46 | print (stderrdata, end="") # file=sys.stderr |
| 47 | return stdoutdata, p.returncode |
Garret Rieger | 5c63c37 | 2018-01-26 16:57:42 -0800 | [diff] [blame] | 48 | |
Ebrahim Byagowi | f57804a | 2018-06-25 18:45:49 +0430 | [diff] [blame] | 49 | def read_binary (file_path): |
| 50 | with open (file_path, 'rb') as f: |
| 51 | return f.read () |
Rod Sheeter | e9d154a | 2018-01-30 19:27:11 -0800 | [diff] [blame] | 52 | |
| 53 | def fail_test(test, cli_args, message): |
| 54 | print ('ERROR: %s' % message) |
| 55 | print ('Test State:') |
Ebrahim Byagowi | f57804a | 2018-06-25 18:45:49 +0430 | [diff] [blame] | 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)) |
Rod Sheeter | e9d154a | 2018-01-30 19:27:11 -0800 | [diff] [blame] | 62 | return 1 |
Garret Rieger | 5c63c37 | 2018-01-26 16:57:42 -0800 | [diff] [blame] | 63 | |
Garret Rieger | b4ba71e | 2018-02-28 15:44:00 -0800 | [diff] [blame] | 64 | def run_test(test, should_check_ots): |
Michiharu Ariza | bf4eb2e | 2018-09-18 15:53:37 -0700 | [diff] [blame] | 65 | out_file = os.path.join(tempfile.mkdtemp (), test.get_font_name () + '-subset' + test.get_font_extension ()) |
Garret Rieger | 058b126 | 2018-02-01 18:22:14 -0800 | [diff] [blame] | 66 | cli_args = [hb_subset, |
| 67 | "--font-file=" + test.font_path, |
| 68 | "--output-file=" + out_file, |
Ebrahim Byagowi | f57804a | 2018-06-25 18:45:49 +0430 | [diff] [blame] | 69 | "--unicodes=%s" % test.unicodes ()] |
| 70 | cli_args.extend (test.get_profile_flags ()) |
| 71 | print (' '.join (cli_args)) |
| 72 | _, return_code = cmd (cli_args) |
Garret Rieger | 5c63c37 | 2018-01-26 16:57:42 -0800 | [diff] [blame] | 73 | |
| 74 | if return_code: |
Ebrahim Byagowi | f57804a | 2018-06-25 18:45:49 +0430 | [diff] [blame] | 75 | return fail_test (test, cli_args, "%s returned %d" % (' '.join (cli_args), return_code)) |
Garret Rieger | 5c63c37 | 2018-01-26 16:57:42 -0800 | [diff] [blame] | 76 | |
Ebrahim Byagowi | f57804a | 2018-06-25 18:45:49 +0430 | [diff] [blame] | 77 | expected_ttx, return_code = run_ttx (os.path.join (test_suite.get_output_directory (), |
| 78 | test.get_font_name ())) |
Garret Rieger | 29d9152 | 2018-02-08 11:31:27 -0800 | [diff] [blame] | 79 | if return_code: |
Ebrahim Byagowi | f57804a | 2018-06-25 18:45:49 +0430 | [diff] [blame] | 80 | return fail_test (test, cli_args, "ttx (expected) returned %d" % (return_code)) |
Rod Sheeter | e9d154a | 2018-01-30 19:27:11 -0800 | [diff] [blame] | 81 | |
Garret Rieger | 29d9152 | 2018-02-08 11:31:27 -0800 | [diff] [blame] | 82 | actual_ttx, return_code = run_ttx(out_file) |
| 83 | if return_code: |
Ebrahim Byagowi | f57804a | 2018-06-25 18:45:49 +0430 | [diff] [blame] | 84 | return fail_test (test, cli_args, "ttx (actual) returned %d" % (return_code)) |
Rod Sheeter | e9d154a | 2018-01-30 19:27:11 -0800 | [diff] [blame] | 85 | |
Garret Rieger | bfec28a | 2018-03-06 15:43:08 -0800 | [diff] [blame] | 86 | print ("stripping checksums.") |
Garret Rieger | 04c1ec2 | 2018-02-14 17:00:18 -0800 | [diff] [blame] | 87 | expected_ttx = strip_check_sum (expected_ttx) |
Garret Rieger | 537698b | 2018-02-22 14:07:52 -0800 | [diff] [blame] | 88 | actual_ttx = strip_check_sum (actual_ttx) |
Garret Rieger | 04c1ec2 | 2018-02-14 17:00:18 -0800 | [diff] [blame] | 89 | |
Garret Rieger | f9420d9 | 2018-02-08 11:30:36 -0800 | [diff] [blame] | 90 | if not actual_ttx == expected_ttx: |
Ebrahim Byagowi | f57804a | 2018-06-25 18:45:49 +0430 | [diff] [blame] | 91 | for line in unified_diff (expected_ttx.splitlines (1), actual_ttx.splitlines (1)): |
| 92 | sys.stdout.write (line) |
| 93 | sys.stdout.flush () |
Garret Rieger | f9420d9 | 2018-02-08 11:30:36 -0800 | [diff] [blame] | 94 | return fail_test(test, cli_args, 'ttx for expected and actual does not match.') |
Garret Rieger | 5c63c37 | 2018-01-26 16:57:42 -0800 | [diff] [blame] | 95 | |
Garret Rieger | b4ba71e | 2018-02-28 15:44:00 -0800 | [diff] [blame] | 96 | if should_check_ots: |
| 97 | print ("Checking output with ots-sanitize.") |
Ebrahim Byagowi | f57804a | 2018-06-25 18:45:49 +0430 | [diff] [blame] | 98 | if not check_ots (out_file): |
| 99 | return fail_test (test, cli_args, 'ots for subsetted file fails.') |
Garret Rieger | b4ba71e | 2018-02-28 15:44:00 -0800 | [diff] [blame] | 100 | |
Garret Rieger | 5c63c37 | 2018-01-26 16:57:42 -0800 | [diff] [blame] | 101 | return 0 |
| 102 | |
Ebrahim Byagowi | f57804a | 2018-06-25 18:45:49 +0430 | [diff] [blame] | 103 | def run_ttx (file): |
Garret Rieger | bfec28a | 2018-03-06 15:43:08 -0800 | [diff] [blame] | 104 | print ("ttx %s" % file) |
Ebrahim Byagowi | f57804a | 2018-06-25 18:45:49 +0430 | [diff] [blame] | 105 | return cmd([ttx, "-q", "-o-", file]) |
Garret Rieger | f9420d9 | 2018-02-08 11:30:36 -0800 | [diff] [blame] | 106 | |
Garret Rieger | 04c1ec2 | 2018-02-14 17:00:18 -0800 | [diff] [blame] | 107 | def strip_check_sum (ttx_string): |
Garret Rieger | a88504c | 2018-02-22 14:28:18 -0800 | [diff] [blame] | 108 | return re.sub ('checkSumAdjustment value=["]0x([0-9a-fA-F])+["]', |
Garret Rieger | 5241d7f | 2018-02-27 13:15:40 -0800 | [diff] [blame] | 109 | 'checkSumAdjustment value="0x00000000"', |
Michiharu Ariza | bf4eb2e | 2018-09-18 15:53:37 -0700 | [diff] [blame] | 110 | ttx_string.decode ("utf-8"), count=1) |
Garret Rieger | 5c63c37 | 2018-01-26 16:57:42 -0800 | [diff] [blame] | 111 | |
Ebrahim Byagowi | 9206762 | 2018-03-09 15:43:03 +0330 | [diff] [blame] | 112 | def has_ots (): |
Ebrahim Byagowi | f57804a | 2018-06-25 18:45:49 +0430 | [diff] [blame] | 113 | if not ots_sanitize: |
Garret Rieger | b4ba71e | 2018-02-28 15:44:00 -0800 | [diff] [blame] | 114 | print("OTS is not present, skipping all ots checks.") |
| 115 | return False |
| 116 | return True |
| 117 | |
| 118 | def check_ots (path): |
Ebrahim Byagowi | f57804a | 2018-06-25 18:45:49 +0430 | [diff] [blame] | 119 | ots_report, returncode = cmd ([ots_sanitize, path]) |
Garret Rieger | b4ba71e | 2018-02-28 15:44:00 -0800 | [diff] [blame] | 120 | if returncode: |
| 121 | print("OTS Failure: %s" % ots_report); |
| 122 | return False |
| 123 | return True |
| 124 | |
Garret Rieger | 5c63c37 | 2018-01-26 16:57:42 -0800 | [diff] [blame] | 125 | args = sys.argv[1:] |
| 126 | if 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) |
| 129 | hb_subset, args = args[0], args[1:] |
| 130 | |
Ebrahim Byagowi | f57804a | 2018-06-25 18:45:49 +0430 | [diff] [blame] | 131 | if not len (args): |
Garret Rieger | 5c63c37 | 2018-01-26 16:57:42 -0800 | [diff] [blame] | 132 | print ("No tests supplied.") |
| 133 | sys.exit (1) |
| 134 | |
Garret Rieger | b4ba71e | 2018-02-28 15:44:00 -0800 | [diff] [blame] | 135 | has_ots = has_ots() |
| 136 | |
Garret Rieger | 5c63c37 | 2018-01-26 16:57:42 -0800 | [diff] [blame] | 137 | fails = 0 |
| 138 | for path in args: |
Ebrahim Byagowi | f57804a | 2018-06-25 18:45:49 +0430 | [diff] [blame] | 139 | with io.open (path, mode="r", encoding="utf-8") as f: |
Garret Rieger | 5c63c37 | 2018-01-26 16:57:42 -0800 | [diff] [blame] | 140 | print ("Running tests in " + path) |
Ebrahim Byagowi | f57804a | 2018-06-25 18:45:49 +0430 | [diff] [blame] | 141 | test_suite = SubsetTestSuite (path, f.read()) |
| 142 | for test in test_suite.tests (): |
| 143 | fails += run_test (test, has_ots) |
Garret Rieger | 5c63c37 | 2018-01-26 16:57:42 -0800 | [diff] [blame] | 144 | |
| 145 | if fails != 0: |
| 146 | print (str (fails) + " test(s) failed.") |
| 147 | sys.exit(1) |
| 148 | else: |
| 149 | print ("All tests passed.") |