liyujie
2025-08-28 786ff4f4ca2374bdd9177f2e24b503d43e7a3b93
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
#!/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 fpdfview_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/fpdfview_c_api_test.c. It then verifies both lists do not contain
duplicates, and they match each other.
 
"""
 
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 _GetFunctionsFromTest(api_test_path):
  chk_regex = re.compile('^    CHK\((.*)\);\n$')
  with open(api_test_path) as f:
    contents = f.readlines()
    functions = []
    for line in contents:
      match = chk_regex.match(line)
      if match:
        functions.append(match.groups()[0])
    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', 'fpdfview_c_api_test.c')
  api_test_path = os.path.join(src_path, api_test_relative_path)
  test_functions = _GetFunctionsFromTest(api_test_path)
 
  result = True
  duplicate_public_functions = _FindDuplicates(public_functions)
  check = _CheckAndPrintFailures(duplicate_public_functions,
                                'Found duplicate functions in public headers')
  result = result and check
 
  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())