1#!/usr/bin/env python3
2#
3# Copyright 2019, 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 aidegen_main."""
18
19from __future__ import print_function
20
21import os
22import sys
23import unittest
24from unittest import mock
25
26from aidegen import aidegen_main
27from aidegen import constant
28from aidegen import unittest_constants
29from aidegen.lib import aidegen_metrics
30from aidegen.lib import common_util
31from aidegen.lib import eclipse_project_file_gen
32from aidegen.lib import errors
33from aidegen.lib import ide_util
34from aidegen.lib import module_info
35from aidegen.lib import native_util
36from aidegen.lib import native_module_info
37from aidegen.lib import native_project_info
38from aidegen.lib import project_config
39from aidegen.lib import project_file_gen
40from aidegen.lib import project_info
41from aidegen.vscode import vscode_workspace_file_gen
42
43
44# pylint: disable=protected-access
45# pylint: disable=invalid-name
46# pylint: disable=too-many-arguments
47# pylint: disable=too-many-function-args
48# pylint: disable=too-many-statements
49class AidegenMainUnittests(unittest.TestCase):
50    """Unit tests for aidegen_main.py"""
51
52    def _init_project_config(self, args):
53        """Initialize project configurations."""
54        self.assertIsNotNone(args)
55        config = project_config.ProjectConfig(args)
56        config.init_environment()
57
58    def test_parse_args(self):
59        """Test _parse_args with different conditions."""
60        args = aidegen_main._parse_args([])
61        self.assertEqual(args.targets, [''])
62        self.assertEqual(args.ide[0], 'j')
63        target = 'tradefed'
64        args = aidegen_main._parse_args([target])
65        self.assertEqual(args.targets, [target])
66        depth = '2'
67        args = aidegen_main._parse_args(['-d', depth])
68        self.assertEqual(args.depth, int(depth))
69        args = aidegen_main._parse_args(['-v'])
70        self.assertTrue(args.verbose)
71        args = aidegen_main._parse_args(['-v'])
72        self.assertTrue(args.verbose)
73        args = aidegen_main._parse_args(['-i', 's'])
74        self.assertEqual(args.ide[0], 's')
75        args = aidegen_main._parse_args(['-i', 'e'])
76        self.assertEqual(args.ide[0], 'e')
77        args = aidegen_main._parse_args(['-p', unittest_constants.TEST_MODULE])
78        self.assertEqual(args.ide_installed_path,
79                         unittest_constants.TEST_MODULE)
80        args = aidegen_main._parse_args(['-n'])
81        self.assertTrue(args.no_launch)
82        args = aidegen_main._parse_args(['-r'])
83        self.assertTrue(args.config_reset)
84        args = aidegen_main._parse_args(['-s'])
85        self.assertTrue(args.skip_build)
86        self.assertEqual(args.exclude_paths, None)
87        excludes = 'path/to/a', 'path/to/b'
88        args = aidegen_main._parse_args(['-e', excludes])
89        self.assertEqual(args.exclude_paths, [excludes])
90
91    @mock.patch.object(project_config.ProjectConfig, 'init_environment')
92    @mock.patch.object(project_config, 'ProjectConfig')
93    @mock.patch.object(project_file_gen.ProjectFileGenerator,
94                       'generate_ide_project_files')
95    @mock.patch.object(eclipse_project_file_gen.EclipseConf,
96                       'generate_ide_project_files')
97    def test_generate_project_files(self, mock_eclipse, mock_ide, mock_config,
98                                    mock_init):
99        """Test _generate_project_files with different conditions."""
100        projects = ['module_a', 'module_v']
101        args = aidegen_main._parse_args([projects, '-i', 'e'])
102        mock_init.return_value = None
103        self._init_project_config(args)
104        mock_config.ide_name = constant.IDE_ECLIPSE
105        aidegen_main._generate_project_files(projects)
106        self.assertTrue(mock_eclipse.called_with(projects))
107        mock_config.ide_name = constant.IDE_ANDROID_STUDIO
108        aidegen_main._generate_project_files(projects)
109        self.assertTrue(mock_ide.called_with(projects))
110        mock_config.ide_name = constant.IDE_INTELLIJ
111        aidegen_main._generate_project_files(projects)
112        self.assertTrue(mock_ide.called_with(projects))
113
114    @mock.patch.object(aidegen_main, 'main_with_message')
115    @mock.patch.object(aidegen_main, 'main_without_message')
116    def test_main(self, mock_without, mock_with):
117        """Test main with conditions."""
118        aidegen_main.main(['-s'])
119        self.assertEqual(mock_without.call_count, 1)
120        aidegen_main.main([''])
121        self.assertEqual(mock_with.call_count, 1)
122
123    @mock.patch.object(aidegen_metrics, 'starts_asuite_metrics')
124    @mock.patch.object(project_config, 'is_whole_android_tree')
125    @mock.patch.object(aidegen_metrics, 'ends_asuite_metrics')
126    @mock.patch.object(aidegen_main, 'main_with_message')
127    def test_main_with_normal(self, mock_main, mock_ends_metrics,
128                              mock_is_whole_tree, mock_starts_metrics):
129        """Test main with normal conditions."""
130        aidegen_main.main(['-h'])
131        self.assertFalse(mock_main.called)
132        mock_is_whole_tree.return_value = True
133        aidegen_main.main([''])
134        mock_starts_metrics.assert_called_with([constant.ANDROID_TREE])
135        self.assertFalse(mock_ends_metrics.called)
136        aidegen_main.main(['-n'])
137        mock_ends_metrics.assert_called_with(constant.EXIT_CODE_NORMAL)
138
139    @mock.patch.object(aidegen_metrics, 'ends_asuite_metrics')
140    @mock.patch.object(aidegen_main, 'main_with_message')
141    def test_main_with_build_fail_errors(self, mock_main, mock_ends_metrics):
142        """Test main with raising build failure error conditions."""
143        mock_main.side_effect = errors.BuildFailureError
144        with self.assertRaises(errors.BuildFailureError):
145            aidegen_main.main([''])
146            _, exc_value, exc_traceback = sys.exc_info()
147            msg = str(exc_value)
148            mock_ends_metrics.assert_called_with(
149                constant.EXIT_CODE_AIDEGEN_EXCEPTION, exc_traceback, msg)
150
151    @mock.patch.object(aidegen_metrics, 'ends_asuite_metrics')
152    @mock.patch.object(aidegen_main, 'main_with_message')
153    def test_main_with_io_errors(self, mock_main, mock_ends_metrics):
154        """Test main with raising IO error conditions."""
155        mock_main.side_effect = IOError
156        with self.assertRaises(IOError):
157            aidegen_main.main([''])
158            _, exc_value, exc_traceback = sys.exc_info()
159            msg = str(exc_value)
160            mock_ends_metrics.assert_called_with(constant.EXIT_CODE_EXCEPTION,
161                                                 exc_traceback, msg)
162
163    @mock.patch.object(aidegen_main, '_launch_ide')
164    @mock.patch.object(ide_util, 'get_ide_util_instance')
165    def test_launch_native_projects_without_ide_object(
166            self, mock_get_ide, mock_launch_ide):
167        """Test _launch_native_projects function without ide object."""
168        target = 'libui'
169        args = aidegen_main._parse_args([target, '-i', 'e'])
170        aidegen_main._launch_native_projects(None, args, [])
171        self.assertFalse(mock_get_ide.called)
172        self.assertFalse(mock_launch_ide.called)
173
174    @mock.patch.object(aidegen_main, '_launch_ide')
175    @mock.patch.object(ide_util, 'get_ide_util_instance')
176    def test_launch_native_projects_with_ide_object(
177            self, mock_get_ide, mock_launch_ide):
178        """Test _launch_native_projects function without ide object."""
179        target = 'libui'
180        args = aidegen_main._parse_args([target, '-i', 'e'])
181        ide_util_obj = 'some_obj'
182        mock_get_ide.return_value = None
183        aidegen_main._launch_native_projects(ide_util_obj, args, [])
184        self.assertTrue(mock_get_ide.called_with('c'))
185        self.assertFalse(mock_launch_ide.called)
186        mock_get_ide.reset_mock()
187        mock_launch_ide.reset_mock()
188        args.ide = ['j']
189        aidegen_main._launch_native_projects(ide_util_obj, args, [])
190        self.assertTrue(mock_get_ide.called_with('c'))
191        self.assertFalse(mock_launch_ide.called)
192        mock_get_ide.reset_mock()
193        mock_launch_ide.reset_mock()
194        mock_get_ide.return_value = 'some_native_obj'
195        aidegen_main._launch_native_projects(ide_util_obj, args, [])
196        self.assertTrue(mock_get_ide.called_with('c'))
197        self.assertTrue(mock_launch_ide.called)
198        mock_get_ide.reset_mock()
199        mock_launch_ide.reset_mock()
200        args.ide = ['e']
201        aidegen_main._launch_native_projects(ide_util_obj, args, [])
202        self.assertTrue(mock_get_ide.called_with('c'))
203        self.assertTrue(mock_launch_ide.called)
204        mock_get_ide.reset_mock()
205        mock_launch_ide.reset_mock()
206        args.ide = ['s']
207        aidegen_main._launch_native_projects(ide_util_obj, args, [])
208        self.assertFalse(mock_get_ide.called)
209        self.assertTrue(mock_launch_ide.called)
210
211    @mock.patch('builtins.print')
212    def test_launch_ide(self, mock_print):
213        """Test _launch_ide function with config parameter."""
214        mock_ide_util = mock.MagicMock()
215        mock_ide_util.launch_ide.return_value = None
216        mock_ide_util.config_ide.return_value = None
217        launch_path = '/test/launch/ide/method'
218        aidegen_main._launch_ide(mock_ide_util, launch_path)
219        self.assertTrue(mock_ide_util.config_ide.called)
220        self.assertTrue(mock_ide_util.launch_ide.called)
221        mock_print.return_value = None
222
223    @mock.patch('builtins.input')
224    def test_get_preferred_ide_from_user(self, mock_input):
225        """Test get_preferred_ide_from_user with different conditions."""
226        test_data = []
227        aidegen_main._get_preferred_ide_from_user(test_data)
228        self.assertFalse(mock_input.called)
229        mock_input.reset_mock()
230
231        test_data = ['One', 'Two', 'Three']
232        mock_input.return_value = '3'
233        self.assertEqual('Three', aidegen_main._get_preferred_ide_from_user(
234            test_data))
235        self.assertEqual(1, mock_input.call_count)
236        mock_input.reset_mock()
237
238        mock_input.side_effect = ['7', '5', '3']
239        self.assertEqual('Three', aidegen_main._get_preferred_ide_from_user(
240            test_data))
241        self.assertEqual(3, mock_input.call_count)
242        mock_input.reset_mock()
243
244        mock_input.side_effect = ('.', '7', 't', '5', '1')
245        self.assertEqual('One', aidegen_main._get_preferred_ide_from_user(
246            test_data))
247        self.assertEqual(5, mock_input.call_count)
248        mock_input.reset_mock()
249
250    @mock.patch.object(project_config.ProjectConfig, 'init_environment')
251    @mock.patch('logging.warning')
252    @mock.patch.object(aidegen_main, '_launch_vscode')
253    @mock.patch.object(aidegen_main, '_launch_native_projects')
254    @mock.patch.object(native_util, 'generate_clion_projects')
255    @mock.patch.object(native_project_info.NativeProjectInfo,
256                       'generate_projects')
257    @mock.patch.object(aidegen_main, '_create_and_launch_java_projects')
258    @mock.patch.object(aidegen_main, '_get_preferred_ide_from_user')
259    def test_launch_ide_by_module_contents(self, mock_choice, mock_j,
260                                           mock_c_prj, mock_genc, mock_c,
261                                           mock_vs, mock_log, mock_init):
262        """Test _launch_ide_by_module_contents with different conditions."""
263        args = aidegen_main._parse_args(['', '-i', 's'])
264        mock_init.return_value = None
265        self._init_project_config(args)
266        ide_obj = 'ide_obj'
267        test_both = False
268        aidegen_main._launch_ide_by_module_contents(args, ide_obj, None,
269                                                    None, test_both)
270        self.assertFalse(mock_vs.called)
271        self.assertTrue(mock_log.called)
272        self.assertFalse(mock_choice.called)
273        self.assertFalse(mock_choice.called)
274        self.assertFalse(mock_j.called)
275        self.assertFalse(mock_c_prj.called)
276        self.assertFalse(mock_genc.called)
277        self.assertFalse(mock_c.called)
278
279        test_both = True
280        aidegen_main._launch_ide_by_module_contents(args, ide_obj, None,
281                                                    None, test_both)
282        self.assertTrue(mock_vs.called)
283        self.assertFalse(mock_j.called)
284        self.assertFalse(mock_genc.called)
285        self.assertFalse(mock_c.called)
286        mock_vs.reset_mock()
287
288        test_j = ['a', 'b', 'c']
289        test_c = ['1', '2', '3']
290        mock_choice.return_value = constant.JAVA
291        aidegen_main._launch_ide_by_module_contents(args, ide_obj, test_j,
292                                                    test_c)
293        self.assertFalse(mock_vs.called)
294        self.assertTrue(mock_j.called)
295        self.assertFalse(mock_genc.called)
296        self.assertFalse(mock_c.called)
297
298        mock_vs.reset_mock()
299        mock_choice.reset_mock()
300        mock_c.reset_mock()
301        mock_genc.reset_mock()
302        mock_j.reset_mock()
303        mock_choice.return_value = constant.C_CPP
304        aidegen_main._launch_ide_by_module_contents(args, ide_obj, test_j,
305                                                    test_c)
306        self.assertTrue(mock_c_prj.called)
307        self.assertFalse(mock_vs.called)
308        self.assertTrue(mock_genc.called)
309        self.assertTrue(mock_c.called)
310        self.assertFalse(mock_j.called)
311
312        mock_vs.reset_mock()
313        mock_choice.reset_mock()
314        mock_c.reset_mock()
315        mock_genc.reset_mock()
316        mock_j.reset_mock()
317        test_none = None
318        aidegen_main._launch_ide_by_module_contents(args, ide_obj, test_none,
319                                                    test_c)
320        self.assertFalse(mock_vs.called)
321        self.assertTrue(mock_genc.called)
322        self.assertTrue(mock_c.called)
323        self.assertFalse(mock_j.called)
324
325        mock_vs.reset_mock()
326        mock_choice.reset_mock()
327        mock_c.reset_mock()
328        mock_genc.reset_mock()
329        mock_j.reset_mock()
330        aidegen_main._launch_ide_by_module_contents(args, ide_obj, test_j,
331                                                    test_none)
332        self.assertFalse(mock_vs.called)
333        self.assertTrue(mock_j.called)
334        self.assertFalse(mock_c.called)
335        self.assertFalse(mock_genc.called)
336
337        args = aidegen_main._parse_args(['frameworks/base', '-i', 'c'])
338        mock_vs.reset_mock()
339        mock_choice.reset_mock()
340        mock_c.reset_mock()
341        mock_genc.reset_mock()
342        mock_c_prj.reset_mock()
343        mock_j.reset_mock()
344        aidegen_main._launch_ide_by_module_contents(args, ide_obj, test_j,
345                                                    test_c)
346        self.assertFalse(mock_vs.called)
347        self.assertFalse(mock_choice.called)
348        self.assertFalse(mock_j.called)
349        self.assertTrue(mock_c.called)
350        self.assertTrue(mock_c_prj.called)
351        self.assertTrue(mock_genc.called)
352
353        args = aidegen_main._parse_args(['frameworks/base'])
354        mock_vs.reset_mock()
355        mock_choice.reset_mock()
356        mock_c.reset_mock()
357        mock_genc.reset_mock()
358        mock_c_prj.reset_mock()
359        mock_j.reset_mock()
360        os.environ[constant.AIDEGEN_TEST_MODE] = 'true'
361        aidegen_main._launch_ide_by_module_contents(args, None, test_j, test_c)
362        self.assertFalse(mock_vs.called)
363        self.assertFalse(mock_choice.called)
364        self.assertTrue(mock_j.called)
365        self.assertFalse(mock_c.called)
366        self.assertFalse(mock_c_prj.called)
367        self.assertFalse(mock_genc.called)
368        del os.environ[constant.AIDEGEN_TEST_MODE]
369
370    @mock.patch.object(aidegen_main, '_launch_ide')
371    @mock.patch.object(aidegen_main, '_generate_project_files')
372    @mock.patch.object(project_info.ProjectInfo, 'multi_projects_locate_source')
373    @mock.patch.object(project_info.ProjectInfo, 'generate_projects')
374    def test_create_and_launch_java_projects(self, mock_prj, mock_compile,
375                                             mock_gen_file, mock_launch):
376        """Test _create_and_launch_java_projects."""
377        ide_obj = 'test_ide'
378        target = ['a', 'b']
379        mock_prj_list = mock.MagicMock()
380        mock_prj_list.project_absolute_path = 'test_path'
381        prj = [mock_prj_list]
382        mock_prj.return_value = prj
383        aidegen_main._create_and_launch_java_projects(ide_obj, target)
384        self.assertTrue(mock_prj.called_with(target))
385        self.assertTrue(mock_compile.called_with(prj))
386        self.assertTrue(mock_gen_file.called_with(prj))
387        self.assertTrue(mock_launch.called_with(ide_obj))
388        mock_launch.reset_mock()
389
390        aidegen_main._create_and_launch_java_projects(None, target)
391        self.assertFalse(mock_launch.called)
392
393    @mock.patch.object(aidegen_main, '_launch_ide')
394    @mock.patch.object(vscode_workspace_file_gen,
395                       'generate_code_workspace_file')
396    @mock.patch.object(common_util, 'get_related_paths')
397    def test_launch_vscode_without_ide_object(
398            self, mock_get_rel, mock_gen_code, mock_launch_ide):
399        """Test _launch_vscode function without ide object."""
400        mock_get_rel.return_value = 'rel', 'abs'
401        aidegen_main._launch_vscode(None, mock.Mock(), ['Settings'], [])
402        self.assertTrue(mock_get_rel.called)
403        self.assertTrue(mock_gen_code.called)
404        self.assertFalse(mock_launch_ide.called)
405
406    @mock.patch.object(aidegen_main, '_launch_ide')
407    @mock.patch.object(vscode_workspace_file_gen,
408                       'generate_code_workspace_file')
409    @mock.patch.object(common_util, 'get_related_paths')
410    def test_launch_vscode_with_ide_object(
411            self, mock_get_rel, mock_gen_code, mock_get_ide):
412        """Test _launch_vscode function with ide object."""
413        mock_get_rel.return_value = 'rel', 'abs'
414        aidegen_main._launch_vscode(mock.Mock(), mock.Mock(), ['Settings'], [])
415        self.assertTrue(mock_get_rel.called)
416        self.assertTrue(mock_gen_code.called)
417        self.assertTrue(mock_get_ide.called)
418
419    @mock.patch.object(aidegen_main, '_launch_vscode')
420    @mock.patch.object(aidegen_main, '_launch_ide')
421    @mock.patch.object(vscode_workspace_file_gen,
422                       'generate_code_workspace_file')
423    @mock.patch.object(common_util, 'get_related_paths')
424    def test_launch_vscode_with_both_languages(
425            self, mock_get_rel, mock_gen_code, mock_get_ide, mock_vscode):
426        """Test _launch_vscode function without ide object."""
427        mock_get_rel.return_value = 'rel', 'abs'
428        aidegen_main._launch_vscode(None, mock.Mock(), ['Settings'], [], True)
429        self.assertFalse(mock_get_rel.called)
430        self.assertFalse(mock_gen_code.called)
431        self.assertFalse(mock_get_ide.called)
432        self.assertTrue(mock_vscode.called)
433
434    @mock.patch.object(aidegen_main, '_launch_ide_by_module_contents')
435    @mock.patch.object(native_util, 'get_native_and_java_projects')
436    @mock.patch.object(native_module_info, 'NativeModuleInfo')
437    @mock.patch.object(module_info, 'AidegenModuleInfo')
438    @mock.patch.object(ide_util, 'get_ide_util_instance')
439    @mock.patch.object(project_config, 'ProjectConfig')
440    def test_aidegen_main(self, mock_config, mock_get_ide, mock_mod_info,
441                          mock_native, mock_get_project, mock_launch_ide):
442        """Test aidegen_main function with conditions."""
443        target = 'Settings'
444        args = aidegen_main._parse_args([target, '-i', 'v'])
445        config = mock.Mock()
446        config.targets = [target]
447        config.ide_name = constant.IDE_VSCODE
448        mock_config.return_value = config
449        ide = mock.Mock()
450        mock_get_ide.return_value = ide
451        mock_get_project.return_value = config.targets, []
452        aidegen_main.aidegen_main(args)
453        self.assertTrue(mock_config.called)
454        self.assertTrue(mock_get_ide.called)
455        self.assertTrue(mock_mod_info.called)
456        self.assertTrue(mock_native.called)
457        self.assertTrue(mock_get_project.called)
458        mock_launch_ide.assert_called_with(args, ide, config.targets, [], True)
459
460        mock_config.mock_reset()
461        mock_get_ide.mock_reset()
462        mock_mod_info.mock_reset()
463        mock_native.mock_reset()
464        mock_get_project.mock_reset()
465        mock_launch_ide.mock_reset()
466
467        args = aidegen_main._parse_args([target])
468        config.ide_name = constant.IDE_INTELLIJ
469        aidegen_main.aidegen_main(args)
470        self.assertTrue(mock_config.called)
471        self.assertTrue(mock_get_ide.called)
472        self.assertTrue(mock_mod_info.called)
473        self.assertTrue(mock_native.called)
474        self.assertTrue(mock_get_project.called)
475        mock_launch_ide.assert_called_with(args, ide, config.targets, [], False)
476
477
478if __name__ == '__main__':
479    unittest.main()
480