1#!/usr/bin/env python3 2# 3# Copyright 2018 - The Android Open Source Project 4# 5# Licensed under the Apache License, Version 2.0 (the "License"); 6# you may not use this file except in compliance with the License. 7# You may obtain a copy of the License at 8# 9# http://www.apache.org/licenses/LICENSE-2.0 10# 11# Unless required by applicable law or agreed to in writing, software 12# distributed under the License is distributed on an "AS IS" BASIS, 13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 14# See the License for the specific language governing permissions and 15# limitations under the License. 16 17"""Functional test for aidegen project files.""" 18 19from __future__ import absolute_import 20 21import argparse 22import itertools 23import json 24import os 25import sys 26import xml.etree.ElementTree 27import xml.parsers.expat 28 29import aidegen.lib.errors 30 31from aidegen import aidegen_main 32from aidegen.lib.common_util import get_related_paths 33from aidegen.lib.common_util import time_logged 34from atest import constants 35from atest import module_info 36from atest import atest_utils 37 38_ANDROID_ROOT_PATH = os.environ.get(constants.ANDROID_BUILD_TOP) 39_ROOT_DIR = os.path.join(_ANDROID_ROOT_PATH, 40 'tools/asuite/aidegen_functional_test') 41_TEST_DATA_PATH = os.path.join(_ROOT_DIR, 'test_data') 42_ANDROID_SINGLE_PROJECT_JSON = os.path.join(_TEST_DATA_PATH, 43 'single_module.json') 44_VERIFY_COMMANDS_JSON = os.path.join(_TEST_DATA_PATH, 'verify_commands.json') 45_PRODUCT_DIR = '$PROJECT_DIR$' 46_ANDROID_COMMON = 'android_common' 47_LINUX_GLIBC_COMMON = 'linux_glibc_common' 48_SRCS = 'srcs' 49_JARS = 'jars' 50_URL = 'url' 51_TEST_ERROR = ('AIDEGen functional test error: %s-%s is different.') 52_MSG_NOT_IN_PROJECT_FILE = ('%s is expected, but not found in the created ' 53 'project file: %s') 54_MSG_NOT_IN_SAMPLE_DATA = ('%s is unexpected, but found in the created project ' 55 'file: %s') 56_TEST_IML_DICT = { 57 'SystemUI': ['SystemUI.iml', 'dependencies-SystemUI.iml'], 58 'tradefed': ['core.iml', 'dependencies-core.iml'] 59} 60_ALL_PASS = 'All tests passed!' 61 62 63def _parse_args(args): 64 """Parse command line arguments. 65 66 Args: 67 args: A list of arguments. 68 69 Returns: 70 An argparse.Namespace class instance holding parsed args. 71 """ 72 parser = argparse.ArgumentParser( 73 description=__doc__, 74 formatter_class=argparse.RawDescriptionHelpFormatter, 75 usage='aidegen_functional_test [-c | -v]') 76 group = parser.add_mutually_exclusive_group() 77 parser.required = False 78 group.add_argument( 79 '-c', 80 '--create-sample', 81 action='store_true', 82 dest='create_sample', 83 help=('Create aidegen project files and write data to sample json file ' 84 'for aidegen_functional_test to compare.')) 85 group.add_argument( 86 '-v', 87 '--verify', 88 action='store_true', 89 dest='verify_aidegen', 90 help='Verify various use cases of executing aidegen.') 91 return parser.parse_args(args) 92 93 94def _import_project_file_xml_etree(filename): 95 """Import iml project file and load data into a dictionary. 96 97 Args: 98 filename: The input project file name. 99 """ 100 data = {} 101 try: 102 tree = xml.etree.ElementTree.parse(filename) 103 data[_SRCS] = [] 104 root = tree.getroot() 105 for element in root.iter('sourceFolder'): 106 src = element.get(_URL).replace(_ANDROID_ROOT_PATH, _PRODUCT_DIR) 107 data[_SRCS].append(src) 108 data[_JARS] = [] 109 for element in root.iter('root'): 110 jar = element.get(_URL).replace(_ANDROID_ROOT_PATH, _PRODUCT_DIR) 111 data[_JARS].append(jar) 112 except (EnvironmentError, ValueError, LookupError, 113 xml.parsers.expat.ExpatError) as err: 114 print("{0}: import error: {1}".format(os.path.basename(filename), err)) 115 raise 116 return data 117 118 119def _generate_sample_json(): 120 """Generate sample iml data and write into a json file.""" 121 atest_module_info = module_info.ModuleInfo() 122 data = {} 123 for target, filelist in _TEST_IML_DICT.items(): 124 aidegen_main.main([target, '-n']) 125 _, abs_path = get_related_paths(atest_module_info, target) 126 for filename in filelist: 127 real_iml_file = os.path.join(abs_path, filename) 128 item_name = os.path.basename(real_iml_file) 129 data[item_name] = _import_project_file_xml_etree(real_iml_file) 130 return data 131 132 133def _create_sample_json_file(): 134 """Write samples' iml data into a json file. 135 136 linked_function: _generate_sample_json() 137 """ 138 data = _generate_sample_json() 139 with open(_ANDROID_SINGLE_PROJECT_JSON, 'w') as outfile: 140 json.dump(data, outfile, indent=4, sort_keys=False) 141 142 143def test_some_sample_iml(): 144 """Compare sample iml data to assure project iml file contents is right.""" 145 test_successful = True 146 with open(_ANDROID_SINGLE_PROJECT_JSON, 'r') as outfile: 147 data_sample = json.load(outfile) 148 data_real = _generate_sample_json() 149 for name in data_real: 150 for item in [_SRCS, _JARS]: 151 s_items = data_sample[name][item] 152 r_items = data_real[name][item] 153 if set(s_items) != set(r_items): 154 diff_iter = _compare_content(name, item, s_items, r_items) 155 if diff_iter: 156 print('\n%s\n%s' % (atest_utils.colorize( 157 'Test error...', constants.RED), _TEST_ERROR % 158 (name, item))) 159 print('%s %s contents are different:' % (name, item)) 160 for diff in diff_iter: 161 print(diff) 162 test_successful = False 163 if test_successful: 164 print(atest_utils.colorize(_ALL_PASS, constants.GREEN)) 165 166 167def _compare_content(module_name, item_type, s_items, r_items): 168 """Compare src or jar files' data of two dictionaries. 169 170 Args: 171 module_name: the test module name. 172 item_type: the type is src or jar. 173 s_items: sample jars' items. 174 r_items: real jars' items. 175 176 Returns: 177 An iterator of not equal sentences after comparison. 178 """ 179 if item_type == _SRCS: 180 cmp_iter1 = _compare_srcs_content(module_name, s_items, r_items, 181 _MSG_NOT_IN_PROJECT_FILE) 182 cmp_iter2 = _compare_srcs_content(module_name, r_items, s_items, 183 _MSG_NOT_IN_SAMPLE_DATA) 184 else: 185 cmp_iter1 = _compare_jars_content(module_name, s_items, r_items, 186 _MSG_NOT_IN_PROJECT_FILE) 187 cmp_iter2 = _compare_jars_content(module_name, r_items, s_items, 188 _MSG_NOT_IN_SAMPLE_DATA) 189 return itertools.chain(cmp_iter1, cmp_iter2) 190 191 192def _compare_srcs_content(module_name, s_items, r_items, msg): 193 """Compare src or jar files' data of two dictionaries. 194 195 Args: 196 module_name: the test module name. 197 s_items: sample jars' items. 198 r_items: real jars' items. 199 msg: the message will be written into log file. 200 201 Returns: 202 An iterator of not equal sentences after comparison. 203 """ 204 for sample in s_items: 205 if not sample in r_items: 206 yield msg % (sample, module_name) 207 208 209def _compare_jars_content(module_name, s_items, r_items, msg): 210 """Compare src or jar files' data of two dictionaries. 211 212 Args: 213 module_name: the test module name. 214 s_items: sample jars' items. 215 r_items: real jars' items. 216 msg: the message will be written into log file. 217 218 Returns: 219 An iterator of not equal sentences after comparison. 220 """ 221 for sample in s_items: 222 if not sample in r_items: 223 lnew = sample 224 if _LINUX_GLIBC_COMMON in sample: 225 lnew = sample.replace(_LINUX_GLIBC_COMMON, _ANDROID_COMMON) 226 else: 227 lnew = sample.replace(_ANDROID_COMMON, _LINUX_GLIBC_COMMON) 228 if not lnew in r_items: 229 yield msg % (sample, module_name) 230 231 232# pylint: disable=broad-except 233# pylint: disable=eval-used 234@time_logged 235def _verify_aidegen(): 236 """Verify various use cases of executing aidegen.""" 237 with open(_VERIFY_COMMANDS_JSON, 'r') as jsfile: 238 data = json.load(jsfile) 239 for use_case in data: 240 for cmd in data[use_case]: 241 try: 242 eval(cmd) 243 except (aidegen.lib.errors.ProjectOutsideAndroidRootError, 244 aidegen.lib.errors.ProjectPathNotExistError, 245 aidegen.lib.errors.NoModuleDefinedInModuleInfoError, 246 aidegen.lib.errors.IDENotExistError) as err: 247 print('{} command has raise error: {}.'.format(use_case, err)) 248 except Exception as exp: 249 print('{}.{} command {}.'.format( 250 use_case, cmd, 251 atest_utils.colorize('executes failed', constants.RED))) 252 raise Exception( 253 'Unexpected command {} exception {}.'.format(use_case, exp)) 254 print('{} command {}!'.format( 255 use_case, atest_utils.colorize('test passed', constants.GREEN))) 256 print(atest_utils.colorize(_ALL_PASS, constants.GREEN)) 257 258 259def main(argv): 260 """Main entry. 261 262 Compare iml project files to the data recorded in single_module.json. 263 264 Args: 265 argv: A list of system arguments. 266 """ 267 args = _parse_args(argv) 268 if args.create_sample: 269 _create_sample_json_file() 270 elif args.verify_aidegen: 271 _verify_aidegen() 272 else: 273 test_some_sample_iml() 274 275 276if __name__ == '__main__': 277 main(sys.argv[1:]) 278