clang-tools  7.0.0
gen_tests.py
Go to the documentation of this file.
1 #!/usr/bin/env python3
2 #
3 #===- gen_tests.py - clang-doc test generator ----------------*- python -*--===#
4 #
5 # The LLVM Compiler Infrastructure
6 #
7 # This file is distributed under the University of Illinois Open Source
8 # License. See LICENSE.TXT for details.
9 #
10 #===------------------------------------------------------------------------===#
11 """
12 clang-doc test generator
13 ==========================
14 
15 Generates tests for clang-doc given a certain set of flags, a prefix for the
16 test file, and a given clang-doc binary. Please check emitted tests for
17 accuracy before using.
18 
19 To generate all current tests:
20 - Generate mapper tests:
21  gen_tests.py -flag='--dump-mapper' -flag='--doxygen' -prefix mapper
22 
23 - Generate reducer tests:
24  gen_tests.py -flag='--dump-intermediate' -flag='--doxygen' -prefix bc
25 
26 - Generate yaml tests:
27  gen_tests.py -flag='--format=yaml' -flag='--doxygen' -prefix yaml
28 
29 This script was written on/for Linux, and has not been tested on any other
30 platform and so it may not work.
31 
32 """
33 
34 import argparse
35 import glob
36 import os
37 import shutil
38 import subprocess
39 
40 RUN_CLANG_DOC = """
41 // RUN: clang-doc {0} -p %t %t/test.cpp -output=%t/docs
42 """
43 RUN = """
44 // RUN: {0} %t/{1} | FileCheck %s --check-prefix CHECK-{2}
45 """
46 
47 CHECK = '// CHECK-{0}: '
48 
49 CHECK_NEXT = '// CHECK-{0}-NEXT: '
50 
51 
52 def clear_test_prefix_files(prefix, tests_path):
53  if os.path.isdir(tests_path):
54  for root, dirs, files in os.walk(tests_path):
55  for filename in files:
56  if filename.startswith(prefix):
57  os.remove(os.path.join(root, filename))
58 
59 
60 def copy_to_test_file(test_case_path, test_cases_path):
61  # Copy file to 'test.cpp' to preserve file-dependent USRs
62  test_file = os.path.join(test_cases_path, 'test.cpp')
63  shutil.copyfile(test_case_path, test_file)
64  return test_file
65 
66 
67 def run_clang_doc(args, out_dir, test_file):
68  # Run clang-doc.
69  current_cmd = [args.clangdoc]
70  current_cmd.extend(args.flags)
71  current_cmd.append('--output=' + out_dir)
72  current_cmd.append(test_file)
73  print('Running ' + ' '.join(current_cmd))
74  return_code = subprocess.call(current_cmd)
75  if return_code:
76  return 1
77  return 0
78 
79 
80 def get_test_case_code(test_case_path, flags):
81  # Get the test case code
82  code = ''
83  with open(test_case_path, 'r') as code_file:
84  code = code_file.read()
85 
86  code += RUN_CLANG_DOC.format(flags)
87  return code
88 
89 
90 def get_output(root, out_file, case_out_path, flags, checkname, bcanalyzer):
91  output = ''
92  run_cmd = ''
93  if '--dump-mapper' in flags or '--dump-intermediate' in flags:
94  # Run llvm-bcanalyzer
95  output = subprocess.check_output(
96  [bcanalyzer, '--dump',
97  os.path.join(root, out_file)])
98  output = output[:output.find('Summary of ')].rstrip()
99  run_cmd = RUN.format('llvm-bcanalyzer --dump',
100  os.path.join('docs', 'bc', out_file), checkname)
101  else:
102  # Run cat
103  output = subprocess.check_output(['cat', os.path.join(root, out_file)])
104  run_cmd = RUN.format(
105  'cat',
106  os.path.join('docs', os.path.relpath(root, case_out_path),
107  out_file), checkname)
108 
109  # Format output.
110  output = output.replace('blob data = \'test\'', 'blob data = \'{{.*}}\'')
111  output = CHECK.format(checkname) + output.rstrip()
112  output = run_cmd + output.replace('\n',
113  '\n' + CHECK_NEXT.format(checkname))
114 
115  return output + '\n'
116 
117 
118 def main():
119  parser = argparse.ArgumentParser(description='Generate clang-doc tests.')
120  parser.add_argument(
121  '-flag',
122  action='append',
123  default=[],
124  dest='flags',
125  help='Flags to pass to clang-doc.')
126  parser.add_argument(
127  '-prefix',
128  type=str,
129  default='',
130  dest='prefix',
131  help='Prefix for this test group.')
132  parser.add_argument(
133  '-clang-doc-binary',
134  dest='clangdoc',
135  metavar="PATH",
136  default='clang-doc',
137  help='path to clang-doc binary')
138  parser.add_argument(
139  '-llvm-bcanalyzer-binary',
140  dest='bcanalyzer',
141  metavar="PATH",
142  default='llvm-bcanalyzer',
143  help='path to llvm-bcanalyzer binary')
144  args = parser.parse_args()
145 
146  flags = ' '.join(args.flags)
147 
148  clang_doc_path = os.path.dirname(__file__)
149  tests_path = os.path.join(clang_doc_path, '..', 'test', 'clang-doc')
150  test_cases_path = os.path.join(tests_path, 'test_cases')
151 
152  clear_test_prefix_files(args.prefix, tests_path)
153 
154  for test_case_path in glob.glob(os.path.join(test_cases_path, '*')):
155  if test_case_path.endswith(
156  'compile_flags.txt') or test_case_path.endswith(
157  'compile_commands.json'):
158  continue
159 
160  # Name of this test case
161  case_name = os.path.basename(test_case_path).split('.')[0]
162 
163  test_file = copy_to_test_file(test_case_path, test_cases_path)
164  out_dir = os.path.join(test_cases_path, case_name)
165 
166  if run_clang_doc(args, out_dir, test_file):
167  return 1
168 
169  # Retrieve output and format as FileCheck tests
170  all_output = ''
171  num_outputs = 0
172  for root, dirs, files in os.walk(out_dir):
173  for out_file in files:
174  # Make the file check the first 3 letters (there's a very small chance
175  # that this will collide, but the fix is to simply change the decl name)
176  usr = os.path.basename(out_file).split('.')
177  # If the usr is less than 2, this isn't one of the test files.
178  if len(usr) < 2:
179  continue
180  all_output += get_output(root, out_file, out_dir, args.flags,
181  num_outputs, args.bcanalyzer)
182  num_outputs += 1
183 
184  # Add test case code to test
185  all_output = get_test_case_code(test_case_path,
186  flags) + '\n' + all_output
187 
188  # Write to test case file in /test.
189  test_out_path = os.path.join(
190  tests_path, args.prefix + '-' + os.path.basename(test_case_path))
191  with open(test_out_path, 'w+') as o:
192  o.write(all_output)
193 
194  # Clean up
195  shutil.rmtree(out_dir)
196  os.remove(test_file)
197 
198 
199 if __name__ == '__main__':
200  main()
def main()
Definition: gen_tests.py:118
def clear_test_prefix_files(prefix, tests_path)
Definition: gen_tests.py:52
def copy_to_test_file(test_case_path, test_cases_path)
Definition: gen_tests.py:60
def get_output(root, out_file, case_out_path, flags, checkname, bcanalyzer)
Definition: gen_tests.py:90
def run_clang_doc(args, out_dir, test_file)
Definition: gen_tests.py:67
static std::string join(ArrayRef< SpecialMemberFunctionsCheck::SpecialMemberFunctionKind > SMFS, llvm::StringRef AndOr)
def get_test_case_code(test_case_path, flags)
Definition: gen_tests.py:80