1#!/usr/bin/env python3 2# 3# Copyright 2020 - 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_splitter.""" 18 19import os 20import shutil 21import tempfile 22import unittest 23from unittest import mock 24 25from aidegen import constant 26from aidegen import unittest_constants 27from aidegen.idea import iml 28from aidegen.lib import common_util 29from aidegen.lib import project_config 30from aidegen.lib import project_info 31from aidegen.project import project_splitter 32 33 34# pylint: disable=protected-access 35class ProjectSplitterUnittest(unittest.TestCase): 36 """Unit tests for ProjectSplitter class.""" 37 38 _TEST_DIR = None 39 _TEST_PATH = unittest_constants.TEST_DATA_PATH 40 _SAMPLE_EXCLUDE_FOLDERS = [ 41 '\n <excludeFolder url="file://%s/.idea" />' % _TEST_PATH, 42 '\n <excludeFolder url="file://%s/out" />' % _TEST_PATH, 43 ] 44 45 def setUp(self): 46 """Prepare the testdata related data.""" 47 projects = [] 48 targets = ['a', 'b', 'c', 'framework'] 49 ProjectSplitterUnittest._TEST_DIR = tempfile.mkdtemp() 50 for i, target in enumerate(targets): 51 with mock.patch.object(project_info, 'ProjectInfo') as proj_info: 52 projects.append(proj_info(target, i == 0)) 53 projects[0].project_relative_path = 'src1' 54 projects[0].source_path = { 55 'source_folder_path': {'src1', 'src2', 'other1'}, 56 'test_folder_path': {'src1/tests'}, 57 'jar_path': {'jar1.jar'}, 58 'jar_module_path': dict(), 59 'r_java_path': set(), 60 'srcjar_path': {'srcjar1.srcjar'} 61 } 62 projects[1].project_relative_path = 'src2' 63 projects[1].source_path = { 64 'source_folder_path': {'src2', 'src2/src3', 'src2/lib', 'other2'}, 65 'test_folder_path': {'src2/tests'}, 66 'jar_path': set(), 67 'jar_module_path': dict(), 68 'r_java_path': set(), 69 'srcjar_path': {'srcjar2.srcjar'} 70 } 71 projects[2].project_relative_path = 'src2/src3' 72 projects[2].source_path = { 73 'source_folder_path': {'src2/src3', 'src2/lib'}, 74 'test_folder_path': {'src2/src3/tests'}, 75 'jar_path': {'jar3.jar'}, 76 'jar_module_path': dict(), 77 'r_java_path': set(), 78 'srcjar_path': {'srcjar3.srcjar'} 79 } 80 projects[3].project_relative_path = 'frameworks/base' 81 projects[3].source_path = { 82 'source_folder_path': set(), 83 'test_folder_path': set(), 84 'jar_path': set(), 85 'jar_module_path': dict(), 86 'r_java_path': set(), 87 'srcjar_path': {'framework.srcjar', 'other.srcjar'} 88 } 89 with mock.patch.object(project_config.ProjectConfig, 90 'get_instance') as proj_cfg: 91 config = mock.Mock() 92 config.full_repo = False 93 proj_cfg.return_value = config 94 self.split_projs = project_splitter.ProjectSplitter(projects) 95 96 def tearDown(self): 97 """Clear the testdata related path.""" 98 self.split_projs = None 99 shutil.rmtree(ProjectSplitterUnittest._TEST_DIR) 100 iml.IMLGenerator.USED_NAME_CACHE.clear() 101 102 @mock.patch.object(common_util, 'get_android_root_dir') 103 @mock.patch.object(project_config.ProjectConfig, 'get_instance') 104 @mock.patch('builtins.any') 105 def test_init(self, mock_any, mock_project, mock_root): 106 """Test initialize the attributes.""" 107 self.assertEqual(len(self.split_projs._projects), 4) 108 mock_any.return_value = False 109 mock_root.return_value = ProjectSplitterUnittest._TEST_DIR 110 with mock.patch.object(project_info, 'ProjectInfo') as proj_info: 111 config = mock.Mock() 112 config.full_repo = False 113 mock_project.return_value = config 114 project = project_splitter.ProjectSplitter(proj_info(['a'], True)) 115 self.assertFalse(project._framework_exist) 116 config.full_repo = True 117 project = project_splitter.ProjectSplitter(proj_info(['a'], True)) 118 self.assertEqual(project._full_repo_iml, 119 os.path.basename( 120 ProjectSplitterUnittest._TEST_DIR)) 121 122 @mock.patch.object(project_splitter.ProjectSplitter, 123 '_remove_duplicate_sources') 124 @mock.patch.object(project_splitter.ProjectSplitter, 125 '_keep_local_sources') 126 @mock.patch.object(project_splitter.ProjectSplitter, 127 '_collect_all_srcs') 128 def test_revise_source_folders(self, mock_copy_srcs, mock_keep_srcs, 129 mock_remove_srcs): 130 """Test revise_source_folders.""" 131 self.split_projs.revise_source_folders() 132 self.assertTrue(mock_copy_srcs.called) 133 self.assertTrue(mock_keep_srcs.called) 134 self.assertTrue(mock_remove_srcs.called) 135 136 def test_collect_all_srcs(self): 137 """Test _collect_all_srcs.""" 138 self.split_projs._collect_all_srcs() 139 sources = self.split_projs._all_srcs 140 expected_srcs = {'src1', 'src2', 'src2/src3', 'src2/lib', 'other1', 141 'other2'} 142 self.assertEqual(sources['source_folder_path'], expected_srcs) 143 expected_tests = {'src1/tests', 'src2/tests', 'src2/src3/tests'} 144 self.assertEqual(sources['test_folder_path'], expected_tests) 145 146 def test_keep_local_sources(self): 147 """Test _keep_local_sources.""" 148 self.split_projs._collect_all_srcs() 149 self.split_projs._keep_local_sources() 150 srcs1 = self.split_projs._projects[0].source_path 151 srcs2 = self.split_projs._projects[1].source_path 152 srcs3 = self.split_projs._projects[2].source_path 153 all_srcs = self.split_projs._all_srcs 154 expected_srcs1 = {'src1'} 155 expected_srcs2 = {'src2', 'src2/src3', 'src2/lib'} 156 expected_srcs3 = {'src2/src3'} 157 expected_all_srcs = {'other1', 'other2'} 158 expected_all_tests = set() 159 self.assertEqual(srcs1['source_folder_path'], expected_srcs1) 160 self.assertEqual(srcs2['source_folder_path'], expected_srcs2) 161 self.assertEqual(srcs3['source_folder_path'], expected_srcs3) 162 self.assertEqual(all_srcs['source_folder_path'], expected_all_srcs) 163 self.assertEqual(all_srcs['test_folder_path'], expected_all_tests) 164 165 @mock.patch.object( 166 project_splitter, '_remove_child_duplicate_sources_from_parent') 167 def test_remove_duplicate_sources(self, mock_remove): 168 """Test _remove_duplicate_sources.""" 169 self.split_projs._collect_all_srcs() 170 self.split_projs._keep_local_sources() 171 mock_remove.return_value = set() 172 self.split_projs._remove_duplicate_sources() 173 srcs2 = self.split_projs._projects[1].source_path 174 srcs3 = self.split_projs._projects[2].source_path 175 expected_srcs2 = {'src2', 'src2/lib'} 176 expected_srcs3 = {'src2/src3'} 177 self.assertEqual(srcs2['source_folder_path'], expected_srcs2) 178 self.assertEqual(srcs3['source_folder_path'], expected_srcs3) 179 self.assertTrue(mock_remove.called) 180 181 def test_get_dependencies(self): 182 """Test get_dependencies.""" 183 iml.IMLGenerator.USED_NAME_CACHE.clear() 184 self.split_projs.get_dependencies() 185 dep1 = ['framework_srcjars', 'base', 'src2', 'dependencies'] 186 dep2 = ['framework_srcjars', 'base', 'dependencies'] 187 dep3 = ['framework_srcjars', 'base', 'src2', 'dependencies'] 188 self.assertEqual(self.split_projs._projects[0].dependencies, dep1) 189 self.assertEqual(self.split_projs._projects[1].dependencies, dep2) 190 self.assertEqual(self.split_projs._projects[2].dependencies, dep3) 191 192 @mock.patch.object(iml.IMLGenerator, 'create') 193 @mock.patch.object(project_splitter.ProjectSplitter, 194 '_get_permission_defined_source_path') 195 @mock.patch.object(project_splitter.ProjectSplitter, 196 '_remove_permission_definition_srcjar_path') 197 @mock.patch.object(common_util, 'get_android_root_dir') 198 def test_gen_framework_srcjars_iml( 199 self, mock_root, mock_remove, mock_get, mock_create_iml): 200 """Test gen_framework_srcjars_iml.""" 201 mock_root.return_value = self._TEST_DIR 202 mock_get.return_value = 'aapt2/R' 203 self.split_projs._projects[0].dep_modules = { 204 'framework-all': { 205 'module_name': 'framework-all', 206 'path': ['frameworks/base'], 207 'srcjars': ['framework.srcjar'], 208 'iml_name': 'framework_srcjars' 209 } 210 } 211 self.split_projs._framework_exist = False 212 self.split_projs.gen_framework_srcjars_iml() 213 srcjar_dict = {constant.KEY_DEP_SRCS: True, constant.KEY_SRCJARS: True, 214 constant.KEY_DEPENDENCIES: True} 215 mock_create_iml.assert_called_with(srcjar_dict) 216 expected_srcjars = [ 217 'other.srcjar', 218 'srcjar1.srcjar', 219 'srcjar2.srcjar', 220 'srcjar3.srcjar', 221 ] 222 expected_path = os.path.join(self._TEST_DIR, 223 'frameworks/base/framework_srcjars.iml') 224 self.split_projs._framework_exist = True 225 self.split_projs.revise_source_folders() 226 mock_get.return_value = None 227 iml_path = self.split_projs.gen_framework_srcjars_iml() 228 srcjars = self.split_projs._all_srcs['srcjar_path'] 229 self.assertEqual(sorted(list(srcjars)), expected_srcjars) 230 self.assertEqual(iml_path, expected_path) 231 self.assertTrue(mock_remove.called) 232 srcjar_dict = {constant.KEY_SRCJARS: True, 233 constant.KEY_DEPENDENCIES: True} 234 mock_create_iml.assert_called_with(srcjar_dict) 235 236 @mock.patch.object(project_splitter.ProjectSplitter, '_unzip_all_scrjars') 237 @mock.patch.object(iml.IMLGenerator, 'create') 238 @mock.patch.object(common_util, 'get_android_root_dir') 239 def test_gen_dependencies_iml(self, mock_root, mock_create_iml, mock_unzip): 240 """Test _gen_dependencies_iml.""" 241 mock_root.return_value = self._TEST_DIR 242 self.split_projs.revise_source_folders() 243 self.split_projs._framework_exist = False 244 self.split_projs._gen_dependencies_iml() 245 self.assertTrue(mock_unzip.called) 246 mock_unzip.mock_reset() 247 self.split_projs._framework_exist = True 248 self.split_projs._gen_dependencies_iml() 249 self.assertTrue(mock_create_iml.called) 250 self.assertTrue(mock_unzip.called) 251 252 @mock.patch.object(project_splitter.ProjectSplitter, '_unzip_all_scrjars') 253 @mock.patch.object(project_splitter, 'get_exclude_content') 254 @mock.patch.object(project_config.ProjectConfig, 'get_instance') 255 @mock.patch.object(iml.IMLGenerator, 'create') 256 @mock.patch.object(common_util, 'get_android_root_dir') 257 def test_gen_projects_iml(self, mock_root, mock_create_iml, mock_project, 258 mock_get_excludes, mock_unzip): 259 """Test gen_projects_iml.""" 260 mock_root.return_value = self._TEST_DIR 261 config = mock.Mock() 262 mock_project.return_value = config 263 config.exclude_paths = [] 264 self.split_projs.revise_source_folders() 265 self.split_projs.gen_projects_iml() 266 self.assertTrue(mock_create_iml.called) 267 self.assertTrue(mock_unzip.called) 268 mock_unzip.mock_reset() 269 self.assertFalse(mock_get_excludes.called) 270 config.exclude_paths = ['a'] 271 self.split_projs.gen_projects_iml() 272 self.assertTrue(mock_get_excludes.called) 273 self.assertTrue(mock_unzip.called) 274 275 def test_get_exclude_content(self): 276 """Test get_exclude_content.""" 277 exclude_folders = project_splitter.get_exclude_content(self._TEST_PATH) 278 self.assertEqual(self._SAMPLE_EXCLUDE_FOLDERS, exclude_folders) 279 280 def test_remove_child_duplicate_sources_from_parent(self): 281 """Test _remove_child_duplicate_sources_from_parent with conditions.""" 282 child = mock.Mock() 283 child.project_relative_path = 'c/d' 284 root = 'a/b' 285 parent_sources = ['a/b/d/e', 'a/b/e/f'] 286 result = project_splitter._remove_child_duplicate_sources_from_parent( 287 child, parent_sources, root) 288 self.assertEqual(set(), result) 289 parent_sources = ['a/b/c/d/e', 'a/b/e/f'] 290 result = project_splitter._remove_child_duplicate_sources_from_parent( 291 child, parent_sources, root) 292 self.assertEqual(set(['a/b/c/d/e']), result) 293 294 @mock.patch('os.path.relpath') 295 def test_get_rel_project_soong_paths(self, mock_rel): 296 """Test _get_rel_project_soong_paths.""" 297 mock_rel.return_value = 'out/soong' 298 expected = [ 299 'out/soong/.intermediates/src1/', 300 'out/soong/.intermediates/src2/', 301 'out/soong/.intermediates/src2/src3/', 302 'out/soong/.intermediates/frameworks/base/' 303 ] 304 self.assertEqual( 305 expected, self.split_projs._get_rel_project_soong_paths()) 306 307 def test_get_real_dependencies_jars(self): 308 """Test _get_real_dependencies_jars with conditions.""" 309 expected = ['a/b/c/d'] 310 self.assertEqual(expected, project_splitter._get_real_dependencies_jars( 311 [], expected)) 312 expected = ['a/b/c/d.jar'] 313 self.assertEqual(expected, project_splitter._get_real_dependencies_jars( 314 ['a/e'], expected)) 315 expected = ['a/b/c/d.jar'] 316 self.assertEqual([], project_splitter._get_real_dependencies_jars( 317 ['a/b'], expected)) 318 expected = ['a/b/c/d.srcjar'] 319 self.assertEqual(expected, project_splitter._get_real_dependencies_jars( 320 ['a/b'], expected)) 321 expected = ['a/b/c/gen'] 322 self.assertEqual(expected, project_splitter._get_real_dependencies_jars( 323 ['a/b'], expected)) 324 325 @mock.patch.object(common_util, 'get_android_root_dir') 326 @mock.patch.object(common_util, 'get_soong_out_path') 327 def test_get_permission_aapt2_rel_path(self, mock_soong, mock_root): 328 """Test _get_permission_aapt2_rel_path.""" 329 mock_soong.return_value = 'a/b/out/soong' 330 mock_root.return_value = 'a/b' 331 expected = ('out/soong/.intermediates/frameworks/base/core/res/' 332 'framework-res/android_common/gen/aapt2/R') 333 self.assertEqual( 334 expected, project_splitter._get_permission_aapt2_rel_path()) 335 336 @mock.patch.object(common_util, 'get_android_root_dir') 337 @mock.patch.object(common_util, 'get_soong_out_path') 338 def test_get_permission_r_srcjar_rel_path(self, mock_soong, mock_root): 339 """Test _get_permission_r_srcjar_rel_path.""" 340 mock_soong.return_value = 'a/b/out/soong' 341 mock_root.return_value = 'a/b' 342 expected = ('out/soong/.intermediates/frameworks/base/core/res/' 343 'framework-res/android_common/gen/android/R.srcjar') 344 self.assertEqual( 345 expected, project_splitter._get_permission_r_srcjar_rel_path()) 346 347 @mock.patch.object(project_splitter, '_get_permission_r_srcjar_rel_path') 348 @mock.patch.object(project_splitter, '_get_permission_aapt2_rel_path') 349 def test_remove_permission_definition_srcjar_path( 350 self, mock_get_aapt2, mock_get_r_srcjar): 351 """Test _remove_permission_definition_srcjar_path with conditions.""" 352 expected_srcjars = [ 353 'other.srcjar', 354 'srcjar1.srcjar', 355 'srcjar2.srcjar', 356 'srcjar3.srcjar', 357 ] 358 mock_get_aapt2.return_value = 'none/aapt2/R' 359 mock_get_r_srcjar.return_value = 'none.srcjar' 360 self.split_projs._all_srcs['srcjar_path'] = expected_srcjars 361 self.split_projs._remove_permission_definition_srcjar_path() 362 srcjars = self.split_projs._all_srcs['srcjar_path'] 363 self.assertEqual(sorted(list(srcjars)), expected_srcjars) 364 365 expected_srcjars = [ 366 'other.srcjar', 367 'srcjar2.srcjar', 368 'srcjar3.srcjar', 369 ] 370 mock_get_r_srcjar.return_value = 'srcjar1.srcjar' 371 self.split_projs._all_srcs['srcjar_path'] = expected_srcjars 372 self.split_projs._remove_permission_definition_srcjar_path() 373 srcjars = self.split_projs._all_srcs['srcjar_path'] 374 self.assertEqual(sorted(list(srcjars)), expected_srcjars) 375 376 @mock.patch('os.path.join') 377 @mock.patch.object(common_util, 'unzip_file') 378 @mock.patch('shutil.rmtree') 379 @mock.patch('os.path.isfile') 380 @mock.patch('os.path.isdir') 381 def test_get_permission_defined_source_path( 382 self, mock_is_dir, mock_is_file, mock_rmtree, mock_unzip, mock_join): 383 """Test _get_permission_defined_source_path function.""" 384 mock_is_dir.return_value = True 385 self.split_projs._get_permission_defined_source_path() 386 self.assertFalse(mock_is_file.called) 387 self.assertFalse(mock_join.called) 388 self.assertFalse(mock_rmtree.called) 389 self.assertFalse(mock_unzip.called) 390 mock_is_dir.return_value = False 391 self.split_projs._get_permission_defined_source_path() 392 self.assertTrue(mock_is_file.called) 393 self.assertTrue(mock_join.called) 394 self.assertFalse(mock_rmtree.called) 395 self.assertTrue(mock_unzip.called) 396 397 @mock.patch.object(common_util, 'unzip_file') 398 @mock.patch('shutil.rmtree') 399 @mock.patch('os.path.join') 400 @mock.patch('os.path.dirname') 401 @mock.patch('os.path.isdir') 402 def test_unzip_all_scrjars( 403 self, mock_is_dir, mock_dirname, mock_join, mock_rmtree, mock_unzip): 404 """Test _unzip_all_scrjars function.""" 405 mock_is_dir.return_value = True 406 self.split_projs._unzip_all_scrjars() 407 self.assertFalse(mock_dirname.called) 408 self.assertFalse(mock_join.called) 409 self.assertFalse(mock_rmtree.called) 410 self.assertFalse(mock_unzip.called) 411 412 413if __name__ == '__main__': 414 unittest.main() 415