• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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