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