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