1import argparse 2import errno 3import json 4import logging 5import os 6from collections import defaultdict 7from copy import deepcopy 8 9from find_apps import find_apps 10from find_build_apps import BUILD_SYSTEM_CMAKE, BUILD_SYSTEMS 11from idf_py_actions.constants import PREVIEW_TARGETS, SUPPORTED_TARGETS 12from ttfw_idf.IDFAssignTest import ExampleAssignTest, TestAppsAssignTest 13 14TEST_LABELS = { 15 'example_test': 'BOT_LABEL_EXAMPLE_TEST', 16 'test_apps': 'BOT_LABEL_CUSTOM_TEST', 17 'component_ut': ['BOT_LABEL_UNIT_TEST', 18 'BOT_LABEL_UNIT_TEST_32', 19 'BOT_LABEL_UNIT_TEST_S2', 20 'BOT_LABEL_UNIT_TEST_C3'], 21} 22 23BUILD_ALL_LABELS = [ 24 'BOT_LABEL_BUILD', 25 'BOT_LABEL_BUILD_ALL_APPS', 26 'BOT_LABEL_REGULAR_TEST', 27 'BOT_LABEL_WEEKEND_TEST', 28] 29 30 31def _has_build_all_label(): 32 for label in BUILD_ALL_LABELS: 33 if os.getenv(label): 34 return True 35 return False 36 37 38def _judge_build_or_not(action, build_all): # type: (str, bool) -> (bool, bool) 39 """ 40 :return: (build_or_not_for_test_related_apps, build_or_not_for_non_related_apps) 41 """ 42 if build_all or _has_build_all_label() or (not os.getenv('BOT_TRIGGER_WITH_LABEL')): 43 logging.info('Build all apps') 44 return True, True 45 46 labels = TEST_LABELS[action] 47 if not isinstance(labels, list): 48 labels = [labels] 49 50 for label in labels: 51 if os.getenv(label): 52 logging.info('Build only test cases apps') 53 return True, False 54 logging.info('Skip all') 55 return False, False 56 57 58def output_json(apps_dict_list, target, build_system, output_dir): 59 output_path = os.path.join(output_dir, 'scan_{}_{}.json'.format(target.lower(), build_system)) 60 with open(output_path, 'w') as fw: 61 fw.writelines([json.dumps(app) + '\n' for app in apps_dict_list]) 62 63 64def main(): 65 parser = argparse.ArgumentParser(description='Scan the required build tests') 66 parser.add_argument('test_type', 67 choices=TEST_LABELS.keys(), 68 help='Scan test type') 69 parser.add_argument('paths', nargs='+', 70 help='One or more app paths') 71 parser.add_argument('-b', '--build-system', 72 choices=BUILD_SYSTEMS.keys(), 73 default=BUILD_SYSTEM_CMAKE) 74 parser.add_argument('-c', '--ci-config-file', 75 required=True, 76 help='gitlab ci config target-test file') 77 parser.add_argument('-o', '--output-path', 78 required=True, 79 help='output path of the scan result') 80 parser.add_argument('--exclude', nargs='*', 81 help='Ignore specified directory. Can be used multiple times.') 82 parser.add_argument('--preserve', action='store_true', 83 help='add this flag to preserve artifacts for all apps') 84 parser.add_argument('--build-all', action='store_true', 85 help='add this flag to build all apps') 86 87 args = parser.parse_args() 88 build_test_case_apps, build_standalone_apps = _judge_build_or_not(args.test_type, args.build_all) 89 90 if not os.path.exists(args.output_path): 91 try: 92 os.makedirs(args.output_path) 93 except OSError as e: 94 if e.errno != errno.EEXIST: 95 raise e 96 97 SUPPORTED_TARGETS.extend(PREVIEW_TARGETS) 98 99 if (not build_standalone_apps) and (not build_test_case_apps): 100 for target in SUPPORTED_TARGETS: 101 output_json([], target, args.build_system, args.output_path) 102 SystemExit(0) 103 104 paths = set([os.path.join(os.getenv('IDF_PATH'), path) if not os.path.isabs(path) else path for path in args.paths]) 105 106 test_cases = [] 107 for path in paths: 108 if args.test_type == 'example_test': 109 assign = ExampleAssignTest(path, args.ci_config_file) 110 elif args.test_type in ['test_apps', 'component_ut']: 111 assign = TestAppsAssignTest(path, args.ci_config_file) 112 else: 113 raise SystemExit(1) # which is impossible 114 115 test_cases.extend(assign.search_cases()) 116 117 ''' 118 { 119 <target>: { 120 'test_case_apps': [<app_dir>], # which is used in target tests 121 'standalone_apps': [<app_dir>], # which is not 122 }, 123 ... 124 } 125 ''' 126 scan_info_dict = defaultdict(dict) 127 # store the test cases dir, exclude these folders when scan for standalone apps 128 default_exclude = args.exclude if args.exclude else [] 129 130 build_system = args.build_system.lower() 131 build_system_class = BUILD_SYSTEMS[build_system] 132 133 for target in SUPPORTED_TARGETS: 134 exclude_apps = deepcopy(default_exclude) 135 136 if build_test_case_apps: 137 scan_info_dict[target]['test_case_apps'] = set() 138 for case in test_cases: 139 app_dir = case.case_info['app_dir'] 140 app_target = case.case_info['target'] 141 if app_target.lower() != target.lower(): 142 continue 143 _apps = find_apps(build_system_class, app_dir, True, exclude_apps, target.lower()) 144 if _apps: 145 scan_info_dict[target]['test_case_apps'].update(_apps) 146 exclude_apps.append(app_dir) 147 else: 148 scan_info_dict[target]['test_case_apps'] = set() 149 150 if build_standalone_apps: 151 scan_info_dict[target]['standalone_apps'] = set() 152 for path in paths: 153 scan_info_dict[target]['standalone_apps'].update( 154 find_apps(build_system_class, path, True, exclude_apps, target.lower())) 155 else: 156 scan_info_dict[target]['standalone_apps'] = set() 157 158 test_case_apps_preserve_default = True if build_system == 'cmake' else False 159 for target in SUPPORTED_TARGETS: 160 apps = [] 161 for app_dir in scan_info_dict[target]['test_case_apps']: 162 apps.append({ 163 'app_dir': app_dir, 164 'build_system': args.build_system, 165 'target': target, 166 'preserve': args.preserve or test_case_apps_preserve_default 167 }) 168 for app_dir in scan_info_dict[target]['standalone_apps']: 169 apps.append({ 170 'app_dir': app_dir, 171 'build_system': args.build_system, 172 'target': target, 173 'preserve': args.preserve 174 }) 175 output_path = os.path.join(args.output_path, 'scan_{}_{}.json'.format(target.lower(), build_system)) 176 with open(output_path, 'w') as fw: 177 fw.writelines([json.dumps(app) + '\n' for app in apps]) 178 179 180if __name__ == '__main__': 181 main() 182