1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
|
#!/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 == 'FSDK_SetUnSpObjProcessHandler' 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, api_test_path):
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, api_test_path)
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())
|