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 'translation_unit', 51 test_directory_for_tool] 52 args.extend(source_files) 53 run_tool = subprocess.Popen(args, stdout=subprocess.PIPE) 54 stdout, _ = run_tool.communicate() 55 if run_tool.returncode != 0: 56 print 'run_tool failed:\n%s' % stdout 57 sys.exit(1) 58 59 passed = 0 60 failed = 0 61 for actual in source_files: 62 actual += '.filepaths' 63 expected = actual + '.expected' 64 print '[ RUN ] %s' % os.path.relpath(actual) 65 expected_output = actual_output = None 66 with open(expected, 'r') as f: 67 expected_output = f.readlines() 68 with open(actual, 'r') as f: 69 actual_output = f.readlines() 70 has_same_filepaths = True 71 for expected_line, actual_line in zip(expected_output, actual_output): 72 if '//' in actual_line: 73 actual_line = '//' + actual_line.split('//')[1] 74 75 if ntpath.basename(expected_line) != ntpath.basename(actual_line): 76 sys.stdout.write('expected: %s' % ntpath.basename(expected_line)) 77 sys.stdout.write('actual: %s' % ntpath.basename(actual_line)) 78 has_same_filepaths = False 79 break 80 if not has_same_filepaths: 81 failed += 1 82 for line in difflib.unified_diff(expected_output, actual_output, 83 fromfile=os.path.relpath(expected), 84 tofile=os.path.relpath(actual)): 85 sys.stdout.write(line) 86 print '[ FAILED ] %s' % os.path.relpath(actual) 87 # Don't clean up the file on failure, so the results can be referenced 88 # more easily. 89 continue 90 print '[ OK ] %s' % os.path.relpath(actual) 91 passed += 1 92 os.remove(actual) 93 94 if failed == 0: 95 os.remove(compile_database) 96 97 print '[==========] %s ran.' % _NumberOfTestsToString(len(source_files)) 98 if passed > 0: 99 print '[ PASSED ] %s.' % _NumberOfTestsToString(passed) 100 if failed > 0: 101 print '[ FAILED ] %s.' % _NumberOfTestsToString(failed) 102 103 104if __name__ == '__main__': 105 sys.exit(main()) 106