1 #!/usr/bin/env python3 2 # Copyright 2016 Google Inc. All Rights Reserved. 3 # 4 # Licensed under the Apache License, Version 2.0 (the "License"); 5 # you may not use this file except in compliance with the License. 6 # You may obtain a copy of the License at 7 # 8 # http://www.apache.org/licenses/LICENSE-2.0 9 # 10 # Unless required by applicable law or agreed to in writing, software 11 # distributed under the License is distributed on an "AS-IS" BASIS, 12 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 # See the License for the specific language governing permissions and 14 # limitations under the License. 15 16 import pytest 17 import re 18 import sys 19 from fruit_test_common import * 20 21 @pytest.mark.skipif( 22 os.sep != '/', 23 reason = 'This only works in platforms where paths are /-separated.') 24 def test_defn_file_inclusion(): 25 include_pattern = re.compile(' *#include *<(.*)> *') 26 27 fruit_headers_root_absolute_path = os.path.abspath(PATH_TO_FRUIT_STATIC_HEADERS) 28 29 includes = {} 30 for root, _, files in os.walk(fruit_headers_root_absolute_path): 31 for file in files: 32 if file.endswith('.h'): 33 path = os.path.join(root, file) 34 with open(path, 'r') as f: 35 current_includes = set() 36 for line in f.readlines(): 37 # Remove the newline 38 line = line[:-1] 39 matches = re.match(include_pattern, line) 40 if matches: 41 current_includes.add(matches.groups()[0]) 42 root_relative_path = root.replace(fruit_headers_root_absolute_path, '') 43 relative_path = os.path.join(root_relative_path, file) 44 if relative_path.startswith(os.sep): 45 relative_path = relative_path[1:] 46 includes[relative_path] = current_includes 47 48 for defn_file, defn_file_includes in includes.items(): 49 if defn_file.endswith('.defn.h'): 50 main_header_file = defn_file.replace('.defn.h', '.h') 51 # This is a special case where we don't follow the convention, so we need to specify the corresponding main 52 # header file explicitly. 53 if defn_file == 'fruit/impl/component_functors.defn.h': 54 main_header_file = 'fruit/component.h' 55 56 # The .defn.h files for headers in fruit/ are in fruit/impl/ 57 alternative_main_header_file = main_header_file.replace('fruit/impl/', 'fruit/') 58 59 if main_header_file not in includes and alternative_main_header_file not in includes: 60 raise Exception('Can\'t find the .h header corresponding to: %s. Considered: %s' % (defn_file, (main_header_file, alternative_main_header_file))) 61 if main_header_file not in defn_file_includes and alternative_main_header_file not in defn_file_includes: 62 raise Exception('%s should have included %s, but it includes only: %s' % (defn_file, main_header_file, defn_file_includes)) 63 if main_header_file in includes and defn_file not in includes[main_header_file]: 64 raise Exception('%s should have included %s, but it includes only: %s' % (main_header_file, defn_file, includes[main_header_file])) 65 if alternative_main_header_file in includes and defn_file not in includes[alternative_main_header_file]: 66 raise Exception('%s should have included %s, but it includes only: %s' % (main_header_file, defn_file, includes[alternative_main_header_file])) 67 for other_header, other_header_includes in includes.items(): 68 if other_header not in (main_header_file, alternative_main_header_file) and defn_file in other_header_includes: 69 raise Exception('Unexpected direct include: %s includes %s' % (other_header, defn_file)) 70 71 if __name__== '__main__': 72 main(__file__) 73