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