1*2d1272b8SAndroid Build Coastguard Worker#!/usr/bin/env python3 2*2d1272b8SAndroid Build Coastguard Worker 3*2d1272b8SAndroid Build Coastguard Worker# Runs a subsetting test suite. Compares the results of subsetting via harfbuzz 4*2d1272b8SAndroid Build Coastguard Worker# to subsetting via fonttools. 5*2d1272b8SAndroid Build Coastguard Worker 6*2d1272b8SAndroid Build Coastguard Workerfrom difflib import unified_diff 7*2d1272b8SAndroid Build Coastguard Workerimport os 8*2d1272b8SAndroid Build Coastguard Workerimport re 9*2d1272b8SAndroid Build Coastguard Workerimport subprocess 10*2d1272b8SAndroid Build Coastguard Workerimport sys 11*2d1272b8SAndroid Build Coastguard Workerimport tempfile 12*2d1272b8SAndroid Build Coastguard Workerimport shutil 13*2d1272b8SAndroid Build Coastguard Workerimport io 14*2d1272b8SAndroid Build Coastguard Worker 15*2d1272b8SAndroid Build Coastguard Workerfrom repack_test import RepackTest 16*2d1272b8SAndroid Build Coastguard Worker 17*2d1272b8SAndroid Build Coastguard Workertry: 18*2d1272b8SAndroid Build Coastguard Worker from fontTools.ttLib import TTFont 19*2d1272b8SAndroid Build Coastguard Workerexcept ImportError: 20*2d1272b8SAndroid Build Coastguard Worker print ("fonttools is not present, skipping test.") 21*2d1272b8SAndroid Build Coastguard Worker sys.exit (77) 22*2d1272b8SAndroid Build Coastguard Worker 23*2d1272b8SAndroid Build Coastguard Workerots_sanitize = shutil.which ("ots-sanitize") 24*2d1272b8SAndroid Build Coastguard Worker 25*2d1272b8SAndroid Build Coastguard Workerdef subset_cmd (command): 26*2d1272b8SAndroid Build Coastguard Worker global hb_subset, process 27*2d1272b8SAndroid Build Coastguard Worker print (hb_subset + ' ' + " ".join(command)) 28*2d1272b8SAndroid Build Coastguard Worker process.stdin.write ((';'.join (command) + '\n').encode ("utf-8")) 29*2d1272b8SAndroid Build Coastguard Worker process.stdin.flush () 30*2d1272b8SAndroid Build Coastguard Worker return process.stdout.readline().decode ("utf-8").strip () 31*2d1272b8SAndroid Build Coastguard Worker 32*2d1272b8SAndroid Build Coastguard Workerdef cmd (command): 33*2d1272b8SAndroid Build Coastguard Worker p = subprocess.Popen ( 34*2d1272b8SAndroid Build Coastguard Worker command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, 35*2d1272b8SAndroid Build Coastguard Worker universal_newlines=True) 36*2d1272b8SAndroid Build Coastguard Worker (stdoutdata, stderrdata) = p.communicate () 37*2d1272b8SAndroid Build Coastguard Worker print (stderrdata, end="", file=sys.stderr) 38*2d1272b8SAndroid Build Coastguard Worker return stdoutdata, p.returncode 39*2d1272b8SAndroid Build Coastguard Worker 40*2d1272b8SAndroid Build Coastguard Workerdef fail_test (test, cli_args, message): 41*2d1272b8SAndroid Build Coastguard Worker print ('ERROR: %s' % message) 42*2d1272b8SAndroid Build Coastguard Worker print ('Test State:') 43*2d1272b8SAndroid Build Coastguard Worker print (' test.font_name %s' % test.font_name) 44*2d1272b8SAndroid Build Coastguard Worker print (' test.test_path %s' % os.path.abspath (test.test_path)) 45*2d1272b8SAndroid Build Coastguard Worker return 1 46*2d1272b8SAndroid Build Coastguard Worker 47*2d1272b8SAndroid Build Coastguard Workerdef run_test (test, should_check_ots): 48*2d1272b8SAndroid Build Coastguard Worker out_file = os.path.join (tempfile.mkdtemp (), test.font_name + '-subset.ttf') 49*2d1272b8SAndroid Build Coastguard Worker cli_args = ["--font-file=" + test.font_path (), 50*2d1272b8SAndroid Build Coastguard Worker "--output-file=" + out_file, 51*2d1272b8SAndroid Build Coastguard Worker "--unicodes=%s" % test.codepoints_string (), 52*2d1272b8SAndroid Build Coastguard Worker "--drop-tables-=GPOS,GSUB,GDEF",] 53*2d1272b8SAndroid Build Coastguard Worker print (' '.join (cli_args)) 54*2d1272b8SAndroid Build Coastguard Worker ret = subset_cmd (cli_args) 55*2d1272b8SAndroid Build Coastguard Worker 56*2d1272b8SAndroid Build Coastguard Worker if ret != "success": 57*2d1272b8SAndroid Build Coastguard Worker return fail_test (test, cli_args, "%s failed" % ' '.join (cli_args)) 58*2d1272b8SAndroid Build Coastguard Worker 59*2d1272b8SAndroid Build Coastguard Worker try: 60*2d1272b8SAndroid Build Coastguard Worker with TTFont (out_file) as font: 61*2d1272b8SAndroid Build Coastguard Worker pass 62*2d1272b8SAndroid Build Coastguard Worker except Exception as e: 63*2d1272b8SAndroid Build Coastguard Worker print (e) 64*2d1272b8SAndroid Build Coastguard Worker return fail_test (test, cli_args, "ttx failed to parse the result") 65*2d1272b8SAndroid Build Coastguard Worker 66*2d1272b8SAndroid Build Coastguard Worker if should_check_ots: 67*2d1272b8SAndroid Build Coastguard Worker print ("Checking output with ots-sanitize.") 68*2d1272b8SAndroid Build Coastguard Worker if not check_ots (out_file): 69*2d1272b8SAndroid Build Coastguard Worker return fail_test (test, cli_args, 'ots for subsetted file fails.') 70*2d1272b8SAndroid Build Coastguard Worker 71*2d1272b8SAndroid Build Coastguard Worker return 0 72*2d1272b8SAndroid Build Coastguard Worker 73*2d1272b8SAndroid Build Coastguard Workerdef has_ots (): 74*2d1272b8SAndroid Build Coastguard Worker if not ots_sanitize: 75*2d1272b8SAndroid Build Coastguard Worker print ("OTS is not present, skipping all ots checks.") 76*2d1272b8SAndroid Build Coastguard Worker return False 77*2d1272b8SAndroid Build Coastguard Worker return True 78*2d1272b8SAndroid Build Coastguard Worker 79*2d1272b8SAndroid Build Coastguard Workerdef check_ots (path): 80*2d1272b8SAndroid Build Coastguard Worker ots_report, returncode = cmd ([ots_sanitize, path]) 81*2d1272b8SAndroid Build Coastguard Worker if returncode: 82*2d1272b8SAndroid Build Coastguard Worker print ("OTS Failure: %s" % ots_report) 83*2d1272b8SAndroid Build Coastguard Worker return False 84*2d1272b8SAndroid Build Coastguard Worker return True 85*2d1272b8SAndroid Build Coastguard Worker 86*2d1272b8SAndroid Build Coastguard Workerargs = sys.argv[1:] 87*2d1272b8SAndroid Build Coastguard Workerif not args or sys.argv[1].find ('hb-subset') == -1 or not os.path.exists (sys.argv[1]): 88*2d1272b8SAndroid Build Coastguard Worker sys.exit ("First argument does not seem to point to usable hb-subset.") 89*2d1272b8SAndroid Build Coastguard Workerhb_subset, args = args[0], args[1:] 90*2d1272b8SAndroid Build Coastguard Worker 91*2d1272b8SAndroid Build Coastguard Workerif len (args) != 1: 92*2d1272b8SAndroid Build Coastguard Worker sys.exit ("No tests supplied.") 93*2d1272b8SAndroid Build Coastguard Worker 94*2d1272b8SAndroid Build Coastguard Workerhas_ots = has_ots() 95*2d1272b8SAndroid Build Coastguard Worker 96*2d1272b8SAndroid Build Coastguard Workerprocess = subprocess.Popen ([hb_subset, '--batch'], 97*2d1272b8SAndroid Build Coastguard Worker stdin=subprocess.PIPE, 98*2d1272b8SAndroid Build Coastguard Worker stdout=subprocess.PIPE, 99*2d1272b8SAndroid Build Coastguard Worker stderr=sys.stdout) 100*2d1272b8SAndroid Build Coastguard Worker 101*2d1272b8SAndroid Build Coastguard Workerfails = 0 102*2d1272b8SAndroid Build Coastguard Worker 103*2d1272b8SAndroid Build Coastguard Workerpath = args[0] 104*2d1272b8SAndroid Build Coastguard Workerif not path.endswith(".tests"): 105*2d1272b8SAndroid Build Coastguard Worker sys.exit ("Not a valid test case path.") 106*2d1272b8SAndroid Build Coastguard Worker 107*2d1272b8SAndroid Build Coastguard Workerwith open (path, mode="r", encoding="utf-8") as f: 108*2d1272b8SAndroid Build Coastguard Worker # TODO(garretrieger): re-enable OTS checking. 109*2d1272b8SAndroid Build Coastguard Worker fails += run_test (RepackTest (path, f.read ()), False) 110*2d1272b8SAndroid Build Coastguard Worker 111*2d1272b8SAndroid Build Coastguard Worker 112*2d1272b8SAndroid Build Coastguard Workerif fails != 0: 113*2d1272b8SAndroid Build Coastguard Worker sys.exit ("%d test(s) failed." % fails) 114*2d1272b8SAndroid Build Coastguard Workerelse: 115*2d1272b8SAndroid Build Coastguard Worker print ("All tests passed.") 116