| #!/usr/bin/env python3 |
| # |
| # Copyright 2018 - The Android Open Source Project |
| # |
| # Licensed under the Apache License, Version 2.0 (the "License"); |
| # you may not use this file except in compliance with the License. |
| # You may obtain a copy of the License at |
| # |
| # http://www.apache.org/licenses/LICENSE-2.0 |
| # |
| # Unless required by applicable law or agreed to in writing, software |
| # distributed under the License is distributed on an "AS IS" BASIS, |
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| # See the License for the specific language governing permissions and |
| # limitations under the License. |
| |
| """Functional test for aidegen project files.""" |
| |
| from __future__ import absolute_import |
| |
| import argparse |
| import itertools |
| import json |
| import os |
| import sys |
| import xml.etree.ElementTree |
| import xml.parsers.expat |
| |
| import aidegen.lib.errors |
| |
| from aidegen import aidegen_main |
| from aidegen.lib import common_util |
| from aidegen.lib.common_util import COLORED_PASS |
| from aidegen.lib.common_util import COLORED_FAIL |
| from aidegen.lib.common_util import get_blueprint_json_path |
| from aidegen.lib.common_util import get_related_paths |
| from aidegen.lib.common_util import time_logged |
| from atest import module_info |
| |
| _ROOT_DIR = os.path.join(common_util.get_android_root_dir(), |
| 'tools/asuite/aidegen_functional_test') |
| _TEST_DATA_PATH = os.path.join(_ROOT_DIR, 'test_data') |
| _ANDROID_SINGLE_PROJECT_JSON = os.path.join(_TEST_DATA_PATH, |
| 'single_module.json') |
| _VERIFY_COMMANDS_JSON = os.path.join(_TEST_DATA_PATH, 'verify_commands.json') |
| _PRODUCT_DIR = '$PROJECT_DIR$' |
| _ANDROID_COMMON = 'android_common' |
| _LINUX_GLIBC_COMMON = 'linux_glibc_common' |
| _SRCS = 'srcs' |
| _JARS = 'jars' |
| _URL = 'url' |
| _TEST_ERROR = ('AIDEGen functional test error: %s-%s is different.') |
| _MSG_NOT_IN_PROJECT_FILE = ('%s is expected, but not found in the created ' |
| 'project file: %s') |
| _MSG_NOT_IN_SAMPLE_DATA = ('%s is unexpected, but found in the created project ' |
| 'file: %s') |
| _TEST_IML_DICT = { |
| 'SystemUI': ['SystemUI.iml', 'dependencies-SystemUI.iml'], |
| 'tradefed': ['core.iml', 'dependencies-core.iml'] |
| } |
| _ALL_PASS = 'All tests passed!' |
| |
| |
| def _parse_args(args): |
| """Parse command line arguments. |
| |
| Args: |
| args: A list of arguments. |
| |
| Returns: |
| An argparse.Namespace class instance holding parsed args. |
| """ |
| parser = argparse.ArgumentParser( |
| description=__doc__, |
| formatter_class=argparse.RawDescriptionHelpFormatter, |
| usage='aidegen_functional_test [-c | -v]') |
| group = parser.add_mutually_exclusive_group() |
| parser.required = False |
| group.add_argument( |
| '-c', |
| '--create-sample', |
| action='store_true', |
| dest='create_sample', |
| help=('Create aidegen project files and write data to sample json file ' |
| 'for aidegen_functional_test to compare.')) |
| group.add_argument( |
| '-v', |
| '--verify', |
| action='store_true', |
| dest='verify_aidegen', |
| help='Verify various use cases of executing aidegen.') |
| return parser.parse_args(args) |
| |
| |
| def _import_project_file_xml_etree(filename): |
| """Import iml project file and load data into a dictionary. |
| |
| Args: |
| filename: The input project file name. |
| |
| Returns: |
| A dictionary contains json data. |
| """ |
| data = {} |
| try: |
| tree = xml.etree.ElementTree.parse(filename) |
| data[_SRCS] = [] |
| root = tree.getroot() |
| for element in root.iter('sourceFolder'): |
| src = element.get(_URL).replace(common_util.get_android_root_dir(), |
| _PRODUCT_DIR) |
| data[_SRCS].append(src) |
| data[_JARS] = [] |
| for element in root.iter('root'): |
| jar = element.get(_URL).replace(common_util.get_android_root_dir(), |
| _PRODUCT_DIR) |
| data[_JARS].append(jar) |
| except (EnvironmentError, ValueError, LookupError, |
| xml.parsers.expat.ExpatError) as err: |
| print("{0}: import error: {1}".format(os.path.basename(filename), err)) |
| raise |
| return data |
| |
| |
| def _generate_sample_json(): |
| """Generate sample iml data from a iml file into a dictionary. |
| |
| Returns: |
| A dictionary contains sample iml data. |
| """ |
| atest_module_info = module_info.ModuleInfo() |
| data = {} |
| for target, filelist in _TEST_IML_DICT.items(): |
| aidegen_main.main([target, '-n']) |
| _, abs_path = get_related_paths(atest_module_info, target) |
| for filename in filelist: |
| real_iml_file = os.path.join(abs_path, filename) |
| item_name = os.path.basename(real_iml_file) |
| data[item_name] = _import_project_file_xml_etree(real_iml_file) |
| return data |
| |
| |
| def _create_sample_json_file(): |
| """Write samples' iml data into a json file. |
| |
| linked_function: _generate_sample_json() |
| """ |
| data = _generate_sample_json() |
| with open(_ANDROID_SINGLE_PROJECT_JSON, 'w') as outfile: |
| json.dump(data, outfile, indent=4, sort_keys=False) |
| |
| |
| def test_some_sample_iml(): |
| """Compare sample iml data to assure project iml file contents is right.""" |
| test_successful = True |
| with open(_ANDROID_SINGLE_PROJECT_JSON, 'r') as outfile: |
| data_sample = json.load(outfile) |
| data_real = _generate_sample_json() |
| for name in data_real: |
| for item in [_SRCS, _JARS]: |
| s_items = data_sample[name][item] |
| r_items = data_real[name][item] |
| if set(s_items) != set(r_items): |
| diff_iter = _compare_content(name, item, s_items, r_items) |
| if diff_iter: |
| print('\n%s\n%s' % (COLORED_FAIL('Test error...'), |
| _TEST_ERROR % (name, item))) |
| print('%s %s contents are different:' % (name, item)) |
| for diff in diff_iter: |
| print(diff) |
| test_successful = False |
| if test_successful: |
| print(COLORED_PASS(_ALL_PASS)) |
| |
| |
| def _compare_content(module_name, item_type, s_items, r_items): |
| """Compare src or jar files' data of two dictionaries. |
| |
| Args: |
| module_name: the test module name. |
| item_type: the type is src or jar. |
| s_items: sample jars' items. |
| r_items: real jars' items. |
| |
| Returns: |
| An iterator of not equal sentences after comparison. |
| """ |
| if item_type == _SRCS: |
| cmp_iter1 = _compare_srcs_content(module_name, s_items, r_items, |
| _MSG_NOT_IN_PROJECT_FILE) |
| cmp_iter2 = _compare_srcs_content(module_name, r_items, s_items, |
| _MSG_NOT_IN_SAMPLE_DATA) |
| else: |
| cmp_iter1 = _compare_jars_content(module_name, s_items, r_items, |
| _MSG_NOT_IN_PROJECT_FILE) |
| cmp_iter2 = _compare_jars_content(module_name, r_items, s_items, |
| _MSG_NOT_IN_SAMPLE_DATA) |
| return itertools.chain(cmp_iter1, cmp_iter2) |
| |
| |
| def _compare_srcs_content(module_name, s_items, r_items, msg): |
| """Compare src or jar files' data of two dictionaries. |
| |
| Args: |
| module_name: the test module name. |
| s_items: sample jars' items. |
| r_items: real jars' items. |
| msg: the message will be written into log file. |
| |
| Returns: |
| An iterator of not equal sentences after comparison. |
| """ |
| for sample in s_items: |
| if not sample in r_items: |
| yield msg % (sample, module_name) |
| |
| |
| def _compare_jars_content(module_name, s_items, r_items, msg): |
| """Compare src or jar files' data of two dictionaries. |
| |
| AIDEGen treats the jars in folder names 'linux_glib_common' and |
| 'android_common' as the same content. If the paths are different only |
| because of these two names, we ignore it. |
| |
| Args: |
| module_name: the test module name. |
| s_items: sample jars' items. |
| r_items: real jars' items. |
| msg: the message will be written into log file. |
| |
| Returns: |
| An iterator of not equal sentences after comparison. |
| """ |
| for sample in s_items: |
| if not sample in r_items: |
| lnew = sample |
| if _LINUX_GLIBC_COMMON in sample: |
| lnew = sample.replace(_LINUX_GLIBC_COMMON, _ANDROID_COMMON) |
| else: |
| lnew = sample.replace(_ANDROID_COMMON, _LINUX_GLIBC_COMMON) |
| if not lnew in r_items: |
| yield msg % (sample, module_name) |
| |
| |
| # pylint: disable=broad-except |
| # pylint: disable=eval-used |
| @time_logged |
| def _verify_aidegen(): |
| """Verify various use cases of executing aidegen.""" |
| bp_json_path = get_blueprint_json_path() |
| with open(_VERIFY_COMMANDS_JSON, 'r') as jsfile: |
| data = json.load(jsfile) |
| for use_case in data: |
| if os.path.exists(bp_json_path): |
| os.remove(bp_json_path) |
| for cmd in data[use_case]: |
| try: |
| eval(cmd) |
| except (aidegen.lib.errors.ProjectOutsideAndroidRootError, |
| aidegen.lib.errors.ProjectPathNotExistError, |
| aidegen.lib.errors.NoModuleDefinedInModuleInfoError, |
| aidegen.lib.errors.IDENotExistError) as err: |
| print('{} command has raise error: {}.'.format(use_case, err)) |
| except BaseException: |
| exc_type, _, _ = sys.exc_info() |
| print('{}.{} command {}.'.format( |
| use_case, cmd, COLORED_FAIL('executes failed'))) |
| raise BaseException( |
| 'Unexpected command {} exception {}.'.format( |
| use_case, exc_type)) |
| print('{} command {}!'.format(use_case, COLORED_PASS('test passed'))) |
| print(COLORED_PASS(_ALL_PASS)) |
| |
| def main(argv): |
| """Main entry. |
| |
| Compare iml project files to the data recorded in single_module.json. |
| |
| Args: |
| argv: A list of system arguments. |
| """ |
| args = _parse_args(argv) |
| if args.create_sample: |
| _create_sample_json_file() |
| elif args.verify_aidegen: |
| _verify_aidegen() |
| else: |
| test_some_sample_iml() |
| |
| |
| if __name__ == '__main__': |
| main(sys.argv[1:]) |