blob: 62b2b0ed3d29a49f09f70d3da0ac3359b551dfc7 [file] [log] [blame]
bralee5b848a72018-11-09 17:44:56 +08001#!/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
morrislin23b618e2020-10-22 14:36:51 +080020from __future__ import print_function
bralee5b848a72018-11-09 17:44:56 +080021
22import argparse
bralee2cb95822019-05-02 11:33:16 +080023import functools
bralee5b848a72018-11-09 17:44:56 +080024import itertools
25import json
bralee2cb95822019-05-02 11:33:16 +080026import logging
bralee5b848a72018-11-09 17:44:56 +080027import os
bralee14629942019-07-15 07:02:01 +080028import subprocess
bralee5b848a72018-11-09 17:44:56 +080029import sys
30import xml.etree.ElementTree
31import xml.parsers.expat
32
33from aidegen import aidegen_main
bralee2cb95822019-05-02 11:33:16 +080034from aidegen import constant
braleec89ed9f2020-01-23 22:26:58 +080035from aidegen.lib import clion_project_file_gen
morrislin23b618e2020-10-22 14:36:51 +080036# pylint: disable=no-name-in-module
bralee627fcd92019-06-04 08:41:28 +080037from aidegen.lib import common_util
bralee14629942019-07-15 07:02:01 +080038from aidegen.lib import errors
braleec89ed9f2020-01-23 22:26:58 +080039from aidegen.lib import module_info_util
40from aidegen.lib import project_config
bralee2cb95822019-05-02 11:33:16 +080041from aidegen.lib import project_file_gen
42
bralee5b848a72018-11-09 17:44:56 +080043from atest import module_info
bralee5b848a72018-11-09 17:44:56 +080044
bralee2cb95822019-05-02 11:33:16 +080045_PRODUCT_DIR = '$PROJECT_DIR$'
bralee627fcd92019-06-04 08:41:28 +080046_ROOT_DIR = os.path.join(common_util.get_android_root_dir(),
bralee5b848a72018-11-09 17:44:56 +080047 'tools/asuite/aidegen_functional_test')
48_TEST_DATA_PATH = os.path.join(_ROOT_DIR, 'test_data')
bralee77318822018-11-23 15:56:06 +080049_VERIFY_COMMANDS_JSON = os.path.join(_TEST_DATA_PATH, 'verify_commands.json')
bralee2cb95822019-05-02 11:33:16 +080050_GOLDEN_SAMPLES_JSON = os.path.join(_TEST_DATA_PATH, 'golden_samples.json')
bralee14629942019-07-15 07:02:01 +080051_VERIFY_BINARY_JSON = os.path.join(_TEST_DATA_PATH, 'verify_binary_upload.json')
morrislin23b618e2020-10-22 14:36:51 +080052_VERIFY_PRESUBMIT_JSON = os.path.join(_TEST_DATA_PATH, 'verify_presubmit.json')
bralee5b848a72018-11-09 17:44:56 +080053_ANDROID_COMMON = 'android_common'
54_LINUX_GLIBC_COMMON = 'linux_glibc_common'
55_SRCS = 'srcs'
56_JARS = 'jars'
57_URL = 'url'
bralee2cb95822019-05-02 11:33:16 +080058_TEST_ERROR = 'AIDEGen functional test error: {}-{} is different.'
59_MSG_NOT_IN_PROJECT_FILE = ('{} is expected, but not found in the created '
60 'project file: {}')
61_MSG_NOT_IN_SAMPLE_DATA = ('{} is unexpected, but found in the created project '
62 'file: {}')
bralee77318822018-11-23 15:56:06 +080063_ALL_PASS = 'All tests passed!'
bralee2cb95822019-05-02 11:33:16 +080064_GIT_COMMIT_ID_JSON = os.path.join(
65 _TEST_DATA_PATH, 'baseline_code_commit_id.json')
66_GIT = 'git'
67_CHECKOUT = 'checkout'
68_BRANCH = 'branch'
69_COMMIT = 'commit'
70_LOG = 'log'
71_ALL = '--all'
72_COMMIT_ID_NOT_EXIST_ERROR = ('Commit ID: {} does not exist in path: {}. '
73 'Please use "git log" command to check if it '
74 'exists. If it does not, try to update your '
75 'source files to the latest version or do not '
76 'use "repo init --depth=1" command.')
77_GIT_LOG_ERROR = 'Command "git log -n 1" failed.'
braleec89ed9f2020-01-23 22:26:58 +080078_BE_REPLACED = '${config.X86_64GccRoot}'
79_TO_REPLACE = 'prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9'
bralee5b848a72018-11-09 17:44:56 +080080
81
82def _parse_args(args):
83 """Parse command line arguments.
84
85 Args:
86 args: A list of arguments.
87
88 Returns:
bralee14629942019-07-15 07:02:01 +080089 An argparse.Namespace object holding parsed args.
bralee5b848a72018-11-09 17:44:56 +080090 """
91 parser = argparse.ArgumentParser(
92 description=__doc__,
93 formatter_class=argparse.RawDescriptionHelpFormatter,
bralee2cb95822019-05-02 11:33:16 +080094 usage='aidegen_functional_test [-c | -u | -b | -a] -v -r')
bralee77318822018-11-23 15:56:06 +080095 group = parser.add_mutually_exclusive_group()
bralee5b848a72018-11-09 17:44:56 +080096 parser.required = False
bralee2cb95822019-05-02 11:33:16 +080097 parser.add_argument(
98 'targets',
99 type=str,
100 nargs='*',
101 default=[''],
102 help='Android module name or path.e.g. frameworks/base')
bralee77318822018-11-23 15:56:06 +0800103 group.add_argument(
bralee5b848a72018-11-09 17:44:56 +0800104 '-c',
105 '--create-sample',
106 action='store_true',
107 dest='create_sample',
108 help=('Create aidegen project files and write data to sample json file '
109 'for aidegen_functional_test to compare.'))
bralee14629942019-07-15 07:02:01 +0800110 parser.add_argument(
bralee77318822018-11-23 15:56:06 +0800111 '-v',
bralee14629942019-07-15 07:02:01 +0800112 '--verbose',
bralee77318822018-11-23 15:56:06 +0800113 action='store_true',
bralee14629942019-07-15 07:02:01 +0800114 help='Show DEBUG level logging.')
115 parser.add_argument(
116 '-r',
117 '--remove_bp_json',
118 action='store_true',
119 help='Remove module_bp_java_deps.json for each use case test.')
bralee50284e32020-10-26 11:32:32 +0800120 parser.add_argument(
121 '-m',
122 '--make_clean',
123 action='store_true',
124 help=('Make clean before testing to create a clean environment, the '
125 'aidegen_functional_test can run only once if users command it.'))
bralee14629942019-07-15 07:02:01 +0800126 group.add_argument(
127 '-u',
bralee2cb95822019-05-02 11:33:16 +0800128 '--use_cases',
bralee14629942019-07-15 07:02:01 +0800129 action='store_true',
130 dest='use_cases_verified',
bralee77318822018-11-23 15:56:06 +0800131 help='Verify various use cases of executing aidegen.')
bralee14629942019-07-15 07:02:01 +0800132 group.add_argument(
133 '-b',
134 action='store_true',
135 dest='binary_upload_verified',
136 help=('Verify aidegen\'s use cases by executing different aidegen '
137 'commands.'))
bralee2cb95822019-05-02 11:33:16 +0800138 group.add_argument(
morrislin23b618e2020-10-22 14:36:51 +0800139 '-p',
140 action='store_true',
141 dest='binary_presubmit_verified',
142 help=('Verify aidegen\'s tool in presubmit test by executing'
143 'different aidegen commands.'))
144 group.add_argument(
bralee2cb95822019-05-02 11:33:16 +0800145 '-a',
146 '--test-all',
147 action='store_true',
148 dest='test_all_samples',
149 help='Test all modules listed in module-info.json.')
braleec89ed9f2020-01-23 22:26:58 +0800150 group.add_argument(
151 '-n',
152 '--compare-sample-native',
153 action='store_true',
154 dest='compare_sample_native',
155 help=('Compare if sample native project file is the same as generated '
156 'by the build system.'))
bralee5b848a72018-11-09 17:44:56 +0800157 return parser.parse_args(args)
158
159
160def _import_project_file_xml_etree(filename):
bralee2cb95822019-05-02 11:33:16 +0800161 """Import iml project file and load its data into a dictionary.
bralee5b848a72018-11-09 17:44:56 +0800162
163 Args:
164 filename: The input project file name.
bralee30fedf32019-05-20 08:30:28 +0800165
166 Returns:
bralee2cb95822019-05-02 11:33:16 +0800167 A dictionary contains dependent files' data of project file's contents.
168 The samples are like:
169 "srcs": [
170 ...
171 "file://$PROJECT_DIR$/frameworks/base/cmds/am/src",
172 "file://$PROJECT_DIR$/frameworks/base/cmds/appwidget/src",
173 ...
174 ]
175 "jars": [
176 ...
177 "jar://$PROJECT_DIR$/out/host/common/obj/**/classes-header.jar!/"
178 ...
179 ]
180
181 Raises:
182 EnvironmentError and xml parsing or format errors.
bralee5b848a72018-11-09 17:44:56 +0800183 """
184 data = {}
185 try:
186 tree = xml.etree.ElementTree.parse(filename)
187 data[_SRCS] = []
188 root = tree.getroot()
189 for element in root.iter('sourceFolder'):
bralee627fcd92019-06-04 08:41:28 +0800190 src = element.get(_URL).replace(common_util.get_android_root_dir(),
191 _PRODUCT_DIR)
bralee5b848a72018-11-09 17:44:56 +0800192 data[_SRCS].append(src)
193 data[_JARS] = []
194 for element in root.iter('root'):
bralee627fcd92019-06-04 08:41:28 +0800195 jar = element.get(_URL).replace(common_util.get_android_root_dir(),
196 _PRODUCT_DIR)
bralee5b848a72018-11-09 17:44:56 +0800197 data[_JARS].append(jar)
198 except (EnvironmentError, ValueError, LookupError,
199 xml.parsers.expat.ExpatError) as err:
200 print("{0}: import error: {1}".format(os.path.basename(filename), err))
201 raise
202 return data
203
204
bralee2cb95822019-05-02 11:33:16 +0800205def _get_project_file_names(abs_path):
206 """Get project file name and depenencies name by relative path.
207
208 Args:
209 abs_path: an absolute module's path.
bralee30fedf32019-05-20 08:30:28 +0800210
211 Returns:
bralee2cb95822019-05-02 11:33:16 +0800212 file_name: a string of the project file name.
213 dep_name: a string of the merged project and dependencies file's name,
214 e.g., frameworks-dependencies.iml.
215 """
morrislin23b618e2020-10-22 14:36:51 +0800216 # pylint: disable=maybe-no-member
bralee2cb95822019-05-02 11:33:16 +0800217 code_name = project_file_gen.ProjectFileGenerator.get_unique_iml_name(
218 abs_path)
219 file_name = ''.join([code_name, '.iml'])
220 dep_name = ''.join([constant.KEY_DEPENDENCIES, '.iml'])
221 return file_name, dep_name
222
223
224def _get_unique_module_name(rel_path, filename):
225 """Get a unique project file name or dependencies name by relative path.
226
227 Args:
228 rel_path: a relative module's path to aosp root path.
229 filename: the file name to be generated in module_in type file name.
230
231 Returns:
232 A string, the unique file name for the whole module-info.json.
233 """
234 code_names = rel_path.split(os.sep)
235 code_names[-1] = filename
236 return '-'.join(code_names)
237
238
239def _get_git_current_commit_id(abs_path):
240 """Get target's git checkout command list.
241
242 When we run command 'git log -n 1' and get the top first git log record, the
243 commit id is next to the specific word 'commit'.
244
245 Args:
246 abs_path: a string of the absolute path of the target branch.
247
248 Return:
249 The current git commit id.
250
251 Raises:
252 Call subprocess.check_output cause subprocess.CalledProcessError.
253 """
254 cwd = os.getcwd()
255 os.chdir(abs_path)
256 git_log_cmds = [_GIT, _LOG, '-n', '1']
257 try:
258 out_put = subprocess.check_output(git_log_cmds).decode("utf-8")
259 except subprocess.CalledProcessError:
260 logging.error(_GIT_LOG_ERROR)
261 raise
262 finally:
263 os.chdir(cwd)
264 com_list = out_put.split()
265 return com_list[com_list.index(_COMMIT) + 1]
266
267
268def _get_commit_id_dictionary():
269 """Get commit id from dictionary of key, value 'module': 'commit id'."""
270 data_id_dict = {}
271 with open(_GIT_COMMIT_ID_JSON, 'r') as jsfile:
272 data_id_dict = json.load(jsfile)
273 return data_id_dict
274
275
276def _git_checkout_commit_id(abs_path, commit_id):
277 """Command to checkout specific commit id.
278
279 Change directory to the module's absolute path which users want to get its
280 specific commit id.
281
282 Args:
283 abs_path: the absolute path of the target branch. E.g., abs_path/.git
284 commit_id: the commit id users want to checkout.
285
286 Raises:
287 Call git checkout commit id failed, raise errors.CommitIDNotExistError.
288 """
289 git_chekout_cmds = [_GIT, _CHECKOUT, commit_id]
290 cwd = os.getcwd()
291 try:
292 os.chdir(abs_path)
293 subprocess.check_output(git_chekout_cmds)
294 except subprocess.CalledProcessError:
295 err = _COMMIT_ID_NOT_EXIST_ERROR.format(commit_id, abs_path)
296 logging.error(err)
297 raise errors.CommitIDNotExistError(err)
298 finally:
299 os.chdir(cwd)
300
301
302def _git_checkout_target_commit_id(target, commit_id):
303 """Command to checkout target commit id.
304
305 Switch code base to specific commit id which is kept in data_id_dict with
306 target: commit_id as key: value. If the data is missing in data_id_dict, the
307 target isn't a selected golden sample raise error for it.
308
309 Args:
310 target: the string of target's module name or module path to checkout
311 the relevant git to its specific commit id.
312 commit_id: a string represent target's specific commit id.
313
314 Returns:
315 current_commit_id: the current commit id of the target which should be
316 switched back to.
bralee30fedf32019-05-20 08:30:28 +0800317 """
bralee5b848a72018-11-09 17:44:56 +0800318 atest_module_info = module_info.ModuleInfo()
bralee2cb95822019-05-02 11:33:16 +0800319 _, abs_path = common_util.get_related_paths(atest_module_info, target)
320 current_commit_id = _get_git_current_commit_id(abs_path)
321 _git_checkout_commit_id(abs_path, commit_id)
322 return current_commit_id
323
324
325def _checkout_baseline_code_to_spec_commit_id():
326 """Get a dict of target, commit id listed in baseline_code_commit_id.json.
327
328 To make sure all samples run on the same environment, we need to keep all
329 the baseline code in a specific commit id. For example, all samples should
330 be created in the same specific commit id of the baseline code
331 'frameworks/base' for comparison except 'frameworks/base' itself.
332
333 Returns:
334 A dictionary contains target, specific and current commit id.
335 """
336 spec_and_cur_commit_id_dict = {}
337 data_id_dict = _get_commit_id_dictionary()
338 for target in data_id_dict:
339 commit_id = data_id_dict.get(target, '')
340 current_commit_id = _git_checkout_target_commit_id(target, commit_id)
341 spec_and_cur_commit_id_dict[target] = {}
342 spec_and_cur_commit_id_dict[target]['current'] = current_commit_id
343 return spec_and_cur_commit_id_dict
344
345
346def _generate_target_real_iml_data(target):
347 """Generate single target's real iml file content's data.
348
349 Args:
350 target: Android module name or path to be create iml data.
351
352 Returns:
353 data: A dictionary contains key: unique file name and value: iml
354 content.
355 """
bralee5b848a72018-11-09 17:44:56 +0800356 data = {}
bralee2cb95822019-05-02 11:33:16 +0800357 try:
358 aidegen_main.main([target, '-s', '-n', '-v'])
359 except (errors.FakeModuleError,
360 errors.ProjectOutsideAndroidRootError,
361 errors.ProjectPathNotExistError,
362 errors.NoModuleDefinedInModuleInfoError) as err:
363 logging.error(str(err))
364 return data
365
366 atest_module_info = module_info.ModuleInfo()
367 rel_path, abs_path = common_util.get_related_paths(atest_module_info,
368 target)
369 for filename in iter(_get_project_file_names(abs_path)):
370 real_iml_file = os.path.join(abs_path, filename)
371 item_name = _get_unique_module_name(rel_path, filename)
372 data[item_name] = _import_project_file_xml_etree(real_iml_file)
bralee5b848a72018-11-09 17:44:56 +0800373 return data
374
375
bralee2cb95822019-05-02 11:33:16 +0800376def _generate_sample_json(test_list):
377 """Generate sample iml data.
378
379 We use all baseline code samples on the version of their own specific commit
380 id which is kept in _GIT_COMMIT_ID_JSON file. We need to switch back to
381 their current commit ids after generating golden samples' data.
382
383 Args:
384 test_list: a list of module name and module path.
bralee50284e32020-10-26 11:32:32 +0800385
bralee2cb95822019-05-02 11:33:16 +0800386 Returns:
387 data: a dictionary contains dependent files' data of project file's
388 contents.
389 The sample is like:
390 "frameworks-base.iml": {
391 "srcs": [
392 ....
393 "file://$PROJECT_DIR$/frameworks/base/cmds/am/src",
394 "file://$PROJECT_DIR$/frameworks/base/cmds/appwidget/src",
395 ....
396 ],
397 "jars": [
398 ....
399 "jar://$PROJECT_DIR$/out/target/common/**/aapt2.srcjar!/",
400 ....
401 ]
402 }
403 """
bralee2cb95822019-05-02 11:33:16 +0800404 data = {}
405 spec_and_cur_commit_id_dict = _checkout_baseline_code_to_spec_commit_id()
406 for target in test_list:
407 data.update(_generate_target_real_iml_data(target))
408 atest_module_info = module_info.ModuleInfo()
409 for target in spec_and_cur_commit_id_dict:
410 _, abs_path = common_util.get_related_paths(atest_module_info, target)
411 _git_checkout_commit_id(
412 abs_path, spec_and_cur_commit_id_dict[target]['current'])
413 return data
414
415
416def _create_some_sample_json_file(targets):
417 """Write some samples' iml data into a json file.
418
419 Args:
420 targets: Android module name or path to be create iml data.
bralee5b848a72018-11-09 17:44:56 +0800421
422 linked_function: _generate_sample_json()
423 """
bralee2cb95822019-05-02 11:33:16 +0800424 data = _generate_sample_json(targets)
425 data_sample = {}
426 with open(_GOLDEN_SAMPLES_JSON, 'r') as infile:
427 try:
428 data_sample = json.load(infile)
morrislin23b618e2020-10-22 14:36:51 +0800429 # pylint: disable=maybe-no-member
bralee2cb95822019-05-02 11:33:16 +0800430 except json.JSONDecodeError as err:
431 print("Json decode error: {}".format(err))
432 data_sample = {}
433 data_sample.update(data)
434 with open(_GOLDEN_SAMPLES_JSON, 'w') as outfile:
435 json.dump(data_sample, outfile, indent=4, sort_keys=False)
bralee5b848a72018-11-09 17:44:56 +0800436
437
bralee2cb95822019-05-02 11:33:16 +0800438def test_samples(func):
439 """Decorate a function to deal with preparing and verifying staffs of it.
440
441 Args:
442 func: a function is to be compared its iml data with the json file's
443 data.
444
445 Returns:
446 The wrapper function.
447 """
448
449 @functools.wraps(func)
450 def wrapper(*args, **kwargs):
451 """A wrapper function."""
452
453 test_successful = True
454 with open(_GOLDEN_SAMPLES_JSON, 'r') as outfile:
455 data_sample = json.load(outfile)
456
457 data_real = func(*args, **kwargs)
458
459 for name in data_real:
460 for item in [_SRCS, _JARS]:
461 s_items = data_sample[name][item]
462 r_items = data_real[name][item]
463 if set(s_items) != set(r_items):
464 diff_iter = _compare_content(name, item, s_items, r_items)
465 if diff_iter:
466 print('\n{} {}'.format(
467 common_util.COLORED_INFO('Test error:'),
468 _TEST_ERROR.format(name, item)))
469 print('{} {} contents are different:'.format(
470 name, item))
471 for diff in diff_iter:
472 print(diff)
473 test_successful = False
474 if test_successful:
475 print(common_util.COLORED_PASS(_ALL_PASS))
476 return test_successful
477
478 return wrapper
479
480
481@test_samples
482def _test_some_sample_iml(targets=None):
483 """Compare with sample iml's data to assure the project's contents is right.
484
485 Args:
486 targets: Android module name or path to be create iml data.
487 """
488 if targets:
489 return _generate_sample_json(targets)
490 return _generate_sample_json(_get_commit_id_dictionary().keys())
491
492
493@test_samples
494def _test_all_samples_iml():
495 """Compare all imls' data with all samples' data.
496
497 It's to make sure each iml's contents is right. The function is implemented
498 but hasn't been used yet.
499 """
500 all_module_list = module_info.ModuleInfo().name_to_module_info.keys()
501 return _generate_sample_json(all_module_list)
bralee5b848a72018-11-09 17:44:56 +0800502
503
504def _compare_content(module_name, item_type, s_items, r_items):
505 """Compare src or jar files' data of two dictionaries.
506
507 Args:
508 module_name: the test module name.
509 item_type: the type is src or jar.
510 s_items: sample jars' items.
511 r_items: real jars' items.
512
513 Returns:
514 An iterator of not equal sentences after comparison.
515 """
516 if item_type == _SRCS:
517 cmp_iter1 = _compare_srcs_content(module_name, s_items, r_items,
518 _MSG_NOT_IN_PROJECT_FILE)
519 cmp_iter2 = _compare_srcs_content(module_name, r_items, s_items,
520 _MSG_NOT_IN_SAMPLE_DATA)
521 else:
522 cmp_iter1 = _compare_jars_content(module_name, s_items, r_items,
523 _MSG_NOT_IN_PROJECT_FILE)
524 cmp_iter2 = _compare_jars_content(module_name, r_items, s_items,
525 _MSG_NOT_IN_SAMPLE_DATA)
526 return itertools.chain(cmp_iter1, cmp_iter2)
527
528
529def _compare_srcs_content(module_name, s_items, r_items, msg):
530 """Compare src or jar files' data of two dictionaries.
531
532 Args:
533 module_name: the test module name.
534 s_items: sample jars' items.
535 r_items: real jars' items.
536 msg: the message will be written into log file.
537
538 Returns:
539 An iterator of not equal sentences after comparison.
540 """
541 for sample in s_items:
bralee2cb95822019-05-02 11:33:16 +0800542 if sample not in r_items:
543 yield msg.format(sample, module_name)
bralee5b848a72018-11-09 17:44:56 +0800544
545
546def _compare_jars_content(module_name, s_items, r_items, msg):
547 """Compare src or jar files' data of two dictionaries.
548
bralee30fedf32019-05-20 08:30:28 +0800549 AIDEGen treats the jars in folder names 'linux_glib_common' and
550 'android_common' as the same content. If the paths are different only
551 because of these two names, we ignore it.
552
bralee5b848a72018-11-09 17:44:56 +0800553 Args:
554 module_name: the test module name.
555 s_items: sample jars' items.
556 r_items: real jars' items.
557 msg: the message will be written into log file.
558
559 Returns:
560 An iterator of not equal sentences after comparison.
561 """
562 for sample in s_items:
bralee2cb95822019-05-02 11:33:16 +0800563 if sample not in r_items:
bralee5b848a72018-11-09 17:44:56 +0800564 lnew = sample
bralee2cb95822019-05-02 11:33:16 +0800565 if constant.LINUX_GLIBC_COMMON in sample:
566 lnew = sample.replace(constant.LINUX_GLIBC_COMMON,
567 constant.ANDROID_COMMON)
bralee5b848a72018-11-09 17:44:56 +0800568 else:
bralee2cb95822019-05-02 11:33:16 +0800569 lnew = sample.replace(constant.ANDROID_COMMON,
570 constant.LINUX_GLIBC_COMMON)
bralee5b848a72018-11-09 17:44:56 +0800571 if not lnew in r_items:
bralee2cb95822019-05-02 11:33:16 +0800572 yield msg.format(sample, module_name)
bralee5b848a72018-11-09 17:44:56 +0800573
574
bralee77318822018-11-23 15:56:06 +0800575# pylint: disable=broad-except
576# pylint: disable=eval-used
braleea65f1e52019-10-17 15:31:47 +0800577@common_util.back_to_cwd
bralee14629942019-07-15 07:02:01 +0800578@common_util.time_logged
morrislin23b618e2020-10-22 14:36:51 +0800579def _verify_aidegen(verified_file_path, forced_remove_bp_json,
580 is_presubmit=False):
bralee14629942019-07-15 07:02:01 +0800581 """Verify various use cases of executing aidegen.
582
583 There are two types of running commands:
584 1. Use 'eval' to run the commands for present codes in aidegen_main.py,
585 such as:
586 aidegen_main.main(['tradefed', '-n', '-v'])
587 2. Use 'subprocess.check_call' to run the commands for the binary codes of
588 aidegen such as:
589 aidegen tradefed -n -v
590
591 Remove module_bp_java_deps.json in the beginning of running use cases. If
592 users need to remove module_bp_java_deps.json between each use case they
593 can set forced_remove_bp_json true.
594
bralee2cb95822019-05-02 11:33:16 +0800595 Args:
bralee14629942019-07-15 07:02:01 +0800596 verified_file_path: The json file path to be verified.
597 forced_remove_bp_json: Remove module_bp_java_deps.json for each use case
598 test.
599
bralee2cb95822019-05-02 11:33:16 +0800600 Raises:
bralee14629942019-07-15 07:02:01 +0800601 There are two type of exceptions:
602 1. aidegen.lib.errors for projects' or modules' issues such as,
603 ProjectPathNotExistError.
604 2. Any exceptions other than aidegen.lib.errors such as,
605 subprocess.CalledProcessError.
606 """
braleeec0dd882019-12-12 21:17:16 +0800607 bp_json_path = common_util.get_blueprint_json_path(
608 constant.BLUEPRINT_JAVA_JSONFILE_NAME)
bralee14629942019-07-15 07:02:01 +0800609 use_eval = (verified_file_path == _VERIFY_COMMANDS_JSON)
610 try:
611 with open(verified_file_path, 'r') as jsfile:
612 data = json.load(jsfile)
613 except IOError as err:
614 raise errors.JsonFileNotExistError(
615 '%s does not exist, error: %s.' % (verified_file_path, err))
616
morrislin23b618e2020-10-22 14:36:51 +0800617 if not is_presubmit:
morrislin23b618e2020-10-22 14:36:51 +0800618 _compare_sample_native_content()
bralee14629942019-07-15 07:02:01 +0800619
braleea65f1e52019-10-17 15:31:47 +0800620 os.chdir(common_util.get_android_root_dir())
bralee77318822018-11-23 15:56:06 +0800621 for use_case in data:
bralee14629942019-07-15 07:02:01 +0800622 print('Use case "{}" is running.'.format(use_case))
623 if forced_remove_bp_json and os.path.exists(bp_json_path):
bralee30fedf32019-05-20 08:30:28 +0800624 os.remove(bp_json_path)
bralee77318822018-11-23 15:56:06 +0800625 for cmd in data[use_case]:
bralee14629942019-07-15 07:02:01 +0800626 print('Command "{}" is running.'.format(cmd))
bralee77318822018-11-23 15:56:06 +0800627 try:
bralee14629942019-07-15 07:02:01 +0800628 if use_eval:
629 eval(cmd)
630 else:
631 subprocess.check_call(cmd, shell=True)
632 except (errors.ProjectOutsideAndroidRootError,
633 errors.ProjectPathNotExistError,
634 errors.NoModuleDefinedInModuleInfoError,
635 errors.IDENotExistError) as err:
636 print('"{}" raises error: {}.'.format(use_case, err))
bralee2cb95822019-05-02 11:33:16 +0800637 continue
bralee30fedf32019-05-20 08:30:28 +0800638 except BaseException:
639 exc_type, _, _ = sys.exc_info()
bralee14629942019-07-15 07:02:01 +0800640 print('"{}.{}" command {}.'.format(
641 use_case, cmd, common_util.COLORED_FAIL('executes failed')))
bralee30fedf32019-05-20 08:30:28 +0800642 raise BaseException(
bralee14629942019-07-15 07:02:01 +0800643 'Unexpected command "{}" exception: {}.'.format(
bralee30fedf32019-05-20 08:30:28 +0800644 use_case, exc_type))
bralee14629942019-07-15 07:02:01 +0800645 print('"{}" command {}!'.format(
646 use_case, common_util.COLORED_PASS('test passed')))
647 print(common_util.COLORED_PASS(_ALL_PASS))
648
bralee77318822018-11-23 15:56:06 +0800649
bralee2cb95822019-05-02 11:33:16 +0800650@common_util.back_to_cwd
651def _make_clean():
652 """Make a command to clean out folder for a pure environment to test.
653
654 Raises:
655 Call subprocess.check_call to execute
656 'build/soong/soong_ui.bash --make-mode clean' and cause
657 subprocess.CalledProcessError.
658 """
659 try:
660 os.chdir(common_util.get_android_root_dir())
661 subprocess.check_call(
662 ['build/soong/soong_ui.bash --make-mode clean', '-j'],
663 shell=True)
664 except subprocess.CalledProcessError:
665 print('"build/soong/soong_ui.bash --make-mode clean" command failed.')
666 raise
667
668
braleec89ed9f2020-01-23 22:26:58 +0800669def _read_file_content(path):
670 """Read file's content.
671
672 Args:
673 path: Path of input file.
674
675 Returns:
676 A list of content strings.
677 """
678 with open(path, encoding='utf8') as template:
679 contents = []
680 for cnt in template:
681 if cnt.startswith('#'):
682 continue
683 contents.append(cnt.rstrip())
684 return contents
685
686
687# pylint: disable=protected-access
688def _compare_sample_native_content():
689 """Compares 'libui' sample module's project file.
690
691 Compares 'libui' sample module's project file generated by AIDEGen with that
692 generated by the soong build system. Check if their contents are the same.
693 There should be only one different:
694 ${config.X86_64GccRoot} # in the soong build sytem
695 becomes
696 prebuilts/gcc/linux-x86/x86/x86_64-linux-android-4.9 # in AIDEGen
697 """
braleec89ed9f2020-01-23 22:26:58 +0800698 target_arch_variant = 'x86_64'
699 env_on = {
700 'TARGET_PRODUCT': 'aosp_x86_64',
701 'TARGET_BUILD_VARIANT': 'eng',
702 'TARGET_ARCH_VARIANT': target_arch_variant,
703 'SOONG_COLLECT_JAVA_DEPS': 'true',
704 'SOONG_GEN_CMAKEFILES': '1',
braleec89ed9f2020-01-23 22:26:58 +0800705 'SOONG_COLLECT_CC_DEPS': '1'
706 }
707
708 try:
709 project_config.ProjectConfig(
710 aidegen_main._parse_args(['-n', '-v'])).init_environment()
bralee20699f02020-07-13 09:36:45 +0800711 module_info_util.generate_merged_module_info(env_on)
braleec89ed9f2020-01-23 22:26:58 +0800712 cc_path = os.path.join(common_util.get_soong_out_path(),
713 constant.BLUEPRINT_CC_JSONFILE_NAME)
714 mod_name = 'libui'
715 mod_info = common_util.get_json_dict(cc_path)['modules'][mod_name]
716 if mod_info:
717 clion_project_file_gen.CLionProjectFileGenerator(
718 mod_info).generate_cmakelists_file()
719 out_dir = os.path.join(common_util.get_android_root_dir(),
720 common_util.get_android_out_dir(),
721 constant.RELATIVE_NATIVE_PATH,
722 mod_info['path'][0])
723 content1 = _read_file_content(os.path.join(
724 out_dir, mod_name, constant.CLION_PROJECT_FILE_NAME))
725 cc_file_name = ''.join(
726 [mod_name, '-', target_arch_variant, '-android'])
727 cc_file_path = os.path.join(
728 out_dir, cc_file_name, constant.CLION_PROJECT_FILE_NAME)
729 content2 = _read_file_content(cc_file_path)
730 same = True
731 for lino, (cnt1, cnt2) in enumerate(
732 zip(content1, content2), start=1):
733 if _BE_REPLACED in cnt2:
734 cnt2 = cnt2.replace(_BE_REPLACED, _TO_REPLACE)
735 if cnt1 != cnt2:
736 print('Contents {} and {} are different in line:{}.'.format(
737 cnt1, cnt2, lino))
738 same = False
739 if same:
740 print('Files {} and {} are the same.'.format(
741 mod_name, cc_file_name))
742 except errors.BuildFailureError:
743 print('Compare native content failed.')
744
745
bralee5b848a72018-11-09 17:44:56 +0800746def main(argv):
747 """Main entry.
748
bralee2cb95822019-05-02 11:33:16 +0800749 1. Create the iml file data of each module in module-info.json and write it
750 into single_module.json.
751 2. Verify every use case of AIDEGen.
752 3. Compare all or some iml project files' data to the data recorded in
753 single_module.json.
bralee5b848a72018-11-09 17:44:56 +0800754
755 Args:
756 argv: A list of system arguments.
757 """
758 args = _parse_args(argv)
bralee14629942019-07-15 07:02:01 +0800759 common_util.configure_logging(args.verbose)
bralee23789ed2020-04-24 19:23:07 +0800760 os.environ[constant.AIDEGEN_TEST_MODE] = 'true'
bralee50284e32020-10-26 11:32:32 +0800761
762 if args.make_clean:
763 _make_clean()
764
bralee5b848a72018-11-09 17:44:56 +0800765 if args.create_sample:
bralee2cb95822019-05-02 11:33:16 +0800766 _create_some_sample_json_file(args.targets)
bralee14629942019-07-15 07:02:01 +0800767 elif args.use_cases_verified:
768 _verify_aidegen(_VERIFY_COMMANDS_JSON, args.remove_bp_json)
769 elif args.binary_upload_verified:
770 _verify_aidegen(_VERIFY_BINARY_JSON, args.remove_bp_json)
morrislin23b618e2020-10-22 14:36:51 +0800771 elif args.binary_presubmit_verified:
772 _verify_aidegen(_VERIFY_PRESUBMIT_JSON, args.remove_bp_json, True)
bralee2cb95822019-05-02 11:33:16 +0800773 elif args.test_all_samples:
774 _test_all_samples_iml()
braleec89ed9f2020-01-23 22:26:58 +0800775 elif args.compare_sample_native:
776 _compare_sample_native_content()
bralee5b848a72018-11-09 17:44:56 +0800777 else:
bralee2cb95822019-05-02 11:33:16 +0800778 if not args.targets[0]:
779 _test_some_sample_iml()
780 else:
781 _test_some_sample_iml(args.targets)
bralee50284e32020-10-26 11:32:32 +0800782
bralee23789ed2020-04-24 19:23:07 +0800783 del os.environ[constant.AIDEGEN_TEST_MODE]
bralee5b848a72018-11-09 17:44:56 +0800784
785
786if __name__ == '__main__':
787 main(sys.argv[1:])