xref: /aosp_15_r20/external/pdfium/testing/tools/api_check.py (revision 3ac0a46f773bac49fa9476ec2b1cf3f8da5ec3a4)
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