1# 2# Copyright (C) 2017 The Android Open Source Project 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 17import os 18import sys 19 20from importlib import import_module 21from xml.etree import ElementTree 22 23 24class FuzzerType(object): 25 """Types of fuzzers.""" 26 FUNC_FUZZER = 0 27 IFACE_FUZZER = 1 28 29 30class ConfigGen(object): 31 """Config generator for test/vts-testcase/fuzz. 32 33 Attributes: 34 _android_build_top: string, equal to environment variable ANDROID_BUILD_TOP. 35 _project_path: string, path to test/vts-testcase/fuzz. 36 _template_dir: string, path to directory containing templates. 37 _utils: test/vts-testcase/hal/script/build/config_gen_utils module. 38 _vts_spec_parser: tools that generates and parses vts spec with hidl-gen. 39 """ 40 41 def __init__(self): 42 """ConfigGen constructor. """ 43 self._android_build_top = os.environ.get('ANDROID_BUILD_TOP') 44 if not self._android_build_top: 45 print 'Run "lunch" command first.' 46 sys.exit(1) 47 self._project_path = os.path.join(self._android_build_top, 'test', 48 'vts-testcase', 'fuzz') 49 self._template_dir = os.path.join(self._project_path, 'script', 50 'config', 'template') 51 sys.path.append( 52 os.path.join(self._android_build_top, 'test', 'vts-testcase', 'hal', 53 'script', 'build')) 54 vts_spec_parser = import_module('vts_spec_parser') 55 self._utils = import_module('build_rule_gen_utils') 56 self._vts_spec_parser = vts_spec_parser.VtsSpecParser() 57 58 def _GetPlansFromConfig(self, xml_file_path): 59 """Gets plan names from a module config. 60 61 Args: 62 xml_file_path: string, path to the XML file. 63 64 Returns: 65 list of strings, the plans that the module belongs to. 66 """ 67 root = ElementTree.parse(xml_file_path).getroot() 68 plans = [e.attrib["value"] for e in root.iterfind("option") if 69 e.get("name", "") == "config-descriptor:metadata" and 70 e.get("key", "") == "plan" and 71 "value" in e.attrib] 72 return plans 73 74 def UpdateFuzzerConfigs(self): 75 """Updates build rules for fuzzers. 76 77 Updates fuzzer configs for each pair of (hal_name, hal_version). 78 """ 79 config_dir = os.path.join(self._project_path, 'config') 80 old_config = dict() 81 82 for base_dir, dirs, files, in os.walk(config_dir): 83 for file_name in files: 84 file_path = os.path.join(base_dir, file_name) 85 if file_name == 'AndroidTest.xml': 86 old_config[file_path] = self._GetPlansFromConfig(file_path) 87 if file_name in ('AndroidTest.xml', 'Android.bp'): 88 os.remove(file_path) 89 90 self.UpdateFuzzerConfigsForType(FuzzerType.IFACE_FUZZER, old_config) 91 92 def UpdateFuzzerConfigsForType(self, fuzzer_type, old_config): 93 """Updates build rules for fuzzers. 94 95 Updates fuzzer configs for given fuzzer type. 96 97 Args: 98 fuzzer_type: FuzzerType, type of fuzzer. 99 old_config: dict. The key is the path to the old XML. The value is 100 the list of the plans the module belongs to. 101 """ 102 bp_template_path = os.path.join(self._template_dir, 'template.bp') 103 xml_template_path = os.path.join(self._template_dir, 'template.xml') 104 with open(bp_template_path) as template_file: 105 bp_template = str(template_file.read()) 106 with open(xml_template_path) as template_file: 107 xml_template = str(template_file.read()) 108 109 hal_list = self._vts_spec_parser.HalNamesAndVersions() 110 for hal_name, hal_version in hal_list: 111 if not self._IsTestable(hal_name, hal_version): 112 continue 113 fuzzer_type_subdir = self._FuzzerTypeUnderscore(fuzzer_type) 114 config_dir = os.path.join( 115 self._project_path, 'config', self._utils.HalNameDir(hal_name), 116 self._utils.HalVerDir(hal_version), fuzzer_type_subdir) 117 bp_file_path = os.path.join(config_dir, 'Android.bp') 118 xml_file_path = os.path.join(config_dir, 'AndroidTest.xml') 119 120 plan = 'vts-staging-fuzz' 121 if xml_file_path in old_config: 122 old_plans = old_config[xml_file_path] 123 if old_plans: 124 plan = old_plans[0] 125 else: 126 print('WARNING: No plan name in %s' % xml_file_path) 127 if len(old_plans) > 1: 128 print('WARNING: More than one plan name in %s' % 129 xml_file_path) 130 131 bp_string = self._FillOutTemplate( 132 hal_name, hal_version, fuzzer_type, plan, bp_template) 133 134 xml_string = self._FillOutTemplate( 135 hal_name, hal_version, fuzzer_type, plan, xml_template) 136 137 self._utils.WriteBuildRule(bp_file_path, bp_string) 138 self._utils.WriteBuildRule(xml_file_path, xml_string) 139 140 def _FuzzerTestName(self, hal_name, hal_version, fuzzer_type): 141 """Returns vts hal fuzzer test module name. 142 143 Args: 144 hal_name: string, name of the hal, e.g. 'vibrator'. 145 hal_version: string, version of the hal, e.g '7.4' 146 fuzzer_type: FuzzerType, type of fuzzer. 147 148 Returns: 149 string, test module name, e.g. VtsHalVibratorV7_4FuncFuzzer 150 """ 151 test_name = 'VtsHal' 152 test_name += ''.join(map(lambda x: x.title(), hal_name.split('.'))) 153 test_name += self._utils.HalVerDir(hal_version) 154 test_name += self._FuzzerTypeCamel(fuzzer_type) 155 return test_name 156 157 def _FuzzerTypeUnderscore(self, fuzzer_type): 158 """Returns vts hal fuzzer type string in underscore case. 159 160 Args: 161 fuzzer_type: FuzzerType, type of fuzzer. 162 163 Returns: 164 string, fuzzer type, e.g. "iface_fuzzer" 165 """ 166 if fuzzer_type == FuzzerType.FUNC_FUZZER: 167 test_type = 'func_fuzzer' 168 else: 169 test_type = 'iface_fuzzer' 170 return test_type 171 172 def _FuzzerTypeCamel(self, fuzzer_type): 173 """Returns vts hal fuzzer type string in camel case. 174 175 Args: 176 fuzzer_type: FuzzerType, type of fuzzer. 177 178 Returns: 179 string, fuzzer type, e.g. "IfaceFuzzer" 180 """ 181 if fuzzer_type == FuzzerType.FUNC_FUZZER: 182 test_type = 'FuncFuzzer' 183 else: 184 test_type = 'IfaceFuzzer' 185 return test_type 186 187 def _FillOutTemplate(self, hal_name, hal_version, fuzzer_type, plan, 188 template): 189 """Returns build rules in string form by filling out given template. 190 191 Args: 192 hal_name: string, name of the hal, e.g. 'vibrator'. 193 hal_version: string, version of the hal, e.g '7.4' 194 fuzzer_type: FuzzerType, type of fuzzer. 195 plan: string, name of the plan, e.g. 'vts-staging-fuzz' 196 template: string, build rule template to fill out. 197 198 Returns: 199 string, complete build rule in string form. 200 """ 201 config = template 202 config = config.replace('{TEST_NAME}', self._FuzzerTestName( 203 hal_name, hal_version, fuzzer_type)) 204 config = config.replace('{HAL_NAME}', hal_name) 205 config = config.replace('{HAL_VERSION}', hal_version) 206 config = config.replace('{TEST_TYPE_CAMEL}', 207 self._FuzzerTypeCamel(fuzzer_type)) 208 config = config.replace('{TEST_TYPE_UNDERSCORE}', 209 self._FuzzerTypeUnderscore(fuzzer_type)) 210 config = config.replace('{PLAN}', plan) 211 return config 212 213 def _IsTestable(self, hal_name, hal_version): 214 """Returns true iff hal can be tested.""" 215 if 'tests' in hal_name: 216 return False 217 return True 218