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 31 32_MODULE_INFO = { 33 'm1': { 34 'class': ['JAVA_LIBRARIES'], 35 'dependencies': ['m2', 'm6'], 36 'path': ['m1'] 37 }, 38 'm2': { 39 'class': ['JAVA_LIBRARIES'], 40 'dependencies': ['m3', 'm4'] 41 }, 42 'm3': { 43 'class': ['JAVA_LIBRARIES'], 44 'dependencies': [] 45 }, 46 'm4': { 47 'class': ['JAVA_LIBRARIES'], 48 'dependencies': ['m6'] 49 }, 50 'm5': { 51 'class': ['JAVA_LIBRARIES'], 52 'dependencies': [] 53 }, 54 'm6': { 55 'class': ['JAVA_LIBRARIES'], 56 'dependencies': ['m2'] 57 }, 58} 59_EXPECT_DEPENDENT_MODULES = { 60 'm1': { 61 'class': ['JAVA_LIBRARIES'], 62 'dependencies': ['m2', 'm6'], 63 'path': ['m1'], 64 'depth': 0 65 }, 66 'm2': { 67 'class': ['JAVA_LIBRARIES'], 68 'dependencies': ['m3', 'm4'], 69 'depth': 1 70 }, 71 'm3': { 72 'class': ['JAVA_LIBRARIES'], 73 'dependencies': [], 74 'depth': 2 75 }, 76 'm4': { 77 'class': ['JAVA_LIBRARIES'], 78 'dependencies': ['m6'], 79 'depth': 2 80 }, 81 'm6': { 82 'class': ['JAVA_LIBRARIES'], 83 'dependencies': ['m2'], 84 'depth': 1 85 }, 86} 87 88 89# pylint: disable=protected-access 90# pylint: disable=invalid-name 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 109 @mock.patch('atest.module_info.ModuleInfo') 110 def test_get_dep_modules(self, mock_module_info): 111 """Test get_dep_modules recursively find dependent modules.""" 112 mock_module_info.name_to_module_info = _MODULE_INFO 113 mock_module_info.is_module.return_value = True 114 mock_module_info.get_paths.return_value = ['m1'] 115 mock_module_info.get_module_names.return_value = ['m1'] 116 project_info.ProjectInfo.modules_info = mock_module_info 117 proj_info = project_info.ProjectInfo(self.args.module_name, False) 118 self.assertEqual(proj_info.dep_modules, _EXPECT_DEPENDENT_MODULES) 119 120 @mock.patch.object(project_info.ProjectInfo, 121 '_get_modules_under_project_path') 122 @mock.patch.object(project_info.ProjectInfo, 'get_dep_modules') 123 def test_init(self, mock_get_deps, mock_get_sub_modules): 124 """Test init.""" 125 project_info.ProjectInfo(constant.FRAMEWORK_ALL, False) 126 self.assertTrue(mock_get_deps.called) 127 self.assertFalse(mock_get_sub_modules.called) 128 129 @mock.patch.object(common_util, 'get_android_root_dir') 130 def test_get_target_name(self, mock_get_root): 131 """Test get_target_name with different conditions.""" 132 mock_get_root.return_value = unittest_constants.TEST_DATA_PATH 133 self.assertEqual( 134 project_info.ProjectInfo.get_target_name( 135 unittest_constants.TEST_MODULE, 136 unittest_constants.TEST_DATA_PATH), 137 os.path.basename(unittest_constants.TEST_DATA_PATH)) 138 self.assertEqual( 139 project_info.ProjectInfo.get_target_name( 140 unittest_constants.TEST_MODULE, unittest_constants.TEST_PATH), 141 unittest_constants.TEST_MODULE) 142 143 # pylint: disable=too-many-locals 144 @mock.patch.object(common_util, 'get_android_root_dir') 145 @mock.patch('atest.module_info.ModuleInfo') 146 @mock.patch('atest.atest_utils.build') 147 def test_locate_source(self, mock_atest_utils_build, mock_module_info, 148 mock_get_root): 149 """Test locate_source handling.""" 150 mock_atest_utils_build.build.return_value = True 151 test_root_path = os.path.join(tempfile.mkdtemp(), 'test') 152 shutil.copytree(unittest_constants.TEST_DATA_PATH, test_root_path) 153 mock_get_root.return_value = test_root_path 154 generated_jar = ('out/soong/.intermediates/packages/apps/test/test/' 155 'android_common/generated.jar') 156 locate_module_info = dict(unittest_constants.MODULE_INFO) 157 locate_module_info['installed'] = [generated_jar] 158 mock_module_info.is_module.return_value = True 159 mock_module_info.get_paths.return_value = [ 160 unittest_constants.MODULE_PATH 161 ] 162 mock_module_info.get_module_names.return_value = [ 163 unittest_constants.TEST_MODULE 164 ] 165 project_config.ProjectConfig(self.args) 166 project_info_obj = project_info.ProjectInfo( 167 mock_module_info.get_paths()[0]) 168 project_info_obj.dep_modules = { 169 unittest_constants.TEST_MODULE: locate_module_info 170 } 171 project_info_obj._init_source_path() 172 # Show warning when the jar not exists after build the module. 173 result_jar = set() 174 project_info_obj.locate_source() 175 self.assertEqual(project_info_obj.source_path['jar_path'], result_jar) 176 177 # Test collects source and test folders. 178 result_source = set(['packages/apps/test/src/main/java']) 179 result_test = set(['packages/apps/test/tests']) 180 self.assertEqual(project_info_obj.source_path['source_folder_path'], 181 result_source) 182 self.assertEqual(project_info_obj.source_path['test_folder_path'], 183 result_test) 184 185 @mock.patch.object(project_info, 'batch_build_dependencies') 186 @mock.patch.object(common_util, 'get_android_root_dir') 187 @mock.patch('atest.module_info.ModuleInfo') 188 @mock.patch('atest.atest_utils.build') 189 def test_locate_source_with_skip_build(self, mock_atest_utils_build, 190 mock_module_info, mock_get_root, 191 mock_batch): 192 """Test locate_source handling.""" 193 mock_atest_utils_build.build.return_value = True 194 test_root_path = os.path.join(tempfile.mkdtemp(), 'test') 195 shutil.copytree(unittest_constants.TEST_DATA_PATH, test_root_path) 196 mock_get_root.return_value = test_root_path 197 generated_jar = ('out/soong/.intermediates/packages/apps/test/test/' 198 'android_common/generated.jar') 199 locate_module_info = dict(unittest_constants.MODULE_INFO) 200 locate_module_info['installed'] = [generated_jar] 201 mock_module_info.is_module.return_value = True 202 mock_module_info.get_paths.return_value = [ 203 unittest_constants.MODULE_PATH 204 ] 205 mock_module_info.get_module_names.return_value = [ 206 unittest_constants.TEST_MODULE 207 ] 208 args = mock.MagicMock() 209 args.module_name = 'm1' 210 args.project_path = '' 211 args.ide = ['j'] 212 args.no_launch = True 213 args.depth = 0 214 args.android_tree = False 215 args.skip_build = True 216 args.targets = ['m1'] 217 args.verbose = False 218 args.ide_installed_path = None 219 args.config_reset = False 220 project_config.ProjectConfig(args) 221 project_info_obj = project_info.ProjectInfo( 222 mock_module_info.get_paths()[0]) 223 project_info_obj.dep_modules = { 224 unittest_constants.TEST_MODULE: locate_module_info 225 } 226 project_info_obj._init_source_path() 227 project_info_obj.locate_source() 228 self.assertFalse(mock_batch.called) 229 230 args.ide = ['v'] 231 args.skip_build = False 232 project_config.ProjectConfig(args) 233 project_info_obj = project_info.ProjectInfo( 234 mock_module_info.get_paths()[0]) 235 project_info_obj.dep_modules = { 236 unittest_constants.TEST_MODULE: locate_module_info 237 } 238 project_info_obj._init_source_path() 239 project_info_obj.locate_source() 240 self.assertFalse(mock_batch.called) 241 242 def test_separate_build_target(self): 243 """Test separate_build_target.""" 244 test_list = ['1', '22', '333', '4444', '55555', '1', '7777777'] 245 targets = [] 246 sample = [['1', '22', '333'], ['4444'], ['55555', '1'], ['7777777']] 247 for start, end in iter( 248 project_info._separate_build_targets(test_list, 9)): 249 targets.append(test_list[start:end]) 250 self.assertEqual(targets, sample) 251 252 def test_separate_build_target_with_length_short(self): 253 """Test separate_build_target with length short.""" 254 test_list = ['1'] 255 sample = [['1']] 256 targets = [] 257 for start, end in iter( 258 project_info._separate_build_targets(test_list, 9)): 259 targets.append(test_list[start:end]) 260 self.assertEqual(targets, sample) 261 262 @mock.patch.object(project_info.ProjectInfo, 'locate_source') 263 @mock.patch('atest.module_info.ModuleInfo') 264 def test_rebuild_jar_once(self, mock_module_info, mock_locate_source): 265 """Test rebuild the jar/srcjar only one time.""" 266 mock_module_info.get_paths.return_value = ['m1'] 267 project_info.ProjectInfo.modules_info = mock_module_info 268 proj_info = project_info.ProjectInfo(self.args.module_name, False) 269 proj_info.locate_source(build=False) 270 self.assertEqual(mock_locate_source.call_count, 1) 271 proj_info.locate_source(build=True) 272 self.assertEqual(mock_locate_source.call_count, 2) 273 274 @mock.patch('builtins.print') 275 @mock.patch('builtins.format') 276 @mock.patch('atest.atest_utils.build') 277 def test_build_target(self, mock_build, mock_format, mock_print): 278 """Test _build_target.""" 279 build_argument = ['-k', 'j'] 280 test_targets = ['mod_1', 'mod_2'] 281 build_argument.extend(test_targets) 282 mock_build.return_value = False 283 project_info._build_target(test_targets) 284 self.assertTrue(mock_build.called_with((build_argument, True))) 285 self.assertTrue(mock_format.called_with('\n'.join(test_targets))) 286 self.assertTrue(mock_print.called) 287 mock_print.reset_mock() 288 mock_format.reset_mock() 289 mock_build.reset_mock() 290 291 mock_build.return_value = True 292 project_info._build_target(test_targets) 293 self.assertTrue(mock_build.called_with((build_argument, True))) 294 self.assertFalse(mock_format.called) 295 self.assertFalse(mock_print.called) 296 mock_print.reset_mock() 297 mock_format.reset_mock() 298 mock_build.reset_mock() 299 300 @mock.patch('builtins.print') 301 @mock.patch.object(project_info.ProjectInfo, '_search_android_make_files') 302 @mock.patch('atest.module_info.ModuleInfo') 303 def test_display_convert_make_files_message( 304 self, mock_module_info, mock_search, mock_print): 305 """Test _display_convert_make_files_message with conditions.""" 306 mock_search.return_value = [] 307 mock_module_info.get_paths.return_value = ['m1'] 308 project_info.ProjectInfo.modules_info = mock_module_info 309 proj_info = project_info.ProjectInfo(self.args.module_name) 310 proj_info._display_convert_make_files_message() 311 self.assertFalse(mock_print.called) 312 313 mock_print.mock_reset() 314 mock_search.return_value = ['a/b/path/to/target.mk'] 315 proj_info = project_info.ProjectInfo(self.args.module_name) 316 proj_info._display_convert_make_files_message() 317 self.assertTrue(mock_print.called) 318 319 @mock.patch.object(project_info, '_build_target') 320 @mock.patch.object(project_info, '_separate_build_targets') 321 @mock.patch.object(logging, 'info') 322 def test_batch_build_dependencies(self, mock_log, mock_sep, mock_build): 323 """Test batch_build_dependencies.""" 324 mock_sep.return_value = [(0, 1)] 325 project_info.batch_build_dependencies({'m1', 'm2'}) 326 self.assertTrue(mock_log.called) 327 self.assertTrue(mock_sep.called) 328 self.assertEqual(mock_build.call_count, 1) 329 330 331class MultiProjectsInfoUnittests(unittest.TestCase): 332 """Unit tests for MultiProjectsInfo class.""" 333 334 @mock.patch.object(project_info.ProjectInfo, '__init__') 335 @mock.patch.object(project_info.ProjectInfo, 'get_dep_modules') 336 @mock.patch.object(project_info.ProjectInfo, 337 '_get_robolectric_dep_module') 338 @mock.patch.object(project_info.ProjectInfo, 339 '_get_modules_under_project_path') 340 @mock.patch.object(common_util, 'get_related_paths') 341 def test_collect_all_dep_modules(self, mock_relpath, mock_sub_modules_path, 342 mock_robo_module, mock_get_dep_modules, 343 mock_init): 344 """Test _collect_all_dep_modules.""" 345 mock_init.return_value = None 346 mock_relpath.return_value = ('path/to/sub/module', '') 347 mock_sub_modules_path.return_value = 'sub_module' 348 mock_robo_module.return_value = 'robo_module' 349 expected = set(project_info._CORE_MODULES) 350 expected.update({'sub_module', 'robo_module'}) 351 proj = project_info.MultiProjectsInfo(['a']) 352 proj.collect_all_dep_modules() 353 self.assertTrue(mock_get_dep_modules.called_with(expected)) 354 355 356if __name__ == '__main__': 357 unittest.main() 358