1#!/usr/bin/env python 2# Copyright (c) 2014 The Chromium 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"""Test for TranslationUnitGenerator tool.""" 7 8import difflib 9import glob 10import json 11import ntpath 12import os 13import os.path 14import subprocess 15import sys 16 17 18def _GenerateCompileCommands(template_path, test_files_dir): 19 """Returns a JSON string containing a compilation database for the input.""" 20 with open(template_path) as fh: 21 return fh.read().replace('$test_files_dir', test_files_dir) 22 23 24def _NumberOfTestsToString(tests): 25 """Returns an English sentence describing the number of tests.""" 26 return "%d test%s" % (tests, 's' if tests != 1 else '') 27 28 29# Before running this test script, please build the translation_unit clang tool 30# first. This is explained here: 31# https://chromium.googlesource.com/chromium/src/+/master/docs/clang_tool_refactoring.md 32def main(): 33 tools_clang_directory = os.path.dirname(os.path.dirname( 34 os.path.realpath(__file__))) 35 tools_clang_scripts_directory = os.path.join(tools_clang_directory, 'scripts') 36 test_directory_for_tool = os.path.join( 37 tools_clang_directory, 'translation_unit', 'test_files') 38 compile_database = os.path.join(test_directory_for_tool, 39 'compile_commands.json') 40 compile_database_template = compile_database + '.template' 41 source_files = glob.glob(os.path.join(test_directory_for_tool, '*.cc')) 42 43 # Generate a temporary compilation database to run the tool over. 44 with open(compile_database, 'w') as f: 45 f.write(_GenerateCompileCommands(compile_database_template, 46 test_directory_for_tool)) 47 48 args = ['python', 49 os.path.join(tools_clang_scripts_directory, 'run_tool.py'), 50 '--tool', 51 'translation_unit', 52 '-p', 53 test_directory_for_tool] 54 args.extend(source_files) 55 run_tool = subprocess.Popen(args, stdout=subprocess.PIPE) 56 stdout, _ = run_tool.communicate() 57 if run_tool.returncode != 0: 58 print 'run_tool failed:\n%s' % stdout 59 sys.exit(1) 60 61 passed = 0 62 failed = 0 63 for actual in source_files: 64 actual += '.filepaths' 65 expected = actual + '.expected' 66 print '[ RUN ] %s' % os.path.relpath(actual) 67 expected_output = actual_output = None 68 with open(expected, 'r') as f: 69 expected_output = f.readlines() 70 with open(actual, 'r') as f: 71 actual_output = f.readlines() 72 has_same_filepaths = True 73 for expected_line, actual_line in zip(expected_output, actual_output): 74 if '//' in actual_line: 75 actual_line = '//' + actual_line.split('//')[1] 76 77 if ntpath.basename(expected_line) != ntpath.basename(actual_line): 78 sys.stdout.write('expected: %s' % ntpath.basename(expected_line)) 79 sys.stdout.write('actual: %s' % ntpath.basename(actual_line)) 80 has_same_filepaths = False 81 break 82 if not has_same_filepaths: 83 failed += 1 84 for line in difflib.unified_diff(expected_output, actual_output, 85 fromfile=os.path.relpath(expected), 86 tofile=os.path.relpath(actual)): 87 sys.stdout.write(line) 88 print '[ FAILED ] %s' % os.path.relpath(actual) 89 # Don't clean up the file on failure, so the results can be referenced 90 # more easily. 91 continue 92 print '[ OK ] %s' % os.path.relpath(actual) 93 passed += 1 94 os.remove(actual) 95 96 if failed == 0: 97 os.remove(compile_database) 98 99 print '[==========] %s ran.' % _NumberOfTestsToString(len(source_files)) 100 if passed > 0: 101 print '[ PASSED ] %s.' % _NumberOfTestsToString(passed) 102 if failed > 0: 103 print '[ FAILED ] %s.' % _NumberOfTestsToString(failed) 104 105 106if __name__ == '__main__': 107 sys.exit(main()) 108