You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
166 lines
5.2 KiB
166 lines
5.2 KiB
#!/usr/bin/env python
|
|
# Copyright 2017 The PDFium Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
"""Verifies exported functions in public/*.h are in fpdf_view_c_api_test.c.
|
|
|
|
This script gathers a list of functions from public/*.h that contain
|
|
FPDF_EXPORT. It then gathers a list of functions from
|
|
fpdfsdk/fpdf_view_c_api_test.c. It then verifies both lists do not contain
|
|
duplicates, and they match each other. It also checks that the order in
|
|
fpdf_view_c_api_test.c is alphabetical within each section.
|
|
|
|
"""
|
|
|
|
import os
|
|
import re
|
|
import sys
|
|
|
|
|
|
def _IsValidFunctionName(function, filename):
|
|
if function.startswith('FPDF'):
|
|
return True
|
|
if function.startswith('FSDK_') and filename == 'fpdf_ext.h':
|
|
return True
|
|
if function.startswith('FORM_') and filename == 'fpdf_formfill.h':
|
|
return True
|
|
return False
|
|
|
|
|
|
def _FindFunction(function_snippet, filename):
|
|
function_split = function_snippet.split('(')
|
|
assert len(function_split) == 2
|
|
function = function_split[0]
|
|
assert _IsValidFunctionName(function, filename)
|
|
return function
|
|
|
|
|
|
def _GetExportsFromHeader(dirname, filename):
|
|
with open(os.path.join(dirname, filename)) as f:
|
|
contents = f.readlines()
|
|
look_for_function_name = False
|
|
functions = []
|
|
for line in contents:
|
|
if look_for_function_name:
|
|
look_for_function_name = False
|
|
split_line = line.rstrip().split(' ')
|
|
functions.append(_FindFunction(split_line[0], filename))
|
|
continue
|
|
|
|
if not line.startswith('FPDF_EXPORT '):
|
|
continue
|
|
|
|
# Format should be: FPDF_EXPORT return_type FPDF_CALLCONV
|
|
split_line = line.rstrip().split(' ')
|
|
callconv_index = split_line.index('FPDF_CALLCONV')
|
|
assert callconv_index >= 2
|
|
if callconv_index + 1 == len(split_line):
|
|
look_for_function_name = True
|
|
continue
|
|
|
|
functions.append(_FindFunction(split_line[callconv_index + 1], filename))
|
|
return functions
|
|
|
|
|
|
def _GetFunctionsFromPublicHeaders(src_path):
|
|
public_path = os.path.join(src_path, 'public')
|
|
functions = []
|
|
for filename in os.listdir(public_path):
|
|
if filename.endswith('.h'):
|
|
functions.extend(_GetExportsFromHeader(public_path, filename))
|
|
return functions
|
|
|
|
|
|
def _CheckSorted(functions):
|
|
unsorted_functions = set()
|
|
for i in range(len(functions) - 1):
|
|
if functions[i] > functions[i + 1]:
|
|
unsorted_functions.add(functions[i])
|
|
unsorted_functions.add(functions[i + 1])
|
|
return unsorted_functions
|
|
|
|
|
|
def _GetFunctionsFromTest(api_test_path):
|
|
chk_regex = re.compile('^ CHK\((.*)\);\n$')
|
|
file_regex = re.compile('^ //.*\.h\n$')
|
|
with open(api_test_path) as f:
|
|
contents = f.readlines()
|
|
functions = []
|
|
functions_in_file = []
|
|
for line in contents:
|
|
if (file_regex.match(line)):
|
|
functions.append(functions_in_file)
|
|
functions_in_file = []
|
|
match = chk_regex.match(line)
|
|
if match:
|
|
functions_in_file.append(match.groups()[0])
|
|
functions.append(functions_in_file)
|
|
return functions
|
|
|
|
|
|
def _FindDuplicates(functions):
|
|
return set([f for f in functions if functions.count(f) > 1])
|
|
|
|
|
|
def _CheckAndPrintFailures(failure_list, failure_message):
|
|
if not failure_list:
|
|
return True
|
|
|
|
print '%s:' % failure_message
|
|
for f in sorted(failure_list):
|
|
print f
|
|
return False
|
|
|
|
|
|
def main():
|
|
script_abspath = os.path.abspath(__file__)
|
|
src_path = os.path.dirname(os.path.dirname(os.path.dirname(script_abspath)))
|
|
public_functions = _GetFunctionsFromPublicHeaders(src_path)
|
|
|
|
api_test_relative_path = os.path.join('fpdfsdk', 'fpdf_view_c_api_test.c')
|
|
api_test_path = os.path.join(src_path, api_test_relative_path)
|
|
test_functions_per_section = _GetFunctionsFromTest(api_test_path)
|
|
result = True
|
|
unsorted_functions = set()
|
|
for functions in test_functions_per_section:
|
|
unsorted_functions |= _CheckSorted(functions)
|
|
check = _CheckAndPrintFailures(
|
|
unsorted_functions,
|
|
'Found CHKs that are not in alphabetical order within each section in %s'
|
|
% api_test_path)
|
|
result = result and check
|
|
|
|
duplicate_public_functions = _FindDuplicates(public_functions)
|
|
check = _CheckAndPrintFailures(duplicate_public_functions,
|
|
'Found duplicate functions in public headers')
|
|
result = result and check
|
|
|
|
test_functions = [
|
|
function for functions in test_functions_per_section
|
|
for function in functions
|
|
]
|
|
duplicate_test_functions = _FindDuplicates(test_functions)
|
|
check = _CheckAndPrintFailures(duplicate_test_functions,
|
|
'Found duplicate functions in API test')
|
|
result = result and check
|
|
|
|
public_functions_set = set(public_functions)
|
|
test_functions_set = set(test_functions)
|
|
not_tested = public_functions_set.difference(test_functions_set)
|
|
check = _CheckAndPrintFailures(not_tested, 'Functions not tested')
|
|
result = result and check
|
|
non_existent = test_functions_set.difference(public_functions_set)
|
|
check = _CheckAndPrintFailures(non_existent, 'Tested functions do not exist')
|
|
result = result and check
|
|
|
|
if not result:
|
|
print('Some checks failed. Make sure %s is in sync with the public API '
|
|
'headers.' % api_test_relative_path)
|
|
return 1
|
|
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|