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 source_splitter."""
18
19import os
20import shutil
21import tempfile
22import unittest
23from unittest import mock
24
25from aidegen import unittest_constants
26from aidegen.idea import iml
27from aidegen.lib import common_util
28from aidegen.lib import project_config
29from aidegen.lib import project_info
30from aidegen.project import source_splitter
31
32
33# pylint: disable=protected-access
34class ProjectSplitterUnittest(unittest.TestCase):
35    """Unit tests for ProjectSplitter class."""
36
37    _TEST_DIR = None
38    _TEST_PATH = unittest_constants.TEST_DATA_PATH
39    _SAMPLE_EXCLUDE_FOLDERS = [
40        '\n            <excludeFolder url="file://%s/.idea" />' % _TEST_PATH,
41        '\n            <excludeFolder url="file://%s/out" />' % _TEST_PATH,
42    ]
43
44    def setUp(self):
45        """Prepare the testdata related data."""
46        projects = []
47        targets = ['a', 'b', 'c', 'framework']
48        ProjectSplitterUnittest._TEST_DIR = tempfile.mkdtemp()
49        for i, target in enumerate(targets):
50            with mock.patch.object(project_info, 'ProjectInfo') as proj_info:
51                projects.append(proj_info(target, i == 0))
52        projects[0].project_relative_path = 'src1'
53        projects[0].source_path = {
54            'source_folder_path': {'src1', 'src2', 'other1'},
55            'test_folder_path': {'src1/tests'},
56            'jar_path': {'jar1.jar'},
57            'jar_module_path': dict(),
58            'r_java_path': set(),
59            'srcjar_path': {'srcjar1.srcjar'}
60        }
61        projects[1].project_relative_path = 'src2'
62        projects[1].source_path = {
63            'source_folder_path': {'src2', 'src2/src3', 'src2/lib', 'other2'},
64            'test_folder_path': {'src2/tests'},
65            'jar_path': set(),
66            'jar_module_path': dict(),
67            'r_java_path': set(),
68            'srcjar_path': {'srcjar2.srcjar'}
69        }
70        projects[2].project_relative_path = 'src2/src3'
71        projects[2].source_path = {
72            'source_folder_path': {'src2/src3', 'src2/lib'},
73            'test_folder_path': {'src2/src3/tests'},
74            'jar_path': {'jar3.jar'},
75            'jar_module_path': dict(),
76            'r_java_path': set(),
77            'srcjar_path': {'srcjar3.srcjar'}
78        }
79        projects[3].project_relative_path = 'frameworks/base'
80        projects[3].source_path = {
81            'source_folder_path': set(),
82            'test_folder_path': set(),
83            'jar_path': set(),
84            'jar_module_path': dict(),
85            'r_java_path': set(),
86            'srcjar_path': {'framework.srcjar', 'other.srcjar'}
87        }
88        with mock.patch.object(project_config.ProjectConfig,
89                               'get_instance') as proj_cfg:
90            config = mock.Mock()
91            config.full_repo = False
92            proj_cfg.return_value = config
93            self.split_projs = source_splitter.ProjectSplitter(projects)
94
95    def tearDown(self):
96        """Clear the testdata related path."""
97        self.split_projs = None
98        shutil.rmtree(ProjectSplitterUnittest._TEST_DIR)
99        iml.IMLGenerator.USED_NAME_CACHE.clear()
100
101    @mock.patch.object(common_util, 'get_android_root_dir')
102    @mock.patch.object(project_config.ProjectConfig, 'get_instance')
103    @mock.patch('builtins.any')
104    def test_init(self, mock_any, mock_project, mock_root):
105        """Test initialize the attributes."""
106        self.assertEqual(len(self.split_projs._projects), 4)
107        mock_any.return_value = False
108        mock_root.return_value = ProjectSplitterUnittest._TEST_DIR
109        with mock.patch.object(project_info, 'ProjectInfo') as proj_info:
110            config = mock.Mock()
111            config.full_repo = False
112            mock_project.return_value = config
113            project = source_splitter.ProjectSplitter(proj_info(['a'], True))
114            self.assertFalse(project._framework_exist)
115            config.full_repo = True
116            project = source_splitter.ProjectSplitter(proj_info(['a'], True))
117            self.assertEqual(project._full_repo_iml,
118                             os.path.basename(
119                                 ProjectSplitterUnittest._TEST_DIR))
120
121    @mock.patch.object(source_splitter.ProjectSplitter,
122                       '_remove_duplicate_sources')
123    @mock.patch.object(source_splitter.ProjectSplitter,
124                       '_keep_local_sources')
125    @mock.patch.object(source_splitter.ProjectSplitter,
126                       '_collect_all_srcs')
127    def test_revise_source_folders(self, mock_copy_srcs, mock_keep_srcs,
128                                   mock_remove_srcs):
129        """Test revise_source_folders."""
130        self.split_projs.revise_source_folders()
131        self.assertTrue(mock_copy_srcs.called)
132        self.assertTrue(mock_keep_srcs.called)
133        self.assertTrue(mock_remove_srcs.called)
134
135    def test_collect_all_srcs(self):
136        """Test _collect_all_srcs."""
137        self.split_projs._collect_all_srcs()
138        sources = self.split_projs._all_srcs
139        expected_srcs = {'src1', 'src2', 'src2/src3', 'src2/lib', 'other1',
140                         'other2'}
141        self.assertEqual(sources['source_folder_path'], expected_srcs)
142        expected_tests = {'src1/tests', 'src2/tests', 'src2/src3/tests'}
143        self.assertEqual(sources['test_folder_path'], expected_tests)
144
145    def test_keep_local_sources(self):
146        """Test _keep_local_sources."""
147        self.split_projs._collect_all_srcs()
148        self.split_projs._keep_local_sources()
149        srcs1 = self.split_projs._projects[0].source_path
150        srcs2 = self.split_projs._projects[1].source_path
151        srcs3 = self.split_projs._projects[2].source_path
152        all_srcs = self.split_projs._all_srcs
153        expected_srcs1 = {'src1'}
154        expected_srcs2 = {'src2', 'src2/src3', 'src2/lib'}
155        expected_srcs3 = {'src2/src3'}
156        expected_all_srcs = {'other1', 'other2'}
157        expected_all_tests = set()
158        self.assertEqual(srcs1['source_folder_path'], expected_srcs1)
159        self.assertEqual(srcs2['source_folder_path'], expected_srcs2)
160        self.assertEqual(srcs3['source_folder_path'], expected_srcs3)
161        self.assertEqual(all_srcs['source_folder_path'], expected_all_srcs)
162        self.assertEqual(all_srcs['test_folder_path'], expected_all_tests)
163
164    def test_remove_duplicate_sources(self):
165        """Test _remove_duplicate_sources."""
166        self.split_projs._collect_all_srcs()
167        self.split_projs._keep_local_sources()
168        self.split_projs._remove_duplicate_sources()
169        srcs2 = self.split_projs._projects[1].source_path
170        srcs3 = self.split_projs._projects[2].source_path
171        expected_srcs2 = {'src2', 'src2/lib'}
172        expected_srcs3 = {'src2/src3'}
173        self.assertEqual(srcs2['source_folder_path'], expected_srcs2)
174        self.assertEqual(srcs3['source_folder_path'], expected_srcs3)
175
176    def test_get_dependencies(self):
177        """Test get_dependencies."""
178        iml.IMLGenerator.USED_NAME_CACHE.clear()
179        self.split_projs.get_dependencies()
180        dep1 = ['framework_srcjars', 'base', 'src2', 'dependencies']
181        dep2 = ['framework_srcjars', 'base', 'dependencies']
182        dep3 = ['framework_srcjars', 'base', 'src2', 'dependencies']
183        self.assertEqual(self.split_projs._projects[0].dependencies, dep1)
184        self.assertEqual(self.split_projs._projects[1].dependencies, dep2)
185        self.assertEqual(self.split_projs._projects[2].dependencies, dep3)
186
187    @mock.patch.object(common_util, 'get_android_root_dir')
188    def test_gen_framework_srcjars_iml(self, mock_root):
189        """Test gen_framework_srcjars_iml."""
190        mock_root.return_value = self._TEST_DIR
191        self.split_projs._projects[0].dep_modules = {
192            'framework-all': {
193                'module_name': 'framework-all',
194                'path': ['frameworks/base'],
195                'srcjars': ['framework.srcjar'],
196                'iml_name': 'framework_srcjars'
197            }
198        }
199        self.split_projs._framework_exist = False
200        self.split_projs.gen_framework_srcjars_iml()
201        expected_srcjars = [
202            'other.srcjar',
203            'srcjar1.srcjar',
204            'srcjar2.srcjar',
205            'srcjar3.srcjar',
206        ]
207        expected_path = os.path.join(self._TEST_DIR,
208                                     'frameworks/base/framework_srcjars.iml')
209        self.split_projs._framework_exist = True
210        self.split_projs.revise_source_folders()
211        iml_path = self.split_projs.gen_framework_srcjars_iml()
212        srcjars = self.split_projs._all_srcs['srcjar_path']
213        self.assertEqual(sorted(list(srcjars)), expected_srcjars)
214        self.assertEqual(iml_path, expected_path)
215
216    @mock.patch.object(iml.IMLGenerator, 'create')
217    @mock.patch.object(common_util, 'get_android_root_dir')
218    def test_gen_dependencies_iml(self, mock_root, mock_create_iml):
219        """Test _gen_dependencies_iml."""
220        mock_root.return_value = self._TEST_DIR
221        self.split_projs.revise_source_folders()
222        self.split_projs._framework_exist = False
223        self.split_projs._gen_dependencies_iml()
224        self.split_projs._framework_exist = True
225        self.split_projs._gen_dependencies_iml()
226        self.assertTrue(mock_create_iml.called)
227
228    @mock.patch.object(source_splitter, 'get_exclude_content')
229    @mock.patch.object(project_config.ProjectConfig, 'get_instance')
230    @mock.patch.object(iml.IMLGenerator, 'create')
231    @mock.patch.object(common_util, 'get_android_root_dir')
232    def test_gen_projects_iml(self, mock_root, mock_create_iml, mock_project,
233                              mock_get_excludes):
234        """Test gen_projects_iml."""
235        mock_root.return_value = self._TEST_DIR
236        config = mock.Mock()
237        mock_project.return_value = config
238        config.exclude_paths = []
239        self.split_projs.revise_source_folders()
240        self.split_projs.gen_projects_iml()
241        self.assertTrue(mock_create_iml.called)
242        self.assertFalse(mock_get_excludes.called)
243        config.exclude_paths = ['a']
244        self.split_projs.gen_projects_iml()
245        self.assertTrue(mock_get_excludes.called)
246
247    def test_get_exclude_content(self):
248        """Test get_exclude_content."""
249        exclude_folders = source_splitter.get_exclude_content(self._TEST_PATH)
250        self.assertEqual(self._SAMPLE_EXCLUDE_FOLDERS, exclude_folders)
251
252
253if __name__ == '__main__':
254    unittest.main()
255