1*3ac0a46fSAndroid Build Coastguard Worker#!/usr/bin/env python3 2*3ac0a46fSAndroid Build Coastguard Worker# Copyright 2017 The PDFium Authors 3*3ac0a46fSAndroid Build Coastguard Worker# Use of this source code is governed by a BSD-style license that can be 4*3ac0a46fSAndroid Build Coastguard Worker# found in the LICENSE file. 5*3ac0a46fSAndroid Build Coastguard Worker"""Verifies exported functions in public/*.h are in fpdf_view_c_api_test.c. 6*3ac0a46fSAndroid Build Coastguard Worker 7*3ac0a46fSAndroid Build Coastguard WorkerThis script gathers a list of functions from public/*.h that contain 8*3ac0a46fSAndroid Build Coastguard WorkerFPDF_EXPORT. It then gathers a list of functions from 9*3ac0a46fSAndroid Build Coastguard Workerfpdfsdk/fpdf_view_c_api_test.c. It then verifies both lists do not contain 10*3ac0a46fSAndroid Build Coastguard Workerduplicates, and they match each other. It also checks that the order in 11*3ac0a46fSAndroid Build Coastguard Workerfpdf_view_c_api_test.c is alphabetical within each section. 12*3ac0a46fSAndroid Build Coastguard Worker 13*3ac0a46fSAndroid Build Coastguard Worker""" 14*3ac0a46fSAndroid Build Coastguard Worker 15*3ac0a46fSAndroid Build Coastguard Workerimport os 16*3ac0a46fSAndroid Build Coastguard Workerimport re 17*3ac0a46fSAndroid Build Coastguard Workerimport sys 18*3ac0a46fSAndroid Build Coastguard Worker 19*3ac0a46fSAndroid Build Coastguard Worker 20*3ac0a46fSAndroid Build Coastguard Workerdef _IsValidFunctionName(function, filename): 21*3ac0a46fSAndroid Build Coastguard Worker if function.startswith('FPDF'): 22*3ac0a46fSAndroid Build Coastguard Worker return True 23*3ac0a46fSAndroid Build Coastguard Worker if function.startswith('FSDK_') and filename == 'fpdf_ext.h': 24*3ac0a46fSAndroid Build Coastguard Worker return True 25*3ac0a46fSAndroid Build Coastguard Worker if function.startswith('FORM_') and filename == 'fpdf_formfill.h': 26*3ac0a46fSAndroid Build Coastguard Worker return True 27*3ac0a46fSAndroid Build Coastguard Worker return False 28*3ac0a46fSAndroid Build Coastguard Worker 29*3ac0a46fSAndroid Build Coastguard Worker 30*3ac0a46fSAndroid Build Coastguard Workerdef _FindFunction(function_snippet, filename): 31*3ac0a46fSAndroid Build Coastguard Worker function_split = function_snippet.split('(') 32*3ac0a46fSAndroid Build Coastguard Worker assert len(function_split) == 2 33*3ac0a46fSAndroid Build Coastguard Worker function = function_split[0] 34*3ac0a46fSAndroid Build Coastguard Worker assert _IsValidFunctionName(function, filename) 35*3ac0a46fSAndroid Build Coastguard Worker return function 36*3ac0a46fSAndroid Build Coastguard Worker 37*3ac0a46fSAndroid Build Coastguard Worker 38*3ac0a46fSAndroid Build Coastguard Workerdef _GetExportsFromHeader(dirname, filename): 39*3ac0a46fSAndroid Build Coastguard Worker with open(os.path.join(dirname, filename)) as f: 40*3ac0a46fSAndroid Build Coastguard Worker contents = f.readlines() 41*3ac0a46fSAndroid Build Coastguard Worker look_for_function_name = False 42*3ac0a46fSAndroid Build Coastguard Worker functions = [] 43*3ac0a46fSAndroid Build Coastguard Worker for line in contents: 44*3ac0a46fSAndroid Build Coastguard Worker if look_for_function_name: 45*3ac0a46fSAndroid Build Coastguard Worker look_for_function_name = False 46*3ac0a46fSAndroid Build Coastguard Worker split_line = line.rstrip().split(' ') 47*3ac0a46fSAndroid Build Coastguard Worker functions.append(_FindFunction(split_line[0], filename)) 48*3ac0a46fSAndroid Build Coastguard Worker continue 49*3ac0a46fSAndroid Build Coastguard Worker 50*3ac0a46fSAndroid Build Coastguard Worker if not line.startswith('FPDF_EXPORT '): 51*3ac0a46fSAndroid Build Coastguard Worker continue 52*3ac0a46fSAndroid Build Coastguard Worker 53*3ac0a46fSAndroid Build Coastguard Worker # Format should be: FPDF_EXPORT return_type FPDF_CALLCONV 54*3ac0a46fSAndroid Build Coastguard Worker split_line = line.rstrip().split(' ') 55*3ac0a46fSAndroid Build Coastguard Worker callconv_index = split_line.index('FPDF_CALLCONV') 56*3ac0a46fSAndroid Build Coastguard Worker assert callconv_index >= 2 57*3ac0a46fSAndroid Build Coastguard Worker if callconv_index + 1 == len(split_line): 58*3ac0a46fSAndroid Build Coastguard Worker look_for_function_name = True 59*3ac0a46fSAndroid Build Coastguard Worker continue 60*3ac0a46fSAndroid Build Coastguard Worker 61*3ac0a46fSAndroid Build Coastguard Worker functions.append(_FindFunction(split_line[callconv_index + 1], filename)) 62*3ac0a46fSAndroid Build Coastguard Worker return functions 63*3ac0a46fSAndroid Build Coastguard Worker 64*3ac0a46fSAndroid Build Coastguard Worker 65*3ac0a46fSAndroid Build Coastguard Workerdef _GetFunctionsFromPublicHeaders(src_path): 66*3ac0a46fSAndroid Build Coastguard Worker public_path = os.path.join(src_path, 'public') 67*3ac0a46fSAndroid Build Coastguard Worker functions = [] 68*3ac0a46fSAndroid Build Coastguard Worker for filename in os.listdir(public_path): 69*3ac0a46fSAndroid Build Coastguard Worker if filename.endswith('.h'): 70*3ac0a46fSAndroid Build Coastguard Worker functions.extend(_GetExportsFromHeader(public_path, filename)) 71*3ac0a46fSAndroid Build Coastguard Worker return functions 72*3ac0a46fSAndroid Build Coastguard Worker 73*3ac0a46fSAndroid Build Coastguard Worker 74*3ac0a46fSAndroid Build Coastguard Workerdef _CheckSorted(functions): 75*3ac0a46fSAndroid Build Coastguard Worker unsorted_functions = set() 76*3ac0a46fSAndroid Build Coastguard Worker for i in range(len(functions) - 1): 77*3ac0a46fSAndroid Build Coastguard Worker if functions[i] > functions[i + 1]: 78*3ac0a46fSAndroid Build Coastguard Worker unsorted_functions.add(functions[i]) 79*3ac0a46fSAndroid Build Coastguard Worker unsorted_functions.add(functions[i + 1]) 80*3ac0a46fSAndroid Build Coastguard Worker return unsorted_functions 81*3ac0a46fSAndroid Build Coastguard Worker 82*3ac0a46fSAndroid Build Coastguard Worker 83*3ac0a46fSAndroid Build Coastguard Workerdef _GetFunctionsFromTest(api_test_path): 84*3ac0a46fSAndroid Build Coastguard Worker chk_regex = re.compile('^ CHK\((.*)\);\n$') 85*3ac0a46fSAndroid Build Coastguard Worker file_regex = re.compile('^ //.*\.h\n$') 86*3ac0a46fSAndroid Build Coastguard Worker with open(api_test_path) as f: 87*3ac0a46fSAndroid Build Coastguard Worker contents = f.readlines() 88*3ac0a46fSAndroid Build Coastguard Worker functions = [] 89*3ac0a46fSAndroid Build Coastguard Worker functions_in_file = [] 90*3ac0a46fSAndroid Build Coastguard Worker for line in contents: 91*3ac0a46fSAndroid Build Coastguard Worker if (file_regex.match(line)): 92*3ac0a46fSAndroid Build Coastguard Worker functions.append(functions_in_file) 93*3ac0a46fSAndroid Build Coastguard Worker functions_in_file = [] 94*3ac0a46fSAndroid Build Coastguard Worker match = chk_regex.match(line) 95*3ac0a46fSAndroid Build Coastguard Worker if match: 96*3ac0a46fSAndroid Build Coastguard Worker functions_in_file.append(match.groups()[0]) 97*3ac0a46fSAndroid Build Coastguard Worker functions.append(functions_in_file) 98*3ac0a46fSAndroid Build Coastguard Worker return functions 99*3ac0a46fSAndroid Build Coastguard Worker 100*3ac0a46fSAndroid Build Coastguard Worker 101*3ac0a46fSAndroid Build Coastguard Workerdef _FindDuplicates(functions): 102*3ac0a46fSAndroid Build Coastguard Worker return {f for f in functions if functions.count(f) > 1} 103*3ac0a46fSAndroid Build Coastguard Worker 104*3ac0a46fSAndroid Build Coastguard Worker 105*3ac0a46fSAndroid Build Coastguard Workerdef _CheckAndPrintFailures(failure_list, failure_message): 106*3ac0a46fSAndroid Build Coastguard Worker if not failure_list: 107*3ac0a46fSAndroid Build Coastguard Worker return True 108*3ac0a46fSAndroid Build Coastguard Worker 109*3ac0a46fSAndroid Build Coastguard Worker print('%s:' % failure_message) 110*3ac0a46fSAndroid Build Coastguard Worker for f in sorted(failure_list): 111*3ac0a46fSAndroid Build Coastguard Worker print(f) 112*3ac0a46fSAndroid Build Coastguard Worker return False 113*3ac0a46fSAndroid Build Coastguard Worker 114*3ac0a46fSAndroid Build Coastguard Worker 115*3ac0a46fSAndroid Build Coastguard Workerdef main(): 116*3ac0a46fSAndroid Build Coastguard Worker script_abspath = os.path.abspath(__file__) 117*3ac0a46fSAndroid Build Coastguard Worker src_path = os.path.dirname(os.path.dirname(os.path.dirname(script_abspath))) 118*3ac0a46fSAndroid Build Coastguard Worker public_functions = _GetFunctionsFromPublicHeaders(src_path) 119*3ac0a46fSAndroid Build Coastguard Worker 120*3ac0a46fSAndroid Build Coastguard Worker api_test_relative_path = os.path.join('fpdfsdk', 'fpdf_view_c_api_test.c') 121*3ac0a46fSAndroid Build Coastguard Worker api_test_path = os.path.join(src_path, api_test_relative_path) 122*3ac0a46fSAndroid Build Coastguard Worker test_functions_per_section = _GetFunctionsFromTest(api_test_path) 123*3ac0a46fSAndroid Build Coastguard Worker result = True 124*3ac0a46fSAndroid Build Coastguard Worker unsorted_functions = set() 125*3ac0a46fSAndroid Build Coastguard Worker for functions in test_functions_per_section: 126*3ac0a46fSAndroid Build Coastguard Worker unsorted_functions |= _CheckSorted(functions) 127*3ac0a46fSAndroid Build Coastguard Worker check = _CheckAndPrintFailures( 128*3ac0a46fSAndroid Build Coastguard Worker unsorted_functions, 129*3ac0a46fSAndroid Build Coastguard Worker 'Found CHKs that are not in alphabetical order within each section in %s' 130*3ac0a46fSAndroid Build Coastguard Worker % api_test_path) 131*3ac0a46fSAndroid Build Coastguard Worker result = result and check 132*3ac0a46fSAndroid Build Coastguard Worker 133*3ac0a46fSAndroid Build Coastguard Worker duplicate_public_functions = _FindDuplicates(public_functions) 134*3ac0a46fSAndroid Build Coastguard Worker check = _CheckAndPrintFailures(duplicate_public_functions, 135*3ac0a46fSAndroid Build Coastguard Worker 'Found duplicate functions in public headers') 136*3ac0a46fSAndroid Build Coastguard Worker result = result and check 137*3ac0a46fSAndroid Build Coastguard Worker 138*3ac0a46fSAndroid Build Coastguard Worker test_functions = [ 139*3ac0a46fSAndroid Build Coastguard Worker function for functions in test_functions_per_section 140*3ac0a46fSAndroid Build Coastguard Worker for function in functions 141*3ac0a46fSAndroid Build Coastguard Worker ] 142*3ac0a46fSAndroid Build Coastguard Worker duplicate_test_functions = _FindDuplicates(test_functions) 143*3ac0a46fSAndroid Build Coastguard Worker check = _CheckAndPrintFailures(duplicate_test_functions, 144*3ac0a46fSAndroid Build Coastguard Worker 'Found duplicate functions in API test') 145*3ac0a46fSAndroid Build Coastguard Worker result = result and check 146*3ac0a46fSAndroid Build Coastguard Worker 147*3ac0a46fSAndroid Build Coastguard Worker public_functions_set = set(public_functions) 148*3ac0a46fSAndroid Build Coastguard Worker test_functions_set = set(test_functions) 149*3ac0a46fSAndroid Build Coastguard Worker not_tested = public_functions_set.difference(test_functions_set) 150*3ac0a46fSAndroid Build Coastguard Worker check = _CheckAndPrintFailures(not_tested, 'Functions not tested') 151*3ac0a46fSAndroid Build Coastguard Worker result = result and check 152*3ac0a46fSAndroid Build Coastguard Worker non_existent = test_functions_set.difference(public_functions_set) 153*3ac0a46fSAndroid Build Coastguard Worker check = _CheckAndPrintFailures(non_existent, 'Tested functions do not exist') 154*3ac0a46fSAndroid Build Coastguard Worker result = result and check 155*3ac0a46fSAndroid Build Coastguard Worker 156*3ac0a46fSAndroid Build Coastguard Worker if not result: 157*3ac0a46fSAndroid Build Coastguard Worker print('Some checks failed. Make sure %s is in sync with the public API ' 158*3ac0a46fSAndroid Build Coastguard Worker 'headers.' % api_test_relative_path) 159*3ac0a46fSAndroid Build Coastguard Worker return 1 160*3ac0a46fSAndroid Build Coastguard Worker 161*3ac0a46fSAndroid Build Coastguard Worker return 0 162*3ac0a46fSAndroid Build Coastguard Worker 163*3ac0a46fSAndroid Build Coastguard Worker 164*3ac0a46fSAndroid Build Coastguard Workerif __name__ == '__main__': 165*3ac0a46fSAndroid Build Coastguard Worker sys.exit(main()) 166