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.object(project_info, '_build_target') 306 @mock.patch.object(project_info, '_separate_build_targets') 307 @mock.patch.object(logging, 'info') 308 def test_batch_build_dependencies(self, mock_log, mock_sep, mock_build): 309 """Test batch_build_dependencies.""" 310 mock_sep.return_value = [(0, 1)] 311 project_info.batch_build_dependencies({'m1', 'm2'}) 312 self.assertTrue(mock_log.called) 313 self.assertTrue(mock_sep.called) 314 self.assertEqual(mock_build.call_count, 1) 315 316 @mock.patch('os.path.relpath') 317 def test_get_rel_project_out_soong_jar_path(self, mock_rel): 318 """Test _get_rel_project_out_soong_jar_path.""" 319 out_dir = 'a/b/out/soong' 320 mock_rel.return_value = out_dir 321 proj_info = project_info.ProjectInfo(self.args.module_name, False) 322 expected = os.sep.join( 323 [out_dir, constant.INTERMEDIATES, 'm1']) + os.sep 324 self.assertEqual( 325 expected, proj_info._get_rel_project_out_soong_jar_path()) 326 327 def test_update_iml_dep_modules(self): 328 """Test _update_iml_dep_modules with conditions.""" 329 project1 = mock.Mock() 330 project1.source_path = { 331 'source_folder_path': [], 'test_folder_path': [], 'r_java_path': [], 332 'srcjar_path': [], 'jar_path': [] 333 } 334 project1.dependencies = [] 335 project2 = mock.Mock() 336 project2.iml_name = 'm2' 337 project2.rel_out_soong_jar_path = 'out/soong/.intermediates/m2' 338 project_info.ProjectInfo.projects = [project1, project2] 339 project_info._update_iml_dep_modules(project1) 340 self.assertEqual([], project1.dependencies) 341 project1.source_path = { 342 'source_folder_path': [], 'test_folder_path': [], 'r_java_path': [], 343 'srcjar_path': [], 344 'jar_path': ['out/soong/.intermediates/m2/a/b/any.jar'] 345 } 346 project_info._update_iml_dep_modules(project1) 347 self.assertEqual(['m2'], project1.dependencies) 348 349 350class MultiProjectsInfoUnittests(unittest.TestCase): 351 """Unit tests for MultiProjectsInfo class.""" 352 353 @mock.patch.object(project_info.ProjectInfo, '__init__') 354 @mock.patch.object(project_info.ProjectInfo, 'get_dep_modules') 355 @mock.patch.object(project_info.ProjectInfo, 356 '_get_robolectric_dep_module') 357 @mock.patch.object(project_info.ProjectInfo, 358 '_get_modules_under_project_path') 359 @mock.patch.object(common_util, 'get_related_paths') 360 def test_collect_all_dep_modules(self, mock_relpath, mock_sub_modules_path, 361 mock_robo_module, mock_get_dep_modules, 362 mock_init): 363 """Test _collect_all_dep_modules.""" 364 mock_init.return_value = None 365 mock_relpath.return_value = ('path/to/sub/module', '') 366 mock_sub_modules_path.return_value = 'sub_module' 367 mock_robo_module.return_value = 'robo_module' 368 expected = set(project_info._CORE_MODULES) 369 expected.update({'sub_module', 'robo_module'}) 370 proj = project_info.MultiProjectsInfo(['a']) 371 proj.project_module_names = set('framework-all') 372 proj.collect_all_dep_modules() 373 self.assertTrue(mock_get_dep_modules.called_with(expected)) 374 375 @mock.patch.object(logging, 'debug') 376 @mock.patch.object(source_locator, 'ModuleData') 377 @mock.patch.object(project_info.ProjectInfo, '__init__') 378 def test_gen_folder_base_dependencies(self, mock_init, mock_module_data, 379 mock_log): 380 """Test _gen_folder_base_dependencies.""" 381 mock_init.return_value = None 382 proj = project_info.MultiProjectsInfo(['a']) 383 module = mock.Mock() 384 mock_module_data.return_value = module 385 mock_module_data.module_path = '' 386 proj.gen_folder_base_dependencies(mock_module_data) 387 self.assertTrue(mock_log.called) 388 mock_module_data.module_path = 'a/b' 389 mock_module_data.src_dirs = ['a/b/c'] 390 mock_module_data.test_dirs = [] 391 mock_module_data.r_java_paths = [] 392 mock_module_data.srcjar_paths = [] 393 mock_module_data.jar_files = [] 394 mock_module_data.dep_paths = [] 395 proj.gen_folder_base_dependencies(mock_module_data) 396 expected = { 397 'a/b': { 398 'src_dirs': ['a/b/c'], 399 'test_dirs': [], 400 'r_java_paths': [], 401 'srcjar_paths': [], 402 'jar_files': [], 403 'dep_paths': [], 404 } 405 } 406 self.assertEqual(proj.path_to_sources, expected) 407 mock_module_data.srcjar_paths = ['x/y.srcjar'] 408 proj.gen_folder_base_dependencies(mock_module_data) 409 expected = { 410 'a/b': { 411 'src_dirs': ['a/b/c'], 412 'test_dirs': [], 413 'r_java_paths': [], 414 'srcjar_paths': ['x/y.srcjar'], 415 'jar_files': [], 416 'dep_paths': [], 417 } 418 } 419 self.assertEqual(proj.path_to_sources, expected) 420 421 @mock.patch.object(source_locator, 'ModuleData') 422 @mock.patch.object(project_info.ProjectInfo, '__init__') 423 def test_add_framework_base_path(self, mock_init, mock_module_data): 424 """Test _gen_folder_base_dependencies.""" 425 mock_init.return_value = None 426 proj = project_info.MultiProjectsInfo(['a']) 427 module = mock.Mock() 428 mock_module_data.return_value = module 429 mock_module_data.module_path = 'frameworks/base' 430 mock_module_data.module_name = 'framework-other' 431 mock_module_data.src_dirs = ['a/b/c'] 432 mock_module_data.test_dirs = [] 433 mock_module_data.r_java_paths = [] 434 mock_module_data.srcjar_paths = ['x/y.srcjar'] 435 mock_module_data.jar_files = [] 436 mock_module_data.dep_paths = [] 437 proj.gen_folder_base_dependencies(mock_module_data) 438 expected = { 439 'frameworks/base': { 440 'dep_paths': [], 441 'jar_files': [], 442 'r_java_paths': [], 443 'src_dirs': ['a/b/c'], 444 'srcjar_paths': [], 445 'test_dirs': [], 446 } 447 } 448 self.assertDictEqual(proj.path_to_sources, expected) 449 450 @mock.patch.object(source_locator, 'ModuleData') 451 @mock.patch.object(project_info.ProjectInfo, '__init__') 452 def test_add_framework_srcjar_path(self, mock_init, mock_module_data): 453 """Test _gen_folder_base_dependencies.""" 454 mock_init.return_value = None 455 proj = project_info.MultiProjectsInfo(['a']) 456 module = mock.Mock() 457 mock_module_data.return_value = module 458 mock_module_data.module_path = 'frameworks/base' 459 mock_module_data.module_name = 'framework-all' 460 mock_module_data.src_dirs = ['a/b/c'] 461 mock_module_data.test_dirs = [] 462 mock_module_data.r_java_paths = [] 463 mock_module_data.srcjar_paths = ['x/y.srcjar'] 464 mock_module_data.jar_files = [] 465 mock_module_data.dep_paths = [] 466 proj.gen_folder_base_dependencies(mock_module_data) 467 expected = { 468 'frameworks/base': { 469 'dep_paths': [], 470 'jar_files': [], 471 'r_java_paths': [], 472 'src_dirs': ['a/b/c'], 473 'srcjar_paths': [], 474 'test_dirs': [], 475 }, 476 'frameworks/base/framework_srcjars': { 477 'dep_paths': ['frameworks/base'], 478 'jar_files': [], 479 'r_java_paths': [], 480 'src_dirs': [], 481 'srcjar_paths': ['x/y.srcjar'], 482 'test_dirs': [], 483 } 484 } 485 self.assertDictEqual(proj.path_to_sources, expected) 486 487 488if __name__ == '__main__': 489 unittest.main() 490