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"""Unittests for project_info.""" 18 19import logging 20import os 21import shutil 22import tempfile 23import unittest 24from unittest import mock 25 26from aidegen import constant 27from aidegen import unittest_constants 28from aidegen.lib import common_util 29from aidegen.lib import project_info 30from aidegen.lib import project_config 31from aidegen.lib import source_locator 32 33_MODULE_INFO = { 34 'm1': { 35 'class': ['JAVA_LIBRARIES'], 36 'dependencies': ['m2', 'm6'], 37 'path': ['m1'] 38 }, 39 'm2': { 40 'class': ['JAVA_LIBRARIES'], 41 'dependencies': ['m3', 'm4'] 42 }, 43 'm3': { 44 'class': ['JAVA_LIBRARIES'], 45 'dependencies': [] 46 }, 47 'm4': { 48 'class': ['JAVA_LIBRARIES'], 49 'dependencies': ['m6'] 50 }, 51 'm5': { 52 'class': ['JAVA_LIBRARIES'], 53 'dependencies': [] 54 }, 55 'm6': { 56 'class': ['JAVA_LIBRARIES'], 57 'dependencies': ['m2'] 58 }, 59} 60_EXPECT_DEPENDENT_MODULES = { 61 'm1': { 62 'class': ['JAVA_LIBRARIES'], 63 'dependencies': ['m2', 'm6'], 64 'path': ['m1'], 65 'depth': 0 66 }, 67 'm2': { 68 'class': ['JAVA_LIBRARIES'], 69 'dependencies': ['m3', 'm4'], 70 'depth': 1 71 }, 72 'm3': { 73 'class': ['JAVA_LIBRARIES'], 74 'dependencies': [], 75 'depth': 2 76 }, 77 'm4': { 78 'class': ['JAVA_LIBRARIES'], 79 'dependencies': ['m6'], 80 'depth': 2 81 }, 82 'm6': { 83 'class': ['JAVA_LIBRARIES'], 84 'dependencies': ['m2'], 85 'depth': 1 86 }, 87} 88 89 90# pylint: disable=protected-access 91# pylint: disable=invalid-name 92class ProjectInfoUnittests(unittest.TestCase): 93 """Unit tests for project_info.py""" 94 95 def setUp(self): 96 """Initialize arguments for ProjectInfo.""" 97 self.args = mock.MagicMock() 98 self.args.module_name = 'm1' 99 self.args.project_path = '' 100 self.args.ide = ['j'] 101 self.args.no_launch = True 102 self.args.depth = 0 103 self.args.android_tree = False 104 self.args.skip_build = True 105 self.args.targets = ['m1'] 106 self.args.verbose = False 107 self.args.ide_installed_path = None 108 self.args.config_reset = False 109 self.args.language = ['j'] 110 111 @mock.patch('atest.module_info.ModuleInfo') 112 def test_get_dep_modules(self, mock_module_info): 113 """Test get_dep_modules recursively find dependent modules.""" 114 mock_module_info.name_to_module_info = _MODULE_INFO 115 mock_module_info.is_module.return_value = True 116 mock_module_info.get_paths.return_value = ['m1'] 117 mock_module_info.get_module_names.return_value = ['m1'] 118 project_info.ProjectInfo.modules_info = mock_module_info 119 proj_info = project_info.ProjectInfo(self.args.module_name, False) 120 self.assertEqual(proj_info.dep_modules, _EXPECT_DEPENDENT_MODULES) 121 122 @mock.patch.object(project_info.ProjectInfo, 123 '_get_modules_under_project_path') 124 @mock.patch.object(project_info.ProjectInfo, 'get_dep_modules') 125 def test_init(self, mock_get_deps, mock_get_sub_modules): 126 """Test init.""" 127 project_info.ProjectInfo(constant.FRAMEWORK_ALL, False) 128 self.assertTrue(mock_get_deps.called) 129 self.assertFalse(mock_get_sub_modules.called) 130 131 @mock.patch.object(common_util, 'get_android_root_dir') 132 def test_get_target_name(self, mock_get_root): 133 """Test get_target_name with different conditions.""" 134 mock_get_root.return_value = unittest_constants.TEST_DATA_PATH 135 self.assertEqual( 136 project_info.ProjectInfo.get_target_name( 137 unittest_constants.TEST_MODULE, 138 unittest_constants.TEST_DATA_PATH), 139 os.path.basename(unittest_constants.TEST_DATA_PATH)) 140 self.assertEqual( 141 project_info.ProjectInfo.get_target_name( 142 unittest_constants.TEST_MODULE, unittest_constants.TEST_PATH), 143 unittest_constants.TEST_MODULE) 144 145 # pylint: disable=too-many-locals 146 @mock.patch('logging.info') 147 @mock.patch.object(common_util, 'get_android_root_dir') 148 @mock.patch('atest.module_info.ModuleInfo') 149 @mock.patch('atest.atest_utils.build') 150 def test_locate_source(self, mock_atest_utils_build, mock_module_info, 151 mock_get_root, mock_info): 152 """Test locate_source handling.""" 153 mock_atest_utils_build.build.return_value = True 154 test_root_path = os.path.join(tempfile.mkdtemp(), 'test') 155 shutil.copytree(unittest_constants.TEST_DATA_PATH, test_root_path) 156 mock_get_root.return_value = test_root_path 157 generated_jar = ('out/soong/.intermediates/packages/apps/test/test/' 158 'android_common/generated.jar') 159 locate_module_info = dict(unittest_constants.MODULE_INFO) 160 locate_module_info['installed'] = [generated_jar] 161 mock_module_info.is_module.return_value = True 162 mock_module_info.get_paths.return_value = [ 163 unittest_constants.MODULE_PATH 164 ] 165 mock_module_info.get_module_names.return_value = [ 166 unittest_constants.TEST_MODULE 167 ] 168 project_config.ProjectConfig(self.args) 169 project_info_obj = project_info.ProjectInfo( 170 mock_module_info.get_paths()[0]) 171 project_info_obj.dep_modules = { 172 unittest_constants.TEST_MODULE: locate_module_info 173 } 174 project_info_obj._init_source_path() 175 # Show warning when the jar not exists after build the module. 176 result_jar = set() 177 project_info_obj.locate_source() 178 self.assertEqual(project_info_obj.source_path['jar_path'], result_jar) 179 self.assertTrue(mock_info.called) 180 181 # Test collects source and test folders. 182 result_source = set(['packages/apps/test/src/main/java']) 183 result_test = set(['packages/apps/test/tests']) 184 self.assertEqual(project_info_obj.source_path['source_folder_path'], 185 result_source) 186 self.assertEqual(project_info_obj.source_path['test_folder_path'], 187 result_test) 188 189 @mock.patch.object(project_info, 'batch_build_dependencies') 190 @mock.patch.object(common_util, 'get_android_root_dir') 191 @mock.patch('atest.module_info.ModuleInfo') 192 @mock.patch('atest.atest_utils.build') 193 def test_locate_source_with_skip_build(self, mock_atest_utils_build, 194 mock_module_info, mock_get_root, 195 mock_batch): 196 """Test locate_source handling.""" 197 mock_atest_utils_build.build.return_value = True 198 test_root_path = os.path.join(tempfile.mkdtemp(), 'test') 199 shutil.copytree(unittest_constants.TEST_DATA_PATH, test_root_path) 200 mock_get_root.return_value = test_root_path 201 generated_jar = ('out/soong/.intermediates/packages/apps/test/test/' 202 'android_common/generated.jar') 203 locate_module_info = dict(unittest_constants.MODULE_INFO) 204 locate_module_info['installed'] = [generated_jar] 205 mock_module_info.is_module.return_value = True 206 mock_module_info.get_paths.return_value = [ 207 unittest_constants.MODULE_PATH 208 ] 209 mock_module_info.get_module_names.return_value = [ 210 unittest_constants.TEST_MODULE 211 ] 212 args = mock.MagicMock() 213 args.module_name = 'm1' 214 args.project_path = '' 215 args.ide = ['j'] 216 args.no_launch = True 217 args.depth = 0 218 args.android_tree = False 219 args.skip_build = True 220 args.targets = ['m1'] 221 args.verbose = False 222 args.ide_installed_path = None 223 args.config_reset = False 224 args.language = ['j'] 225 project_config.ProjectConfig(args) 226 project_info_obj = project_info.ProjectInfo( 227 mock_module_info.get_paths()[0]) 228 project_info_obj.dep_modules = { 229 unittest_constants.TEST_MODULE: locate_module_info 230 } 231 project_info_obj._init_source_path() 232 project_info_obj.locate_source() 233 self.assertFalse(mock_batch.called) 234 235 args.ide = ['v'] 236 args.skip_build = False 237 project_config.ProjectConfig(args) 238 project_info_obj = project_info.ProjectInfo( 239 mock_module_info.get_paths()[0]) 240 project_info_obj.dep_modules = { 241 unittest_constants.TEST_MODULE: locate_module_info 242 } 243 project_info_obj._init_source_path() 244 project_info_obj.locate_source() 245 self.assertFalse(mock_batch.called) 246 247 def test_separate_build_target(self): 248 """Test separate_build_target.""" 249 test_list = ['1', '22', '333', '4444', '55555', '1', '7777777'] 250 targets = [] 251 sample = [['1', '22', '333'], ['4444'], ['55555', '1'], ['7777777']] 252 for start, end in iter( 253 project_info._separate_build_targets(test_list, 9)): 254 targets.append(test_list[start:end]) 255 self.assertEqual(targets, sample) 256 257 def test_separate_build_target_with_length_short(self): 258 """Test separate_build_target with length short.""" 259 test_list = ['1'] 260 sample = [['1']] 261 targets = [] 262 for start, end in iter( 263 project_info._separate_build_targets(test_list, 9)): 264 targets.append(test_list[start:end]) 265 self.assertEqual(targets, sample) 266 267 @mock.patch.object(project_info.ProjectInfo, 'locate_source') 268 @mock.patch('atest.module_info.ModuleInfo') 269 def test_rebuild_jar_once(self, mock_module_info, mock_locate_source): 270 """Test rebuild the jar/srcjar only one time.""" 271 mock_module_info.get_paths.return_value = ['m1'] 272 project_info.ProjectInfo.modules_info = mock_module_info 273 proj_info = project_info.ProjectInfo(self.args.module_name, False) 274 proj_info.locate_source(build=False) 275 self.assertEqual(mock_locate_source.call_count, 1) 276 proj_info.locate_source(build=True) 277 self.assertEqual(mock_locate_source.call_count, 2) 278 279 @mock.patch('builtins.print') 280 @mock.patch('builtins.format') 281 @mock.patch('atest.atest_utils.build') 282 def test_build_target(self, mock_build, mock_format, mock_print): 283 """Test _build_target.""" 284 build_argument = ['-k', 'j'] 285 test_targets = ['mod_1', 'mod_2'] 286 build_argument.extend(test_targets) 287 mock_build.return_value = False 288 project_info._build_target(test_targets) 289 self.assertTrue(mock_build.called_with((build_argument, True))) 290 self.assertTrue(mock_format.called_with('\n'.join(test_targets))) 291 self.assertTrue(mock_print.called) 292 mock_print.reset_mock() 293 mock_format.reset_mock() 294 mock_build.reset_mock() 295 296 mock_build.return_value = True 297 project_info._build_target(test_targets) 298 self.assertTrue(mock_build.called_with((build_argument, True))) 299 self.assertFalse(mock_format.called) 300 self.assertFalse(mock_print.called) 301 mock_print.reset_mock() 302 mock_format.reset_mock() 303 mock_build.reset_mock() 304 305 @mock.patch('builtins.print') 306 @mock.patch.object(project_info.ProjectInfo, '_search_android_make_files') 307 @mock.patch('atest.module_info.ModuleInfo') 308 def test_display_convert_make_files_message( 309 self, mock_module_info, mock_search, mock_print): 310 """Test _display_convert_make_files_message with conditions.""" 311 mock_search.return_value = [] 312 mock_module_info.get_paths.return_value = ['m1'] 313 project_info.ProjectInfo.modules_info = mock_module_info 314 proj_info = project_info.ProjectInfo(self.args.module_name) 315 proj_info._display_convert_make_files_message() 316 self.assertFalse(mock_print.called) 317 318 mock_print.mock_reset() 319 mock_search.return_value = ['a/b/path/to/target.mk'] 320 proj_info = project_info.ProjectInfo(self.args.module_name) 321 proj_info._display_convert_make_files_message() 322 self.assertTrue(mock_print.called) 323 324 @mock.patch.object(project_info, '_build_target') 325 @mock.patch.object(project_info, '_separate_build_targets') 326 @mock.patch.object(logging, 'info') 327 def test_batch_build_dependencies(self, mock_log, mock_sep, mock_build): 328 """Test batch_build_dependencies.""" 329 mock_sep.return_value = [(0, 1)] 330 project_info.batch_build_dependencies({'m1', 'm2'}) 331 self.assertTrue(mock_log.called) 332 self.assertTrue(mock_sep.called) 333 self.assertEqual(mock_build.call_count, 1) 334 335 336class MultiProjectsInfoUnittests(unittest.TestCase): 337 """Unit tests for MultiProjectsInfo class.""" 338 339 @mock.patch.object(project_info.ProjectInfo, '__init__') 340 @mock.patch.object(project_info.ProjectInfo, 'get_dep_modules') 341 @mock.patch.object(project_info.ProjectInfo, 342 '_get_robolectric_dep_module') 343 @mock.patch.object(project_info.ProjectInfo, 344 '_get_modules_under_project_path') 345 @mock.patch.object(common_util, 'get_related_paths') 346 def test_collect_all_dep_modules(self, mock_relpath, mock_sub_modules_path, 347 mock_robo_module, mock_get_dep_modules, 348 mock_init): 349 """Test _collect_all_dep_modules.""" 350 mock_init.return_value = None 351 mock_relpath.return_value = ('path/to/sub/module', '') 352 mock_sub_modules_path.return_value = 'sub_module' 353 mock_robo_module.return_value = 'robo_module' 354 expected = set(project_info._CORE_MODULES) 355 expected.update({'sub_module', 'robo_module'}) 356 proj = project_info.MultiProjectsInfo(['a']) 357 proj.project_module_names = set('framework-all') 358 proj.collect_all_dep_modules() 359 self.assertTrue(mock_get_dep_modules.called_with(expected)) 360 361 @mock.patch.object(logging, 'debug') 362 @mock.patch.object(source_locator, 'ModuleData') 363 @mock.patch.object(project_info.ProjectInfo, '__init__') 364 def test_gen_folder_base_dependencies(self, mock_init, mock_module_data, 365 mock_log): 366 """Test _gen_folder_base_dependencies.""" 367 mock_init.return_value = None 368 proj = project_info.MultiProjectsInfo(['a']) 369 module = mock.Mock() 370 mock_module_data.return_value = module 371 mock_module_data.module_path = '' 372 proj.gen_folder_base_dependencies(mock_module_data) 373 self.assertTrue(mock_log.called) 374 mock_module_data.module_path = 'a/b' 375 mock_module_data.src_dirs = ['a/b/c'] 376 mock_module_data.test_dirs = [] 377 mock_module_data.r_java_paths = [] 378 mock_module_data.srcjar_paths = [] 379 mock_module_data.jar_files = [] 380 mock_module_data.dep_paths = [] 381 proj.gen_folder_base_dependencies(mock_module_data) 382 expected = { 383 'a/b': { 384 'src_dirs': ['a/b/c'], 385 'test_dirs': [], 386 'r_java_paths': [], 387 'srcjar_paths': [], 388 'jar_files': [], 389 'dep_paths': [], 390 } 391 } 392 self.assertEqual(proj.path_to_sources, expected) 393 mock_module_data.srcjar_paths = ['x/y.srcjar'] 394 proj.gen_folder_base_dependencies(mock_module_data) 395 expected = { 396 'a/b': { 397 'src_dirs': ['a/b/c'], 398 'test_dirs': [], 399 'r_java_paths': [], 400 'srcjar_paths': ['x/y.srcjar'], 401 'jar_files': [], 402 'dep_paths': [], 403 } 404 } 405 self.assertEqual(proj.path_to_sources, expected) 406 407 @mock.patch.object(source_locator, 'ModuleData') 408 @mock.patch.object(project_info.ProjectInfo, '__init__') 409 def test_add_framework_base_path(self, mock_init, mock_module_data): 410 """Test _gen_folder_base_dependencies.""" 411 mock_init.return_value = None 412 proj = project_info.MultiProjectsInfo(['a']) 413 module = mock.Mock() 414 mock_module_data.return_value = module 415 mock_module_data.module_path = 'frameworks/base' 416 mock_module_data.module_name = 'framework-other' 417 mock_module_data.src_dirs = ['a/b/c'] 418 mock_module_data.test_dirs = [] 419 mock_module_data.r_java_paths = [] 420 mock_module_data.srcjar_paths = ['x/y.srcjar'] 421 mock_module_data.jar_files = [] 422 mock_module_data.dep_paths = [] 423 proj.gen_folder_base_dependencies(mock_module_data) 424 expected = { 425 'frameworks/base': { 426 'dep_paths': [], 427 'jar_files': [], 428 'r_java_paths': [], 429 'src_dirs': ['a/b/c'], 430 'srcjar_paths': [], 431 'test_dirs': [], 432 } 433 } 434 self.assertDictEqual(proj.path_to_sources, expected) 435 436 @mock.patch.object(source_locator, 'ModuleData') 437 @mock.patch.object(project_info.ProjectInfo, '__init__') 438 def test_add_framework_srcjar_path(self, mock_init, mock_module_data): 439 """Test _gen_folder_base_dependencies.""" 440 mock_init.return_value = None 441 proj = project_info.MultiProjectsInfo(['a']) 442 module = mock.Mock() 443 mock_module_data.return_value = module 444 mock_module_data.module_path = 'frameworks/base' 445 mock_module_data.module_name = 'framework-all' 446 mock_module_data.src_dirs = ['a/b/c'] 447 mock_module_data.test_dirs = [] 448 mock_module_data.r_java_paths = [] 449 mock_module_data.srcjar_paths = ['x/y.srcjar'] 450 mock_module_data.jar_files = [] 451 mock_module_data.dep_paths = [] 452 proj.gen_folder_base_dependencies(mock_module_data) 453 expected = { 454 'frameworks/base': { 455 'dep_paths': [], 456 'jar_files': [], 457 'r_java_paths': [], 458 'src_dirs': ['a/b/c'], 459 'srcjar_paths': [], 460 'test_dirs': [], 461 }, 462 'frameworks/base/framework_srcjars': { 463 'dep_paths': ['frameworks/base'], 464 'jar_files': [], 465 'r_java_paths': [], 466 'src_dirs': [], 467 'srcjar_paths': ['x/y.srcjar'], 468 'test_dirs': [], 469 } 470 } 471 self.assertDictEqual(proj.path_to_sources, expected) 472 473 474if __name__ == '__main__': 475 unittest.main() 476