1#!/usr/bin/python3 2 3""" 4List VTS tests for each HAL by parsing module-info.json. 5 6Example usage: 7 8 # First, build modules-info.json 9 m -j "${ANDROID_PRODUCT_OUT#$ANDROID_BUILD_TOP/}/module-info.json" 10 11 # List with pretty-printed JSON. *IDL packages without a VTS module will show up 12 # as keys with empty lists. 13 ./list_hals_vts.py | python3 -m json.tool 14 15 # List with CSV. *IDL packages without a VTS module will show up as a line with 16 # empty value in the VTS module column. 17 ./list_hals_vts.py --csv 18""" 19 20import argparse 21import collections 22import csv 23import io 24import json 25import os 26import logging 27import pathlib 28import re 29import sys 30 31PATH_PACKAGE_PATTERN = re.compile( 32 r'^hardware/interfaces/(?P<path>(?:\w+/)*?)(?:aidl|(?P<version>\d+\.\d+))/.*') 33 34 35class CriticalHandler(logging.StreamHandler): 36 def emit(self, record): 37 super(CriticalHandler, self).emit(record) 38 if record.levelno >= logging.CRITICAL: 39 sys.exit(1) 40 41 42logger = logging.getLogger(__name__) 43logger.addHandler(CriticalHandler()) 44 45 46def default_json(): 47 out = os.environ.get('ANDROID_PRODUCT_OUT') 48 if not out: return None 49 return os.path.join(out, 'module-info.json') 50 51 52def infer_package(path): 53 """ 54 Infer package from a relative path from build top where a VTS module lives. 55 56 :param path: a path like 'hardware/interfaces/vibrator/aidl/vts' 57 :return: The inferred *IDL package, e.g. 'android.hardware.vibrator' 58 59 >>> infer_package('hardware/interfaces/automotive/sv/1.0/vts/functional') 60 'android.hardware.automotive.sv@1.0' 61 >>> infer_package('hardware/interfaces/vibrator/aidl/vts') 62 'android.hardware.vibrator' 63 """ 64 mo = re.match(PATH_PACKAGE_PATTERN, path) 65 if not mo: return None 66 package = 'android.hardware.' + ('.'.join(pathlib.Path(mo.group('path')).parts)) 67 if mo.group('version'): 68 package += '@' + mo.group('version') 69 return package 70 71 72def load_modules_info(json_file): 73 """ 74 :param json_file: The path to modules-info.json 75 :return: a dictionary, where the keys are inferred *IDL package names, and 76 values are a list of VTS modules with that inferred package name. 77 """ 78 with open(json_file) as fp: 79 root = json.load(fp) 80 ret = collections.defaultdict(list) 81 for module_name, module_info in root.items(): 82 if 'vts' not in module_info.get('compatibility_suites', []): 83 continue 84 for path in module_info.get('path', []): 85 inferred_package = infer_package(path) 86 if not inferred_package: 87 continue 88 ret[inferred_package].append(module_name) 89 return ret 90 91 92def add_missing_idl(vts_modules): 93 top = os.environ.get("ANDROID_BUILD_TOP") 94 interfaces = None 95 if top: 96 interfaces = os.path.join(top, "hardware", "interfaces") 97 else: 98 logger.warning("Missing ANDROID_BUILD_TOP") 99 interfaces = "hardware/interfaces" 100 if not os.path.isdir(interfaces): 101 logger.error("Not adding missing *IDL modules because missing hardware/interfaces dir") 102 return 103 assert not interfaces.endswith(os.path.sep) 104 for root, dirs, files in os.walk(interfaces): 105 for dir in dirs: 106 full_dir = os.path.join(root, dir) 107 assert full_dir.startswith(interfaces) 108 top_rel_dir = os.path.join('hardware', 'interfaces', full_dir[len(interfaces) + 1:]) 109 inferred_package = infer_package(top_rel_dir) 110 if inferred_package is None: 111 continue 112 if inferred_package not in vts_modules: 113 vts_modules[inferred_package] = [] 114 115 116def main(): 117 parser = argparse.ArgumentParser(__doc__, formatter_class=argparse.RawDescriptionHelpFormatter) 118 parser.add_argument('json', metavar='module-info.json', default=default_json(), nargs='?') 119 parser.add_argument('--csv', action='store_true', help='Print CSV. If not specified print JSON.') 120 args = parser.parse_args() 121 if not args.json: 122 logger.critical('No module-info.json is specified or found.') 123 vts_modules = load_modules_info(args.json) 124 add_missing_idl(vts_modules) 125 126 if args.csv: 127 out = io.StringIO() 128 writer = csv.writer(out, ) 129 writer.writerow(["package", "vts_module"]) 130 for package, modules in vts_modules.items(): 131 if not modules: 132 writer.writerow([package, ""]) 133 for module in modules: 134 writer.writerow([package, module]) 135 result = out.getvalue() 136 else: 137 result = json.dumps(vts_modules) 138 139 print(result) 140 141 142if __name__ == '__main__': 143 main() 144