#!/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.common_util import get_related_paths
from aidegen.lib.common_util import time_logged
from atest import constants
from atest import module_info
from atest import atest_utils
_ANDROID_ROOT_PATH = os.environ.get(constants.ANDROID_BUILD_TOP)
_ROOT_DIR = os.path.join(_ANDROID_ROOT_PATH,
'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.
"""
data = {}
try:
tree = xml.etree.ElementTree.parse(filename)
data[_SRCS] = []
root = tree.getroot()
for element in root.iter('sourceFolder'):
src = element.get(_URL).replace(_ANDROID_ROOT_PATH, _PRODUCT_DIR)
data[_SRCS].append(src)
data[_JARS] = []
for element in root.iter('root'):
jar = element.get(_URL).replace(_ANDROID_ROOT_PATH, _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 and write into a json file."""
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' % (atest_utils.colorize(
'Test error...', constants.RED), _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(atest_utils.colorize(_ALL_PASS, constants.GREEN))
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.
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."""
with open(_VERIFY_COMMANDS_JSON, 'r') as jsfile:
data = json.load(jsfile)
for use_case in data:
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 Exception as exp:
print('{}.{} command {}.'.format(
use_case, cmd,
atest_utils.colorize('executes failed', constants.RED)))
raise Exception(
'Unexpected command {} exception {}.'.format(use_case, exp))
print('{} command {}!'.format(
use_case, atest_utils.colorize('test passed', constants.GREEN)))
print(atest_utils.colorize(_ALL_PASS, constants.GREEN))
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:])